1
0
Fork 0
mirror of https://github.com/gwm17/Specter.git synced 2024-11-22 18:28:52 -05:00

Clean out old Navigator directory

This commit is contained in:
Gordon McCann 2022-06-19 16:23:43 -04:00 committed by GitHub
parent a5a2b4a786
commit aaf2cc325f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
94 changed files with 0 additions and 14067 deletions

View File

@ -1,38 +0,0 @@
/*
Navigator.h
This header file contains all of the essential includes for a project made using the Navigator library.
Should be included into your main project files.
GWM -- Feb 2022
*/
#ifndef NAVIGATOR_H
#define NAVIGATOR_H
#include <iostream>
#include <fstream>
#include <memory>
#include <utility>
#include <algorithm>
#include <functional>
#include <string>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <cstdint>
#include "Navigator/Core/Logger.h"
#include "Navigator/Core/Application.h"
#include "Navigator/Physics/PhysicsLayer.h"
#include "Navigator/Physics/AnalysisStage.h"
#include "Navigator/Core/Parameter.h"
#include "Navigator/Core/SpectrumManager.h"
#include "Navigator/Core/Layer.h"
#include "Navigator/Events/Event.h"
#include "Navigator/Utils/TestServerLayer.h"
#include "Navigator/Utils/Instrumentor.h"
#endif

View File

@ -1,105 +0,0 @@
/*
Application.cpp
This is the main application class, master and controller of program flow. Contains a layer stack, where each layer represets an
aspect of the application. Based on the Application class written by @TheCherno during his tutorial series on making a game engine.
Check out his work to learn more!
Note that Application is a singleton. Should only ever be one application ever created in the project.
GWM -- Feb 2022
*/
#include "Application.h"
#include "Navigator/Renderer/RenderCommand.h"
#include "Navigator/Editor/EditorLayer.h"
#include "Timestep.h"
namespace Navigator {
Application* Application::s_instance = nullptr;
Application::Application() :
m_runFlag(true)
{
NAV_PROFILE_FUNCTION();
s_instance = this;
m_window = std::unique_ptr<Window>(Window::Create());
m_window->SetEventCallback(BIND_EVENT_FUNCTION(Application::OnEvent)); //Allow window to pass events back
m_physicsLayer = new PhysicsLayer();
PushLayer(m_physicsLayer);
EditorLayer* editor = new EditorLayer(); //memory handled by layer stack
editor->SetEventCallbackFunc(BIND_EVENT_FUNCTION(Application::OnEvent)); //Allow editor to pass events back
PushLayer(editor);
m_imgui_layer = new ImGuiLayer();
PushOverlay(m_imgui_layer);
}
Application::~Application()
{
}
void Application::OnEvent(Event& event)
{
NAV_PROFILE_FUNCTION();
EventDispatcher dispatch(event);
dispatch.Dispatch<WindowCloseEvent>(BIND_EVENT_FUNCTION(Application::OnWindowCloseEvent));
for(auto iter = m_stack.end(); iter != m_stack.begin(); )
{
(*(--iter))->OnEvent(event);
if(event.handledFlag)
break;
}
}
bool Application::OnWindowCloseEvent(WindowCloseEvent& event)
{
m_runFlag = false;
NAV_WARN("Closing the window!");
return true;
}
void Application::PushLayer(Layer* layer)
{
NAV_PROFILE_FUNCTION();
m_stack.PushLayer(layer);
layer->OnAttach();
}
void Application::PushOverlay(Layer* layer)
{
NAV_PROFILE_FUNCTION();
m_stack.PushOverlay(layer);
layer->OnAttach();
}
void Application::Run()
{
NAV_PROFILE_FUNCTION();
float lastFrameTime = 0;
float time;
Timestep step;
while(m_runFlag)
{
RenderCommand::SetClearColor(m_bckgnd_color);
RenderCommand::Clear();
time = RenderCommand::GetFrameTime();
step.SetTime(time - lastFrameTime);
lastFrameTime = time;
for(auto layer : m_stack)
layer->OnUpdate(step);
m_imgui_layer->Begin();
for(auto layer : m_stack)
layer->OnImGuiRender();
m_imgui_layer->End();
m_window->OnUpdate();
}
}
}

View File

@ -1,68 +0,0 @@
/*
Application.h
This is the main application class, master and controller of program flow. Contains a layer stack, where each layer represets an
aspect of the application. Based on the Application class written by @TheCherno during his tutorial series on making a game engine.
Check out his work to learn more!
Note that Application is a singleton. Should only ever be one application ever created in the project.
GWM -- Feb 2022
*/
#ifndef APPLICATION_H
#define APPLICATION_H
#include "Navigator/Core/NavCore.h"
#include "Navigator/Events/Event.h"
#include "Navigator/Events/AppEvent.h"
#include "Navigator/Events/PhysicsEvent.h"
#include "Navigator/Core/LayerStack.h"
#include "Navigator/Core/Layer.h"
#include "Navigator/Core/Window.h"
#include "Navigator/ImGui/ImGuiLayer.h"
#include "Navigator/Physics/PhysicsLayer.h"
#include "glm/vec4.hpp"
namespace Navigator {
class NAV_API Application
{
public:
Application();
virtual ~Application();
void Run();
inline void Close() { m_runFlag = false; }
void OnEvent(Event& event);
void PushLayer(Layer* layer);
inline void PushAnalysisStage(AnalysisStage* stage) { m_physicsLayer->PushStage(stage); }
void PushOverlay(Layer* layer);
inline static Application& Get() { return *s_instance; }
inline Window& GetWindow() { return *m_window; }
private:
bool OnWindowCloseEvent(WindowCloseEvent& event);
LayerStack m_stack;
std::unique_ptr<Window> m_window;
ImGuiLayer* m_imgui_layer;
PhysicsLayer* m_physicsLayer;
bool m_runFlag;
//Dark grey background
glm::vec4 m_bckgnd_color = {0.1f, 0.1f, 0.1f, 1.0f};
static Application* s_instance;
};
/*
This function is left to be defined by the user. In principle we don't need to do this, as the Navigator library doesn't handle creation of the application,
but I like it and might be useful for changing to a system with a pre-defined entry point.
*/
Application* CreateApplication();
}
#endif

View File

@ -1,133 +0,0 @@
/*
Cut.cpp
Cut related classes. A cut here is defined as a filter upon a histogram based on some range for a parameter or set of parameters.
CutArgs is the underlying data that defines a cut (excluding the actual points).
Cut is the base class for all cut objects. Should not be used in practice. All cut objects have functions which can query what kind of cut it is. If one has the cut object,
Is1D() or Is2D() can be called. If one has the CutArgs, a 1D cut will have y_par set to "None" while a 2D cut will have a valid parameter name.
Cut1D is a one-dimensional (single parameter cut) while Cut2D is a two-dimensional (two parameter cut). There are a few differences between 1D and 2D cuts.
A Cut1D only contains two values, a min and a max. The parameter is checked that it falls within these bounds.
A Cut2D contains a set of (x,y) points that form a closed polygon. The polygon may be convex. The parameter is checked that it falls within the polygon using similar methods
to flood-fill algorithms.
GWM -- Feb 2022
*/
#include "Cut.h"
#include "implot.h"
namespace Navigator {
std::string ConvertCutTypeToString(CutType type)
{
NAV_PROFILE_FUNCTION();
switch(type)
{
case CutType::Cut1D: return "Cut1D";
case CutType::Cut2D: return "Cut2D";
case CutType::CutSummaryAll: return "CutSummaryAll";
case CutType::CutSummaryAny: return "CutSummaryAny";
case CutType::None: return "None";
}
return "None";
}
/*1D Cuts -- Can be made on and applied to either 1D or 2D histograms*/
Cut1D::Cut1D(const CutArgs& params, double min, double max) :
Cut(params), m_minVal(min), m_maxVal(max)
{
m_params.type = CutType::Cut1D;
}
Cut1D::~Cut1D() {}
void Cut1D::IsInside(double x, double y)
{
NAV_PROFILE_FUNCTION();
m_isValid = x >= m_minVal && x <= m_maxVal;
}
//Only within an ImPlot/ImGui context!!!
void Cut1D::Draw() const
{
NAV_PROFILE_FUNCTION();
double points[2] = { m_minVal, m_maxVal };
ImPlot::PlotVLines(m_params.name.c_str(), points, 2);
}
/*2D Cuts -- Can only be made on 2D histogram, but applied to either 1D or 2D histograms*/
Cut2D::Cut2D(const CutArgs& params, const std::vector<double>& xpoints, const std::vector<double>& ypoints) :
Cut(params), m_xpoints(xpoints), m_ypoints(ypoints)
{
m_params.type = CutType::Cut2D;
}
Cut2D::~Cut2D() {}
/*
Even-odd point in polygon algorithm (see Wikipedia)
Walk around the sides of the polygon and check intersection with each of the sides.
Cast a ray from the point to infinity in any direction and check the number of intersections.
If odd number of intersections, point is inside. Even, point is outside.
Edge cases of point is a vertex or on a side considered.
*/
void Cut2D::IsInside(double x, double y)
{
NAV_PROFILE_FUNCTION();
m_isValid = false;
double slope;
for(size_t i=0; i<(m_xpoints.size()-1); i++)
{
if (x == m_xpoints[i + 1] && y == m_ypoints[i + 1])
{
m_isValid = true;
return;
}
else if((m_ypoints[i+1] > y) != (m_ypoints[i] > y))
{
slope = (x - m_xpoints[i+1])*(m_ypoints[i] - m_ypoints[i+1]) - (m_xpoints[i] - m_xpoints[i+1])*(y - m_ypoints[i+1]);
if (slope == 0.0)
{
m_isValid = true;
return;
}
else if ((slope < 0.0) != (m_ypoints[i] < m_ypoints[i + 1]))
{
m_isValid = !m_isValid;
}
}
}
}
//Only in ImPlot/ImGui context!!!!
void Cut2D::Draw() const
{
NAV_PROFILE_FUNCTION();
ImPlot::PlotLine(m_params.name.c_str(), m_xpoints.data(), m_ypoints.data(), (int)m_xpoints.size());
}
/*CutSummaryAll -- Can only be made on a HistogramSummary but can be applied to any*/
CutSummary::CutSummary(const CutArgs& params, const std::vector<std::string>& subhistos, double min, double max) :
Cut(params), m_subhistos(subhistos), m_minVal(min), m_maxVal(max)
{
}
CutSummary::~CutSummary()
{
}
void CutSummary::IsInside(double x, double y)
{
NAV_PROFILE_FUNCTION();
m_isValid = x >= m_minVal && x <= m_maxVal;
}
//Only within an ImPlot/ImGui context!!!
void CutSummary::Draw() const
{
NAV_PROFILE_FUNCTION();
double points[2] = { m_minVal, m_maxVal };
ImPlot::PlotVLines(m_params.name.c_str(), points, 2);
}
}

View File

@ -1,126 +0,0 @@
/*
Cut.h
Cut related classes. A cut here is defined as a filter upon a histogram based on some range for a parameter or set of parameters.
CutArgs is the underlying data that defines a cut (excluding the actual points).
Cut is the base class for all cut objects. Should not be used in practice. All cut objects have functions which can query what kind of cut it is. If one has the cut object,
Is1D() or Is2D() can be called. If one has the CutArgs, a 1D cut will have y_par set to "None" while a 2D cut will have a valid parameter name.
Cut1D is a one-dimensional (single parameter cut) while Cut2D is a two-dimensional (two parameter cut). There are a few differences between 1D and 2D cuts.
A Cut1D only contains two values, a min and a max. The parameter is checked that it falls within these bounds.
A Cut2D contains a set of (x,y) points that form a closed polygon. The polygon may be convex. The parameter is checked that it falls within the polygon using similar methods
to flood-fill algorithms.
GWM -- Feb 2022
*/
#ifndef CUT_H
#define CUT_H
#include "NavCore.h"
#include "imgui.h"
namespace Navigator {
enum class CutType
{
Cut1D,
Cut2D,
CutSummaryAny,
CutSummaryAll,
None
};
std::string ConvertCutTypeToString(CutType type);
struct NAV_API CutArgs
{
CutArgs() {}
CutArgs(const std::string& n, const std::string& x, const std::string& y = "None") :
name(n), x_par(x), y_par(y)
{
}
CutType type = CutType::None;
std::string name = "None";
std::string x_par = "None";
std::string y_par = "None";
};
class NAV_API Cut
{
public:
Cut(const CutArgs& params) :
m_params(params), m_isValid(false)
{
}
virtual ~Cut() {}
virtual void IsInside(double x, double y=0.0) = 0;
virtual void Draw() const = 0;
virtual std::vector<double> GetXValues() const = 0;
virtual std::vector<double> GetYValues() const = 0;
inline const bool IsValid() const { return m_isValid; }
inline void ResetValidity() { m_isValid = false; }
inline CutType GetType() const { return m_params.type; }
inline const std::string& GetName() const { return m_params.name; }
inline const std::string& GetXParameter() const { return m_params.x_par; }
inline const std::string& GetYParameter() const { return m_params.y_par; }
inline const CutArgs& GetCutArgs() const { return m_params; }
protected:
CutArgs m_params;
bool m_isValid;
};
class NAV_API Cut1D : public Cut
{
public:
Cut1D(const CutArgs& params, double min, double max);
virtual ~Cut1D();
virtual void IsInside(double x, double y=0.0) override;
virtual void Draw() const override;
virtual std::vector<double> GetXValues() const override { return std::vector<double>({ m_minVal, m_maxVal }); }
virtual std::vector<double> GetYValues() const override { return std::vector<double>(); }
private:
double m_minVal, m_maxVal;
};
class NAV_API Cut2D : public Cut
{
public:
Cut2D(const CutArgs& params, const std::vector<double>& xpoints, const std::vector<double>& ypoints);
virtual ~Cut2D();
virtual void IsInside(double x, double y) override;
virtual void Draw() const override;
virtual std::vector<double> GetXValues() const override { return m_xpoints; }
virtual std::vector<double> GetYValues() const override { return m_ypoints; }
private:
std::vector<double> m_xpoints;
std::vector<double> m_ypoints;
};
class NAV_API CutSummary : public Cut
{
public:
CutSummary(const CutArgs& params, const std::vector<std::string>& subhistos, double min, double max);
virtual ~CutSummary();
virtual void IsInside(double x, double y) override;
virtual void Draw() const override;
virtual std::vector<double> GetXValues() const override { return std::vector<double>({ m_minVal, m_maxVal }); }
virtual std::vector<double> GetYValues() const override { return std::vector<double>(); }
inline const std::vector<std::string>& GetSubHistograms() const { return m_subhistos; }
private:
double m_minVal, m_maxVal;
std::vector<std::string> m_subhistos;
};
}
#endif

View File

@ -1,42 +0,0 @@
#include "Graph.h"
#include "implot.h"
namespace Navigator {
ScalerGraph::ScalerGraph(const GraphArgs& args) :
m_args(args), m_lastScalerVal(0)
{
m_xPoints.reserve(m_args.maxPoints);
m_xPoints.reserve(m_args.maxPoints);
}
ScalerGraph::~ScalerGraph() {}
void ScalerGraph::UpdatePoints(double timeStep, uint64_t scalerVal)
{
double rate = (scalerVal - m_lastScalerVal) / timeStep;
if (m_xPoints.size() < m_args.maxPoints)
{
m_xPoints.push_back(timeStep);
m_yPoints.push_back(rate);
}
else
{
for (size_t i = 0; i < (m_args.maxPoints - 1); i++)
{
m_xPoints[i] = m_xPoints[i + 1];
m_yPoints[i] = m_yPoints[i + 1];
}
m_xPoints[m_args.maxPoints - 1] = timeStep;
m_yPoints[m_args.maxPoints - 1] = rate;
}
m_lastScalerVal = scalerVal;
}
void ScalerGraph::Draw()
{
ImPlot::SetupAxes("time (s)", "rate (Hz)");
ImPlot::PlotLine(m_args.name.c_str(), m_xPoints.data(), m_yPoints.data(), m_xPoints.size());
}
}

View File

@ -1,41 +0,0 @@
#ifndef GRAPH_H
#define GRAPH_H
#include <queue>
namespace Navigator {
struct GraphArgs
{
std::string name;
std::string scalerName;
size_t maxPoints;
};
class ScalerGraph
{
public:
ScalerGraph(const GraphArgs& args);
~ScalerGraph();
void UpdatePoints(double timeStep, uint64_t scalerVal);
void Draw();
inline void Clear() { m_xPoints.clear(); m_yPoints.clear(); }
inline const std::string& GetName() const { return m_args.name; }
inline const std::string& GetScaler() const { return m_args.scalerName; }
inline const GraphArgs& GetArgs() const { return m_args; }
private:
GraphArgs m_args;
uint64_t m_lastScalerVal;
std::vector<double> m_yPoints;
std::vector<double> m_xPoints;
};
}
#endif

View File

@ -1,421 +0,0 @@
/*
Histogram.cpp
Histogram related classes. We use a custom histogram class here because the ImPlot histograms are not a data structure, but rather just a function. This means that at every draw call for an ImPlot
histogram the entire data set will need to be properly binned and memory will need to be allocated to make histogram arrays. For our use case this is obviously bad. For one thing, data runs can have
thousands to millions of events. In the ImPlot paradigm we would need to loop over all of this data and bin it, not to mention explicitly store all of this data in memory for every histogram. I point this
out not to say that ImPlot histograms are bad intrinsically, because they definitely have a use for smaller data sets, but rather to explain why for this program I have re-invented the wheel somewhat.
HistogramArgs are the underlying data which define a histogram. This is grouped in a struct to easily pass these around for use in contexts like the Editor.
Every histogram has a set of histogram parameters.
Histogram is the base class of all histograms. Should not be used in practice. Every histogram contains functions to query what type of underlying histogram it is. If one has
the Histogram object, Is1D() or Is2D() can be called. If one only has the HistogramArgs, the values of x_par and y_par can be inspected. In particular, a 1D histogram will have
y_par set to "None", while a 2D histogram should have a valid parameter name for y_par.
Histogram1D is a one dimensional (single parameter) histogram. Histogram2D is a two dimensional (two parameter) histogram. The only real difference between these in practice, other than
the obvious two vs. one parameter thing, is that a Histogram2D contains methods to set the z-axis range (color scale) which ImPlot does not provide intrinsic access to from the plot itself.
When the range is set to (0,0), the color scale is set to the default (0, maxValue). Otherwise the color is scaled as appropriate. If you query a Histogram1D for its color scale you will recieve
a nullptr.
StatResults is a struct containing statistical information about a region of a histogram.
GWM -- Feb 2022
*/
#include "Histogram.h"
#include "implot.h"
/*
For Navigator histograms, binnning is done over ranges as
[min, max), so that each bin is exclusive.
*/
namespace Navigator {
std::string ConvertSpectrumTypeToString(SpectrumType type)
{
NAV_PROFILE_FUNCTION();
switch (type)
{
case SpectrumType::Histo1D: return "Histogram1D";
case SpectrumType::Histo2D: return "Histogram2D";
case SpectrumType::Summary: return "Summary";
case SpectrumType::None: return "None";
}
return "None";
}
/*
1D Histogram class
*/
Histogram1D::Histogram1D(const HistogramArgs& params) :
Histogram(params)
{
InitBins();
}
Histogram1D::~Histogram1D() {}
void Histogram1D::InitBins()
{
NAV_PROFILE_FUNCTION();
m_params.type = SpectrumType::Histo1D;
if(m_params.nbins_x == 0 || (m_params.min_x >= m_params.max_x))
{
NAV_WARN("Attempting to create an illegal Histogram1D {0} with {1} bins and a range from {2} to {3}. Historgram not initialized.", m_params.name, m_params.nbins_x, m_params.min_x, m_params.max_x);
m_initFlag = false;
return;
}
m_binWidth = (m_params.max_x - m_params.min_x)/m_params.nbins_x;
m_binCenters.resize(m_params.nbins_x);
m_binCounts.resize(m_params.nbins_x);
for(int i=0; i<m_params.nbins_x; i++)
{
m_binCenters[i] = m_params.min_x + i*m_binWidth + m_binWidth*0.5;
m_binCounts[i] = 0;
}
m_initFlag = true;
}
//Note: only x is used here, y is simply present to maintain compliance with 2D case and can be ignored
void Histogram1D::FillData(double x, double y)
{
NAV_PROFILE_FUNCTION();
if (x < m_params.min_x || x >= m_params.max_x)
return;
int bin = int((x - m_params.min_x)/(m_binWidth));
m_binCounts[bin] += 1.0;
}
//Can only be used within an ImGui / ImPlot context!!
void Histogram1D::Draw()
{
NAV_PROFILE_FUNCTION();
ImPlot::SetupAxes(m_params.x_par.c_str(), "Counts",0, ImPlotAxisFlags_LockMin | ImPlotAxisFlags_AutoFit);
ImPlot::PlotBars(m_params.name.c_str(), &m_binCenters.data()[0], &m_binCounts.data()[0], m_params.nbins_x, m_binWidth);
}
void Histogram1D::ClearData()
{
for(int i=0; i<m_params.nbins_x; i++)
m_binCounts[i] = 0;
}
//Again here yvalues can be ignored, only for compliance
StatResults Histogram1D::AnalyzeRegion(double x_min, double x_max, double y_min, double y_max)
{
NAV_PROFILE_FUNCTION();
int bin_min, bin_max;
StatResults results;
//We clamp to the boundaries of the histogram
if (x_min <= m_params.min_x)
bin_min = 0;
else
bin_min = int((x_min - m_params.min_x) / (m_binWidth));
if (x_max >= m_params.max_x)
bin_max = m_params.nbins_x - 1;
else
bin_max = int((x_max - m_params.min_x) / (m_binWidth));
for (int i = bin_min; i <= bin_max; i++)
{
results.integral += m_binCounts[i];
results.cent_x += m_binCounts[i] * (m_params.min_x + m_binWidth * i);
}
if (results.integral == 0)
return results;
results.cent_x /= results.integral;
for (int i = bin_min; i <= bin_max; i++)
results.sigma_x += m_binCounts[i] * ((m_params.min_x + m_binWidth * i) - results.cent_x) * ((m_params.min_x + m_binWidth * i) - results.cent_x);
results.sigma_x = std::sqrt(results.sigma_x / (results.integral - 1));
return results;
}
/*
2D Histogram class
Note for 2D: Rendering is done from top left to bottom right. So ybins run from top to bottom (ymin is last row, ymax is first row)
*/
Histogram2D::Histogram2D(const HistogramArgs& params) :
Histogram(params)
{
m_colorScaleRange[0] = 0.0f;
m_colorScaleRange[1] = 0.0f;
InitBins();
}
Histogram2D::~Histogram2D() {}
void Histogram2D::InitBins()
{
NAV_PROFILE_FUNCTION();
m_params.type = SpectrumType::Histo2D;
if(m_params.nbins_x <= 0 || m_params.nbins_y <= 0 || m_params.min_x >= m_params.max_x || m_params.min_y >= m_params.max_y)
{
NAV_WARN("Attempting to create illegal Histogram2D {0} with {1} x bins, {2} y bins, an x range of {3} to {4}, and a y range of {5} to {6}. Not initialized.", m_params.name, m_params.nbins_x, m_params.nbins_y,
m_params.min_x, m_params.max_x, m_params.min_y, m_params.max_y);
m_initFlag = false;
return;
}
m_binWidthX = (m_params.max_x - m_params.min_x)/m_params.nbins_x;
m_binWidthY = (m_params.max_y - m_params.min_y)/m_params.nbins_y;
m_nBinsTotal = m_params.nbins_x*m_params.nbins_y;
m_binCounts.resize(m_nBinsTotal);
for(int i=0; i<m_nBinsTotal; i++)
m_binCounts[i] = 0;
m_maxBinContent = 0;
m_initFlag = true;
}
void Histogram2D::FillData(double x, double y)
{
NAV_PROFILE_FUNCTION();
if (x < m_params.min_x || x >= m_params.max_x || y <= m_params.min_y || y > m_params.max_y)
return;
int bin_x = int((x - m_params.min_x)/m_binWidthX);
int bin_y = int((m_params.max_y - y)/m_binWidthY);
int bin = bin_y*m_params.nbins_x + bin_x;
m_binCounts[bin] += 1.0;
m_maxBinContent = m_binCounts[bin] > m_maxBinContent ? (m_binCounts[bin]) : m_maxBinContent;
}
//Can only be used within an ImGui / ImPlot context!!
/*
Brief note on colormaps: There are several kinds of colormaps, each with specific use cases. But broadly, the two main categories are discrete and continuous.
Discrete maps are good for categorical data, and we use these as the standard for plots we draw. This is how you make cuts standout from your histograms.
But for the colors of the bins, we want a continuous scale, i.e. smooth variation on a single hue, as the color differences represent the value differences
relative to other bins. Hence the pushing of the Matplotlib virdis colormap. For more on colormaps, I suggest looking at matplotlib documentation.
*/
void Histogram2D::Draw()
{
NAV_PROFILE_FUNCTION();
ImPlot::SetupAxes(m_params.x_par.c_str(), m_params.y_par.c_str());
ImPlot::PushColormap(ImPlotColormap_Viridis);
ImPlot::PlotHeatmap(m_params.name.c_str(), &m_binCounts.data()[0], m_params.nbins_y, m_params.nbins_x, m_colorScaleRange[0], m_colorScaleRange[1], NULL,
ImPlotPoint(m_params.min_x, m_params.min_y), ImPlotPoint(m_params.max_x, m_params.max_y));
ImPlot::PopColormap();
}
void Histogram2D::ClearData()
{
for(int i=0; i<m_nBinsTotal; i++)
m_binCounts[i] = 0;
m_maxBinContent = 0;
}
StatResults Histogram2D::AnalyzeRegion(double x_min, double x_max, double y_min, double y_max)
{
NAV_PROFILE_FUNCTION();
int xbin_min, xbin_max, ybin_min, ybin_max;
int curbin;
StatResults results;
//We clamp to the boundaries of the histogram
if (x_min <= m_params.min_x)
xbin_min = 0;
else
xbin_min = int((x_min - m_params.min_x) / (m_binWidthX));
if (x_max >= m_params.max_x)
xbin_max = m_params.nbins_x - 1;
else
xbin_max = int((x_max - m_params.min_x) / (m_binWidthX));
if (y_min <= m_params.min_y)
ybin_max = m_params.nbins_y - 1;
else
ybin_max = int((m_params.max_y - y_min) / m_binWidthY);
if (y_max >= m_params.max_y)
ybin_min = 0;
else
ybin_min = int((m_params.max_y - y_max) / m_binWidthY);
for (int y = ybin_min; y <= ybin_max; y++)
{
for (int x = xbin_min; x <= xbin_max; x++)
{
curbin = y * m_params.nbins_x + x;
results.integral += m_binCounts[curbin];
results.cent_x += m_binCounts[curbin] * (m_params.min_x + m_binWidthX * x);
results.cent_y += m_binCounts[curbin] * (m_params.max_y - m_binWidthY * y);
}
}
if (results.integral == 0)
return results;
results.cent_x /= results.integral;
results.cent_y /= results.integral;
for (int y = ybin_min; y <= ybin_max; y++)
{
for (int x = xbin_min; x <= xbin_max; x++)
{
curbin = y * m_params.nbins_x + x;
results.sigma_x += m_binCounts[curbin] * ((m_params.min_x + m_binWidthX * x) - results.cent_x) * ((m_params.min_x + m_binWidthX * x) - results.cent_x);
results.sigma_y += m_binCounts[curbin] * ((m_params.max_y - m_binWidthY * y) - results.cent_y) * ((m_params.max_y - m_binWidthY * y) - results.cent_y);
}
}
results.sigma_x = std::sqrt(results.sigma_x / (results.integral - 1));
results.sigma_y = std::sqrt(results.sigma_y / (results.integral - 1));
return results;
}
/*
HistogramSummary class methods
-- 03/18/22 Adding in the basics, unsure of how to exactly approach design
-- Fill data to independent histogram? Steal data from other histograms?
-- Cuts?
-- Literally everything hahaha
*/
HistogramSummary::HistogramSummary(const HistogramArgs& params, const std::vector<std::string>& subhistos) :
Histogram(params), m_subhistos(subhistos), m_labels(nullptr)
{
m_colorScaleRange[0] = 0.0f;
m_colorScaleRange[1] = 0.0f;
InitBins();
}
HistogramSummary::~HistogramSummary()
{
if (m_labels)
delete[] m_labels;
}
void HistogramSummary::InitBins()
{
NAV_PROFILE_FUNCTION();
m_params.type = SpectrumType::Summary;
if (m_params.nbins_x <= 0 || m_params.min_x >= m_params.max_x)
{
NAV_WARN("Attempting to create illegal HistogramSummary {0} with {1} x bins and an x range of {2} to {3}. Not initialized.", m_params.name, m_params.nbins_x, m_params.min_x, m_params.max_x);
m_initFlag = false;
return;
}
m_labels = new const char* [m_subhistos.size() + 1];
for (size_t i = 0; i < m_subhistos.size(); i++)
m_labels[i] = m_subhistos[i].c_str();
m_labels[m_subhistos.size()] = "";
m_params.nbins_y = m_subhistos.size();
m_params.min_y = 0.0;
m_params.max_y = m_subhistos.size();
m_binWidthX = (m_params.max_x - m_params.min_x) / m_params.nbins_x;
m_nBinsTotal = m_params.nbins_x * m_params.nbins_y;
m_binCounts.resize(m_nBinsTotal);
for (int i = 0; i < m_nBinsTotal; i++)
m_binCounts[i] = 0;
m_initFlag = true;
}
void HistogramSummary::FillData(double x, double y)
{
NAV_PROFILE_FUNCTION();
if (x < m_params.min_x || x >= m_params.max_x || y <= m_params.min_y || y > m_params.max_y)
return;
int bin_x = int((x - m_params.min_x) / m_binWidthX);
int bin_y = int((m_params.max_y - y) / m_binWidthY);
int bin = bin_y * m_params.nbins_x + bin_x;
m_binCounts[bin] += 1.0;
}
void HistogramSummary::Draw()
{
NAV_PROFILE_FUNCTION();
ImPlot::SetupAxisTicks(ImAxis_Y1, m_params.min_y, m_params.max_y, m_params.nbins_y, m_labels, false);
ImPlot::PushColormap(ImPlotColormap_Viridis);
ImPlot::PlotHeatmap(m_params.name.c_str(), &m_binCounts.data()[0], m_params.nbins_y, m_params.nbins_x, m_colorScaleRange[0], m_colorScaleRange[1], NULL,
ImPlotPoint(m_params.min_x, m_params.min_y), ImPlotPoint(m_params.max_x, m_params.max_y));
ImPlot::PopColormap();
}
void HistogramSummary::ClearData()
{
for (int i = 0; i < m_nBinsTotal; i++)
m_binCounts[i] = 0;
}
StatResults HistogramSummary::AnalyzeRegion(double x_min, double x_max, double y_min, double y_max)
{
NAV_PROFILE_FUNCTION();
int xbin_min, xbin_max, ybin_min, ybin_max;
int curbin;
StatResults results;
//We clamp to the boundaries of the histogram
if (x_min <= m_params.min_x)
xbin_min = 0;
else
xbin_min = int((x_min - m_params.min_x) / (m_binWidthX));
if (x_max >= m_params.max_x)
xbin_max = m_params.nbins_x - 1;
else
xbin_max = int((x_max - m_params.min_x) / (m_binWidthX));
if (y_min <= m_params.min_y)
ybin_max = m_params.nbins_y - 1;
else
ybin_max = int((m_params.max_y - y_min) / m_binWidthY);
if (y_max >= m_params.max_y)
ybin_min = 0;
else
ybin_min = int((m_params.max_y - y_max) / m_binWidthY);
for (int y = ybin_min; y <= ybin_max; y++)
{
for (int x = xbin_min; x <= xbin_max; x++)
{
curbin = y * m_params.nbins_x + x;
results.integral += m_binCounts[curbin];
results.cent_x += m_binCounts[curbin] * (m_params.min_x + m_binWidthX * x);
results.cent_y += m_binCounts[curbin] * (m_params.max_y - m_binWidthY * y);
}
}
if (results.integral == 0)
return results;
results.cent_x /= results.integral;
results.cent_y /= results.integral;
for (int y = ybin_min; y <= ybin_max; y++)
{
for (int x = xbin_min; x <= xbin_max; x++)
{
curbin = y * m_params.nbins_x + x;
results.sigma_x += m_binCounts[curbin] * ((m_params.min_x + m_binWidthX * x) - results.cent_x) * ((m_params.min_x + m_binWidthX * x) - results.cent_x);
results.sigma_y += m_binCounts[curbin] * ((m_params.max_y - m_binWidthY * y) - results.cent_y) * ((m_params.max_y - m_binWidthY * y) - results.cent_y);
}
}
results.sigma_x = std::sqrt(results.sigma_x / (results.integral - 1));
results.sigma_y = std::sqrt(results.sigma_y / (results.integral - 1));
return results;
}
}

View File

@ -1,184 +0,0 @@
/*
Histogram.h
Histogram related classes. We use a custom histogram class here because the ImPlot histograms are not a data structure, but rather just a function. This means that at every draw call for an ImPlot
histogram the entire data set will need to be properly binned and memory will need to be allocated to make histogram arrays. For our use case this is obviously bad. For one thing, data runs can have
thousands to millions of events. In the ImPlot paradigm we would need to loop over all of this data and bin it, not to mention explicitly store all of this data in memory for every histogram. I point this
out not to say that ImPlot histograms are bad intrinsically, because they definitely have a use for smaller data sets, but rather to explain why for this program I have re-invented the wheel somewhat.
HistogramArgs are the underlying data which define a histogram. This is grouped in a struct to easily pass these around for use in contexts like the Editor.
Every histogram has a set of histogram parameters.
Histogram is the base class of all histograms. Should not be used in practice. Every histogram contains functions to query what type of underlying histogram it is. If one has
the Histogram object, Is1D() or Is2D() can be called. If one only has the HistogramArgs, the values of x_par and y_par can be inspected. In particular, a 1D histogram will have
y_par set to "None", while a 2D histogram should have a valid parameter name for y_par.
Histogram1D is a one dimensional (single parameter) histogram. Histogram2D is a two dimensional (two parameter) histogram. The only real difference between these in practice, other than
the obvious two vs. one parameter thing, is that a Histogram2D contains methods to set the z-axis range (color scale) which ImPlot does not provide intrinsic access to from the plot itself.
When the range is set to (0,0), the color scale is set to the default (0, maxValue). Otherwise the color is scaled as appropriate. If you query a Histogram1D for its color scale you will recieve
a nullptr.
StatResults is a struct containing statistical information about a region of a histogram.
GWM -- Feb 2022
*/
#ifndef HISTOGRAM_H
#define HISTOGRAM_H
#include "NavCore.h"
namespace Navigator {
enum class SpectrumType
{
Histo1D,
Histo2D,
Summary,
None
};
std::string ConvertSpectrumTypeToString(SpectrumType type);
struct NAV_API StatResults
{
double integral = 0.0;
double cent_x = 0.0;
double cent_y = 0.0;
double sigma_x = 0.0;
double sigma_y = 0.0;
};
struct NAV_API HistogramArgs
{
HistogramArgs() {}
HistogramArgs(const std::string& n, const std::string& x, int bins, double min, double max) :
name(n), x_par(x), nbins_x(bins), min_x(min), max_x(max)
{
}
HistogramArgs(const std::string& n, const std::string& x, const std::string& y, int binsx, double minx, double maxx, int binsy, double miny, double maxy) :
name(n), x_par(x), y_par(y), nbins_x(binsx), min_x(minx), max_x(maxx), nbins_y(binsy), min_y(miny), max_y(maxy)
{
}
SpectrumType type = SpectrumType::None;
std::string name = "None";
std::string x_par = "None";
std::string y_par = "None";
std::vector<std::string> cutsDrawnUpon;
std::vector<std::string> cutsAppliedTo;
int nbins_x = 0;
double min_x = 0;
double max_x = 0;
int nbins_y = 0;
double min_y = 0;
double max_y = 0;
};
class NAV_API Histogram
{
public:
Histogram() :
m_initFlag(false)
{
}
Histogram(const HistogramArgs& params) :
m_params(params), m_initFlag(false)
{
}
virtual ~Histogram() {};
virtual void FillData(double x, double y=0.0) { NAV_WARN("Trying to fill a default histogram!"); }
virtual void Draw() {}
virtual void ClearData() {}
virtual StatResults AnalyzeRegion(double x_min, double x_max, double y_min = 0.0, double y_max = 0.0) { return StatResults(); }
inline virtual float* GetColorScaleRange() { return nullptr; }
inline virtual std::vector<double> GetBinData() { return std::vector<double>(); }
inline HistogramArgs& GetParameters() { return m_params; }
inline SpectrumType GetType() { return m_params.type; }
inline const std::string& GetXParam() const { return m_params.x_par; };
inline const std::string& GetYParam() const { return m_params.y_par; };
inline const std::string& GetName() const { return m_params.name; }
inline void AddCutToBeDrawn(const std::string& name) { m_params.cutsDrawnUpon.push_back(name); }
inline void AddCutToBeApplied(const std::string& name) { m_params.cutsAppliedTo.push_back(name); }
protected:
HistogramArgs m_params;
bool m_initFlag;
};
class NAV_API Histogram1D : public Histogram
{
public:
Histogram1D(const HistogramArgs& params);
virtual ~Histogram1D();
virtual void FillData(double x, double y=0.0) override;
virtual void Draw() override;
virtual void ClearData() override;
virtual StatResults AnalyzeRegion(double x_min, double x_max, double y_min = 0.0, double y_max = 0.0) override;
inline virtual std::vector<double> GetBinData() override { return m_binCounts; }
private:
void InitBins();
std::vector<double> m_binCenters;
std::vector<double> m_binCounts;
double m_binWidth;
};
class NAV_API Histogram2D : public Histogram
{
public:
Histogram2D(const HistogramArgs& params);
virtual ~Histogram2D();
virtual void FillData(double x, double y) override;
virtual void Draw() override;
virtual void ClearData() override;
virtual StatResults AnalyzeRegion(double x_min, double x_max, double y_min = 0.0, double y_max = 0.0) override;
inline virtual std::vector<double> GetBinData() override { return m_binCounts; }
inline virtual float* GetColorScaleRange() override { return m_colorScaleRange; }
private:
void InitBins();
std::vector<double> m_binCounts;
int m_nBinsTotal;
double m_binWidthY;
double m_binWidthX;
double m_maxBinContent;
float m_colorScaleRange[2];
};
class NAV_API HistogramSummary : public Histogram
{
public:
HistogramSummary(const HistogramArgs& params, const std::vector<std::string>& subhistos);
~HistogramSummary();
inline const std::vector<std::string>& GetSubHistograms() const { return m_subhistos; }
virtual void FillData(double x, double y) override;
virtual void ClearData() override;
virtual void Draw() override;
inline virtual float* GetColorScaleRange() override { return m_colorScaleRange; }
virtual StatResults AnalyzeRegion(double x_min, double x_max, double y_min = 0.0, double y_max = 0.0) override;
inline virtual std::vector<double> GetBinData() override { return m_binCounts; }
private:
void InitBins();
std::vector<std::string> m_subhistos;
const char** m_labels;
std::vector<double> m_binCounts;
int m_nBinsTotal;
double m_binWidthX;
const double m_binWidthY = 1.0;
float m_colorScaleRange[2];
};
}
#endif

View File

@ -1,17 +0,0 @@
/*
Layer.cpp
Layer is an abstract class representing an aspect of the application. Based entirely upon @TheCherno's tutorials in his game engine series.
GWM -- Feb 2022
*/
#include "Layer.h"
namespace Navigator {
Layer::Layer(const std::string& name) :
m_name(name)
{
}
Layer::~Layer() {}
}

View File

@ -1,36 +0,0 @@
/*
Layer.h
Layer is an abstract class representing an aspect of the application. Based entirely upon @TheCherno's tutorials in his game engine series.
GWM -- Feb 2022
*/
#ifndef LAYER_H
#define LAYER_H
#include "NavCore.h"
#include "Navigator/Events/Event.h"
#include "Timestep.h"
namespace Navigator {
class NAV_API Layer
{
public:
Layer(const std::string& name="Layer");
virtual ~Layer();
virtual void OnAttach() {}
virtual void OnDetach() {}
virtual void OnImGuiRender() {}
virtual void OnUpdate(Timestep& step) {}
virtual void OnEvent(Event& event) {}
inline const std::string& GetName() { return m_name; }
private:
std::string m_name;
};
}
#endif

View File

@ -1,49 +0,0 @@
/*
LayerStack.cpp
LayerStack is a container for Layers. Should only be owned by the Application. Layers are
managed by the LayerStack (memory-wise). There are two types of layers, overlays and regular layers.
Overlays are processed first in the event stack. This is entirely based upon @TheCherno's work shown in his
game engine tutorial series.
GWM -- Feb 2022
*/
#include "LayerStack.h"
namespace Navigator {
LayerStack::LayerStack() {}
LayerStack::~LayerStack()
{
for(Layer* layer : m_stack)
delete layer;
}
void LayerStack::PushLayer(Layer* layer)
{
m_stack.emplace(m_stack.begin() + m_insertIndex, layer);
m_insertIndex++;
}
void LayerStack::PushOverlay(Layer* layer)
{
m_stack.emplace_back(layer);
}
void LayerStack::PopLayer(Layer* layer)
{
auto iter = std::find(m_stack.begin(), m_stack.end(), layer);
if(iter != m_stack.end())
{
m_stack.erase(iter);
m_insertIndex--;
}
}
void LayerStack::PopOverlay(Layer* layer)
{
auto iter = std::find(m_stack.begin(), m_stack.end(), layer);
if(iter != m_stack.end())
m_stack.erase(iter);
}
}

View File

@ -1,39 +0,0 @@
/*
LayerStack.h
LayerStack is a container for Layers. Should only be owned by the Application. Layers are
managed by the LayerStack (memory-wise). There are two types of layers, overlays and regular layers.
Overlays are processed first in the event stack. This is entirely based upon @TheCherno's work shown in his
game engine tutorial series.
GWM -- Feb 2022
*/
#ifndef LAYER_STACK_H
#define LAYER_STACK_H
#include "NavCore.h"
#include "Layer.h"
namespace Navigator {
class NAV_API LayerStack
{
public:
LayerStack();
~LayerStack();
void PushLayer(Layer* layer);
void PopLayer(Layer* layer);
void PushOverlay(Layer* layer);
void PopOverlay(Layer* layer);
//helpers for iterator for loops
std::vector<Layer*>::iterator begin() { return m_stack.begin(); }
std::vector<Layer*>::iterator end() { return m_stack.end(); }
private:
std::vector<Layer*> m_stack; //These layers are owned by the LayerStack!
unsigned int m_insertIndex=0; //used to keep track of where to put layers vs. overlays.
};
}
#endif

View File

@ -1,26 +0,0 @@
/*
Logger.cpp
Logging class which is a thin wrapper on the spdlog library. Must be initialized in the project at start (see NavProject main.cpp for example).
Again, strongly based upon @TheCherno's work, see his Hazel repository for more details.
Note Logger is a singleton. Should only ever be intialized once. Macros for calling the log provided to make clean looking code at the other side.
Consider making some logging calls only defined on Debug.
GWM -- Feb 2022
*/
#include "Logger.h"
#include "spdlog/sinks/stdout_color_sinks.h"
namespace Navigator {
std::shared_ptr<spdlog::logger> Logger::s_logger;
void Logger::Init()
{
spdlog::set_pattern("%^[%T] %n: %v%$");
s_logger = spdlog::stdout_color_mt("NAV");
s_logger->set_level(spdlog::level::trace);
}
}

View File

@ -1,42 +0,0 @@
/*
Logger.h
Logging class which is a thin wrapper on the spdlog library. Must be initialized in the project at start (see NavProject main.cpp for example).
Again, strongly based upon @TheCherno's work, see his Hazel repository for more details.
Note Logger is a singleton. Should only ever be intialized once. Macros for calling the log provided to make clean looking code at the other side.
Consider making some logging calls only defined on Debug.
GWM -- Feb 2022
*/
#ifndef LOGGER_H
#define LOGGER_H
#include "NavCore.h"
#include "spdlog/spdlog.h"
#include "spdlog/fmt/ostr.h"
namespace Navigator {
class NAV_API Logger
{
public:
static void Init();
inline static std::shared_ptr<spdlog::logger> GetLogger() { return s_logger; }
private:
static std::shared_ptr<spdlog::logger> s_logger;
};
}
//Macros for clean code. Different logging levels.
#define NAV_CRITICAL(...) ::Navigator::Logger::GetLogger()->critical(__VA_ARGS__)
#define NAV_WARN(...) ::Navigator::Logger::GetLogger()->warn(__VA_ARGS__)
#define NAV_ERROR(...) ::Navigator::Logger::GetLogger()->error(__VA_ARGS__)
#define NAV_TRACE(...) ::Navigator::Logger::GetLogger()->trace(__VA_ARGS__)
#define NAV_INFO(...) ::Navigator::Logger::GetLogger()->info(__VA_ARGS__)
#endif

View File

@ -1,34 +0,0 @@
#ifndef NAVCORE_H
#define NAVCORE_H
/*
Have to handle Windows dll import/export behavior. Otherwise, NAV_API
is just an empty expression.
*/
#ifdef NAV_WINDOWS
#define NAV_API
#ifdef _MSC_VER
#pragma warning (disable: 4127) // condition expression is constant
#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when NAV_API is set to__declspec(dllexport)
#pragma warning (disable: 4091) // '__declspec(dllimport)': ignored on left of class 'xxx' when no variable is declared
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later
#pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types
#endif
#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2).
#pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6).
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
#endif
#else
#define NAV_API
#endif
//Bit field setter
#define BIT(x) (1<<x)
//Macro to bind a function using lambda expressions
#define BIND_EVENT_FUNCTION(x) [this](auto&&... args) -> decltype(auto) { return this->x(std::forward<decltype(args)>(args)...); }
#endif

View File

@ -1,96 +0,0 @@
/*
Parameter.cpp
Contains two data related structures, ParameterData and Parameter. Parameter here refers to a value which
can be associated with the axis of a histogram. ParameterData is a two component struct which represents the state of a single parameter, which is stored
in the global SpectrumManager (see SpectrumManager documentation). Parameter is an access point class for use within analysis. It provides scoped access to
the managed data. There are several important caveats to using the parameters this way, which is mostly related to synchronization:
- Parameter contains no guarantee of thread safety. THIS IS BY DESIGN. Parameters should only be created within the context of an AnalysisStage (see AnalysisStage documentation).
As long as this is true, there is no risk of accessing parameter data outside of the physics thread, and that thread explicitly handles calculation of parameters
and updating of histograms. DO NOT make a Parameter outside of the AnalysisStage context.
- Parameters must be bound to the active SpectrumManager. This is done using the BindParameter function of the manager; Parameters are keyed based on their name string.
If two Parameters are made that are both named "x1", they are in fact the same parameter. In this way, values can be passed from one AnalysisStage to another. If you make a stage
called InitialStage with a Parameter named "x1" where x1 is set to 1.0, and then have a later stage called LaterStage with another Parameter named x1, it implicitly has the value 1.0
due to it being set in the previous stage.
- Each Parameter has a valid flag. This is a boolean which idicates whether or not the parameter is in a valid state (the data event contianed a value for this parameter). Before using a parameter
in a calculation one should check if the parameter is valid using the IsValid() function. When a parameter is set using the SetValue() function, the valid flag is set to true. After the event is completely
processed (all analysis stages have been run and histograms have been updated) the manager should be called to run InvalidateParameters() to set all parameters as invalid (valid flag false).
Those are the key points I can think of now. As more people use the code, I expect this section to grow and change.
Credit to nscldaq and in particular NSCLSpecTcl which provided the inspiration for this parameter model.
GWM -- Feb 2022
*/
#include "Parameter.h"
namespace Navigator {
Parameter::Parameter() :
m_name(""), m_pdata(nullptr)
{
}
Parameter::Parameter(const std::string& name) :
m_name(name), m_pdata(nullptr)
{
}
Parameter::~Parameter() {}
//So that you can make arrays, std::vectors of Parameters (useful for big detector arrays)
//SpectrumManager::BindParameter() still needs to be used after this!
void Parameter::SetName(const std::string& name)
{
if (m_name != "")
{
NAV_ERROR("Attempting to change the name of an already bound Parameter! Set name: {0} New name: {1}", m_name, name);
return;
}
m_name = name;
}
Variable::Variable() :
m_name(""), m_pdata(nullptr)
{
}
Variable::Variable(const std::string& name) :
m_name(name), m_pdata(nullptr)
{
}
Variable::~Variable() {}
void Variable::SetName(const std::string& name)
{
if (m_name != "")
{
NAV_ERROR("Attempting to change the name of an already bound Variable! Set name: {0} New name: {1}", m_name, name);
return;
}
m_name = name;
}
Scaler::Scaler() :
m_pdata(nullptr), m_name("")
{
}
Scaler::Scaler(const std::string& name) :
m_pdata(nullptr), m_name(name)
{
}
Scaler::~Scaler() {}
void Scaler::SetName(const std::string& name)
{
if (m_name != "")
{
NAV_ERROR("Attempting to change the name of an already bound Scaler! Set name: {0} New name: {1}", m_name, name);
return;
}
m_name = name;
}
}

View File

@ -1,106 +0,0 @@
/*
Parameter.h
Contains two data related structures, ParameterData and Parameter. Parameter here refers to a value which
can be associated with the axis of a histogram. ParameterData is a two component struct which represents the state of a single parameter, which is stored
in the global SpectrumManager (see SpectrumManager documentation). Parameter is an access point class for use within analysis. It provides scoped access to
the managed data. There are several important caveats to using the parameters this way, which is mostly related to synchronization:
- Parameter contains no guarantee of thread safety. THIS IS BY DESIGN. Parameters should only be created within the context of an AnalysisStage (see AnalysisStage documentation).
As long as this is true, there is no risk of accessing parameter data outside of the physics thread, and that thread explicitly handles calculation of parameters
and updating of histograms. DO NOT make a Parameter outside of the AnalysisStage context.
- Parameters must be bound to the active SpectrumManager. This is done using the BindParameter function of the manager; Parameters are keyed based on their name string.
If two Parameters are made that are both named "x1", they are in fact the same parameter. In this way, values can be passed from one AnalysisStage to another. If you make a stage
called InitialStage with a Parameter named "x1" where x1 is set to 1.0, and then have a later stage called LaterStage with another Parameter named x1, it implicitly has the value 1.0
due to it being set in the previous stage.
- Each Parameter has a valid flag. This is a boolean which idicates whether or not the parameter is in a valid state (the data event contianed a value for this parameter). Before using a parameter
in a calculation one should check if the parameter is valid using the IsValid() function. When a parameter is set using the SetValue() function, the valid flag is set to true. After the event is completely
processed (all analysis stages have been run and histograms have been updated) the manager should be called to run InvalidateParameters() to set all parameters as invalid (valid flag false).
Those are the key points I can think of now. As more people use the code, I expect this section to grow and change rapidly.
Credit to nscldaq and in particular NSCLSpecTcl which provided the inspiration for this parameter model.
GWM -- Feb 2022
*/
#ifndef PARAMETER_H
#define PARAMETER_H
#include "NavCore.h"
namespace Navigator {
//Underlying data
struct NAV_API ParameterData
{
double value=0.0;
bool validFlag=false;
};
//Interface to parameter data
class NAV_API Parameter
{
public:
Parameter();
Parameter(const std::string& name);
~Parameter();
inline bool IsValid() const { return m_pdata->validFlag; }
inline void Invalidate() { m_pdata->validFlag = false; }
inline void SetValue(double value) { m_pdata->validFlag = true; m_pdata->value = value; }
inline double GetValue() const { return m_pdata->value; }
inline const std::string& GetName() const { return m_name; }
void SetName(const std::string& name);
friend class SpectrumManager;
private:
std::string m_name;
std::shared_ptr<ParameterData> m_pdata;
};
//Similar to parameters, sometimes you want to have a numeric input (in calculation terms, a constant)
//which you can use with your analysis. To be able to expose these numeric values to the UI, we need to implement them
//in the manager. To help with this, Variables are atomics. So unlike Parameters they are implicity thread safe on read and write.
//However, this does not mean they can be modified in the analysis! To the AnalysisStage they should be treated as constant, while the UI
//should view them as modifiable. These are real god damn dangerous, but I think the power they offer outweighs the risk, for now.
class NAV_API Variable
{
public:
Variable();
Variable(const std::string& name);
~Variable();
inline void SetValue(double value) { *(m_pdata) = value; }
inline double GetValue() { return *(m_pdata); }
inline const std::string& GetName() { return m_name; }
void SetName(const std::string& name);
friend class SpectrumManager;
private:
std::shared_ptr<std::atomic<double>> m_pdata;
std::string m_name;
};
class Scaler
{
public:
Scaler();
Scaler(const std::string& name);
~Scaler();
inline void Increment() { ++(*m_pdata); }
inline const std::string& GetName() { return m_name; }
inline uint64_t GetCounts() { return *m_pdata; }
void SetName(const std::string& name);
friend class SpectrumManager;
private:
std::shared_ptr<std::atomic<uint64_t>> m_pdata;
std::string m_name;
};
}
#endif

View File

@ -1,631 +0,0 @@
/*
SpectrumManager.cpp
SpectrumManager is the global resource management class. Controls everything related to spectra (histograms, cuts, parameters). Since
the manager must traverse threads, explicit synchronoization is handled through a mutex. To this end, excessive calls to the manager should be
avoided if possible, as this will increase the lock deadtime in the application, which is especially bad for online data sources.
Note that SpectrumManager is a singleton. There should only ever be one SpectrumManager with a given application.
GWM -- Feb 2022
*/
#include "SpectrumManager.h"
#include "implot.h"
namespace Navigator {
SpectrumManager* SpectrumManager::s_instance = new SpectrumManager();
SpectrumManager::SpectrumManager()
{
}
SpectrumManager::~SpectrumManager()
{
}
/*************Histogram Functions Begin*************/
void SpectrumManager::AddHistogram(const HistogramArgs& params)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
if (params.y_par == "None") //Check dimensionality
m_histoMap[params.name].reset(new Histogram1D(params));
else
m_histoMap[params.name].reset(new Histogram2D(params));
}
void SpectrumManager::AddHistogramSummary(const HistogramArgs& params, const std::vector<std::string>& subhistos)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
m_histoMap[params.name].reset(new HistogramSummary(params, subhistos));
}
void SpectrumManager::RemoveHistogram(const std::string& name)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
m_histoMap.erase(name);
}
void SpectrumManager::AddCutToHistogramDraw(const std::string& cutname, const std::string& histoname)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_histoMap.find(histoname);
if (iter != m_histoMap.end())
iter->second->AddCutToBeDrawn(cutname);
}
void SpectrumManager::AddCutToHistogramApplied(const std::string& cutname, const std::string& histoname)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_histoMap.find(histoname);
if (iter != m_histoMap.end())
iter->second->AddCutToBeApplied(cutname);
}
//Use this to fill histograms. Currently can only be filled in bulk; maybe a use case for individual fills?
void SpectrumManager::UpdateHistograms()
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
//Set state of all cuts for the event
CheckCuts();
bool cutFlag;
for (auto& pair : m_histoMap)
{
cutFlag = true;
for (auto& cutname : pair.second->GetParameters().cutsAppliedTo) //check the associated cuts
{
if (!IsCutValid(cutname))
{
cutFlag = false;
break;
}
}
if (!cutFlag)
continue;
switch (pair.second->GetType())
{
case SpectrumType::Histo1D:
{
auto iterX = m_paramMap.find(pair.second->GetXParam());
if (iterX != m_paramMap.end() && iterX->second->validFlag)
pair.second->FillData(iterX->second->value);
break;
}
case SpectrumType::Histo2D:
{
auto iterX = m_paramMap.find(pair.second->GetXParam());
auto iterY = m_paramMap.find(pair.second->GetYParam());
if (iterX != m_paramMap.end() && iterY != m_paramMap.end() && iterX->second->validFlag && iterY->second->validFlag)
pair.second->FillData(iterX->second->value, iterY->second->value);
break;
}
case SpectrumType::Summary:
{
const std::vector<std::string>& subhistos = std::static_pointer_cast<HistogramSummary>(pair.second)->GetSubHistograms();
for (size_t i = 0; i < subhistos.size(); i++)
{
auto iterX = m_paramMap.find(subhistos[i]);
if (iterX != m_paramMap.end() && iterX->second->validFlag)
pair.second->FillData(iterX->second->value, i + 0.5); //avoid floating point conversion issues
}
break;
}
case SpectrumType::None:
{
NAV_WARN("Found a spectrum with None type!");
break;
}
}
}
//Reset the state of all cuts in preparation for next event
ResetCutValidities();
}
void SpectrumManager::ClearHistograms()
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
for (auto& pair : m_histoMap)
pair.second->ClearData();
}
void SpectrumManager::ClearHistogram(const std::string& name)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_histoMap.find(name);
if (iter != m_histoMap.end())
iter->second->ClearData();
}
void SpectrumManager::DrawHistogram(const std::string& name)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_histoMap.find(name);
if (iter != m_histoMap.end())
{
iter->second->Draw();
for (auto& cutname : iter->second->GetParameters().cutsDrawnUpon) //Draw all cuts made upon the histogram
DrawCut(cutname);
}
}
const HistogramArgs& SpectrumManager::GetHistogramParams(const std::string& name)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_histoMap.find(name);
if (iter != m_histoMap.end())
return iter->second->GetParameters();
else
return m_nullHistoResult;
}
//For 2D spectra, we want to allow zooming along the z-axis (color)
float* SpectrumManager::GetColorScaleRange(const std::string& name)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_histoMap.find(name);
if (iter != m_histoMap.end())
{
return iter->second->GetColorScaleRange();
}
return nullptr;
}
std::vector<double> SpectrumManager::GetBinData(const std::string& name)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_histoMap.find(name);
if (iter != m_histoMap.end())
{
return iter->second->GetBinData();
}
return std::vector<double>();
}
std::vector<std::string> SpectrumManager::GetSubHistograms(const std::string& name)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_histoMap.find(name);
if (iter != m_histoMap.end() && iter->second->GetType() == SpectrumType::Summary)
{
auto gram = std::static_pointer_cast<HistogramSummary>(iter->second);
return gram->GetSubHistograms();
}
return std::vector<std::string>();
}
//Pass through for stats
StatResults SpectrumManager::AnalyzeHistogramRegion(const std::string& name, const ImPlotRect& region)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_histoMap.find(name);
if (iter != m_histoMap.end())
return iter->second->AnalyzeRegion(region.X.Min, region.X.Max, region.Y.Min, region.Y.Max);
else
return StatResults();
}
//This function allows us to obtain the key histogram info in a list, avoiding excessive manager calls and thread-locks
//in something like the Editor.
std::vector<HistogramArgs> SpectrumManager::GetListOfHistograms()
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
std::vector<HistogramArgs> list;
list.reserve(m_histoMap.size());
for (auto& gram : m_histoMap)
{
list.push_back(gram.second->GetParameters());
}
return list;
}
/*************Histogram Functions End*************/
/*************Graph Functions Begin*************/
void SpectrumManager::AddGraph(const GraphArgs& args)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
m_graphMap[args.name].reset(new ScalerGraph(args));
}
void SpectrumManager::RemoveGraph(const std::string& name)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
m_graphMap.erase(name);
}
void SpectrumManager::UpdateGraphs(const Timestep& step)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
uint64_t scalerVal;
for (auto& graph : m_graphMap)
{
auto& scalerIter = m_scalerMap.find(graph.second->GetScaler());
if (scalerIter != m_scalerMap.end())
{
scalerVal = *(scalerIter->second);
graph.second->UpdatePoints(step, scalerVal);
}
}
}
void SpectrumManager::ClearGraphs()
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
for (auto& graph : m_graphMap)
graph.second->Clear();
}
void SpectrumManager::ClearGraph(const std::string& name)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto& iter = m_graphMap.find(name);
if (iter != m_graphMap.end())
iter->second->Clear();
}
void SpectrumManager::DrawGraph(const std::string& name)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto& iter = m_graphMap.find(name);
if (iter != m_graphMap.end())
iter->second->Draw();
}
const GraphArgs& SpectrumManager::GetGraphArgs(const std::string& name)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto& iter = m_graphMap.find(name);
if (iter != m_graphMap.end())
return iter->second->GetArgs();
return m_nullGraphResult;
}
std::vector<GraphArgs> SpectrumManager::GetListOfGraphs()
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
std::vector<GraphArgs> list;
list.reserve(m_graphMap.size());
for (auto& graph : m_graphMap)
list.push_back(graph.second->GetArgs());
return list;
}
/*************Graph Functions End*************/
/*************Parameter Functions Begin*************/
//Bind a Parameter instance to the manager. If the Parameter doesn't exist, make a new one, otherwise attach to extant memory
void SpectrumManager::BindParameter(Parameter& param)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_paramMap.find(param.GetName());
if (iter == m_paramMap.end())
{
m_paramMap[param.GetName()].reset(new ParameterData());
}
param.m_pdata = m_paramMap[param.GetName()];
}
//Bind a Parameter instance to the manager. If the Parameter doesn't exist, make a new one, otherwise attach to extant memory
//Additionally, make a default 1D histogram for the parameter (histogram has same name as parameter)
void SpectrumManager::BindParameter(Parameter& param, int nbins, double minVal, double maxVal)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_paramMap.find(param.GetName());
if (iter == m_paramMap.end())
{
m_paramMap[param.GetName()].reset(new ParameterData());
}
param.m_pdata = m_paramMap[param.GetName()];
auto histoIter = m_histoMap.find(param.GetName());
if (histoIter == m_histoMap.end())
{
HistogramArgs histo(param.GetName(), param.GetName(), nbins, minVal, maxVal);
m_histoMap[param.GetName()].reset(new Histogram1D(histo));
}
}
//Once an analysis pass is done and histograms filled, reset all parameters
void SpectrumManager::InvalidateParameters()
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
for (auto& param : m_paramMap)
{
param.second->validFlag = false;
param.second->value = 0.0;
}
}
//Similar to GetListOfHistograms, see that documentation
std::vector<std::string> SpectrumManager::GetListOfParameters()
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
std::vector<std::string> list;
list.reserve(m_paramMap.size());
for (auto iter : m_paramMap)
{
list.push_back(iter.first);
}
return list;
}
/*************Parameter Functions End*************/
/*************Variable Functions Begin*************/
void SpectrumManager::BindVariable(Variable& var)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_varMap.find(var.GetName());
if (iter == m_varMap.end())
{
m_varMap[var.GetName()].reset(new std::atomic<double>(0.0));
}
var.m_pdata = m_varMap[var.GetName()];
}
std::vector<std::string> SpectrumManager::GetListOfVariables()
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
std::vector<std::string> list;
list.reserve(m_varMap.size());
for (auto iter : m_varMap)
list.push_back(iter.first);
return list;
}
/*************Variable Functions End*************/
void SpectrumManager::BindScaler(Scaler& scaler)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_scalerMap.find(scaler.GetName());
if (iter == m_scalerMap.end())
{
m_scalerMap[scaler.GetName()].reset(new std::atomic<uint64_t>(0));
}
scaler.m_pdata = m_scalerMap[scaler.GetName()];
}
void SpectrumManager::ResetScalers()
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
for (auto& scaler : m_scalerMap)
{
*(scaler.second) = 0;
}
}
std::vector<std::string> SpectrumManager::GetListOfScalers()
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
std::vector<std::string> list;
list.reserve(m_scalerMap.size());
for (auto& iter : m_scalerMap)
list.push_back(iter.first);
return list;
}
/*************Cut Functions Begin*************/
void SpectrumManager::AddCut(const CutArgs& params, double min, double max)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
m_cutMap[params.name].reset(new Cut1D(params, min, max));
}
void SpectrumManager::AddCut(const CutArgs& params, const std::vector<double>& xpoints, const std::vector<double>& ypoints)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
m_cutMap[params.name].reset(new Cut2D(params, xpoints, ypoints));
}
void SpectrumManager::AddCut(const CutArgs& params, const std::vector<std::string>& subhistos, double min, double max)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
m_cutMap[params.name].reset(new CutSummary(params, subhistos, min, max));
}
void SpectrumManager::RemoveCut(const std::string& name)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_managerMutex);
m_cutMap.erase(name);
RemoveCutFromHistograms(name); //Once a cut is gone, remove all references to it.
}
std::vector<double> SpectrumManager::GetCutXPoints(const std::string& name)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
std::vector<double> null_result;
auto iter = m_cutMap.find(name);
if (iter != m_cutMap.end())
return iter->second->GetXValues();
return null_result;
}
std::vector<double> SpectrumManager::GetCutYPoints(const std::string& name)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
std::vector<double> null_result;
auto iter = m_cutMap.find(name);
if (iter != m_cutMap.end())
return iter->second->GetYValues();
return null_result;
}
std::vector<std::string> SpectrumManager::GetCutSubHistograms(const std::string& cutname)
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
auto iter = m_cutMap.find(cutname);
if (iter != m_cutMap.end() && iter->second->GetType() == CutType::CutSummaryAny || iter->second->GetType() == CutType::CutSummaryAll)
{
auto cut = std::static_pointer_cast<CutSummary>(iter->second);
return cut->GetSubHistograms();
}
return std::vector<std::string>();
}
//Similar to GetListOfHistograms, see that documentation
std::vector<CutArgs> SpectrumManager::GetListOfCuts()
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
std::vector<CutArgs> list;
list.reserve(m_cutMap.size());
for (auto& entry : m_cutMap)
list.push_back(entry.second->GetCutArgs());
return list;
}
/*************Cut Functions End*************/
/*
Private Functions
Can only be called from within the SpectrumManager, therefore the lock should already have been aquired by
whatever parent function calls them. No explicit synchronization.
*/
//Can only be called by RemoveCut currently. May be a use case where this should be promoted to public to on the fly mod a gram.
void SpectrumManager::RemoveCutFromHistograms(const std::string& cutname)
{
NAV_PROFILE_FUNCTION();
for (auto& gram : m_histoMap)
{
auto& params = gram.second->GetParameters();
for (size_t i = 0; i < params.cutsDrawnUpon.size(); ++i)
{
if (params.cutsDrawnUpon[i] == cutname)
{
params.cutsDrawnUpon.erase(params.cutsDrawnUpon.begin() + i);
}
}
for (size_t i = 0; i < params.cutsAppliedTo.size(); ++i)
{
if (params.cutsAppliedTo[i] == cutname)
{
params.cutsAppliedTo.erase(params.cutsAppliedTo.begin() + i);
}
}
}
}
//Obv. only need to draw a cut if its parent histogram is drawn.
void SpectrumManager::DrawCut(const std::string& name)
{
NAV_PROFILE_FUNCTION();
auto iter = m_cutMap.find(name);
if (iter != m_cutMap.end())
iter->second->Draw();
}
//Set the state of the cuts for the current event. Called by the
void SpectrumManager::CheckCuts()
{
NAV_PROFILE_FUNCTION();
for (auto& iter : m_cutMap)
{
const std::string& xpar = iter.second->GetXParameter();
const std::string& ypar = iter.second->GetYParameter();
switch (iter.second->GetType())
{
case CutType::Cut1D:
{
auto iterX = m_paramMap.find(xpar);
if (iterX != m_paramMap.end() && iterX->second->validFlag)
iter.second->IsInside(iterX->second->value);
break;
}
case CutType::Cut2D:
{
auto iterX = m_paramMap.find(xpar);
auto iterY = m_paramMap.find(ypar);
if (iterX != m_paramMap.end() && iterX->second->validFlag && iterY != m_paramMap.end() && iterY->second->validFlag)
iter.second->IsInside(iterX->second->value, iterY->second->value);
break;
}
case CutType::CutSummaryAll:
{
std::vector<std::string> paramlist = std::static_pointer_cast<CutSummary>(iter.second)->GetSubHistograms();
for (auto& param : paramlist)
{
auto iterX = m_paramMap.find(param);
if (iterX != m_paramMap.end() && iterX->second->validFlag)
{
iter.second->IsInside(iterX->second->value);
if (!iter.second->IsValid())
break;
}
}
break;
}
case CutType::CutSummaryAny:
{
std::vector<std::string> paramlist = std::static_pointer_cast<CutSummary>(iter.second)->GetSubHistograms();
for (auto& param : paramlist)
{
auto iterX = m_paramMap.find(param);
if (iterX != m_paramMap.end() && iterX->second->validFlag)
{
iter.second->IsInside(iterX->second->value);
if (iter.second->IsValid())
break;
}
}
break;
}
case CutType::None:
{
NAV_WARN("Found a cut with None type!");
break;
}
}
}
}
bool SpectrumManager::IsCutValid(const std::string& name)
{
NAV_PROFILE_FUNCTION();
auto iter = m_cutMap.find(name);
if (iter != m_cutMap.end())
return iter->second->IsValid();
return false;
}
void SpectrumManager::ResetCutValidities()
{
for (auto& iter : m_cutMap)
{
iter.second->ResetValidity();
}
}
}

View File

@ -1,126 +0,0 @@
/*
SpectrumManager.h
SpectrumManager is the global resource management class. Controls everything related to spectra (histograms, cuts, parameters). Since
the manager must traverse threads, explicit synchronoization is handled through a mutex. To this end, excessive calls to the manager should be
avoided if possible, as this will increase the lock deadtime in the application, which is especially bad for online data sources.
Note that SpectrumManager is a singleton. There should only ever be one SpectrumManager with a given application.
GWM -- Feb 2022
*/
#ifndef SPECTRUM_MANAGER_H
#define SPECTRUM_MANAGER_H
#include "Histogram.h"
#include "Cut.h"
#include "Parameter.h"
#include "Graph.h"
#include "Timestep.h"
#include <thread>
struct ImPlotRect;
namespace Navigator {
class NAV_API SpectrumManager
{
public:
SpectrumManager();
~SpectrumManager();
inline static SpectrumManager& GetInstance() { return *s_instance; }
//To clear all managed spectra. Note that Parameters are left untouched.
inline void RemoveAllSpectra()
{
std::scoped_lock<std::mutex> guard(m_managerMutex);
m_histoMap.clear();
m_cutMap.clear();
}
/*Histogram Functions*/
void AddHistogram(const HistogramArgs& params);
void AddHistogramSummary(const HistogramArgs& params, const std::vector<std::string>& subhistos);
void RemoveHistogram(const std::string& name);
void AddCutToHistogramDraw(const std::string& cutname, const std::string& histoname);
void AddCutToHistogramApplied(const std::string& cutname, const std::string& histoname);
void UpdateHistograms();
void ClearHistograms();
void ClearHistogram(const std::string& name);
void DrawHistogram(const std::string& name);
const HistogramArgs& GetHistogramParams(const std::string& name);
float* GetColorScaleRange(const std::string& name);
std::vector<double> GetBinData(const std::string& name);
std::vector<std::string> GetSubHistograms(const std::string& name);
StatResults AnalyzeHistogramRegion(const std::string& name, const ImPlotRect& region);
std::vector<HistogramArgs> GetListOfHistograms();
/********************/
/*ScalerGraph Functions*/
void AddGraph(const GraphArgs& args);
void RemoveGraph(const std::string& name);
void UpdateGraphs(const Timestep& step);
void ClearGraphs();
void ClearGraph(const std::string& name);
void DrawGraph(const std::string& name);
const GraphArgs& GetGraphArgs(const std::string& name);
std::vector<GraphArgs> GetListOfGraphs();
/********************/
/*Parameter Functions*/
void BindParameter(Parameter& param);
void BindParameter(Parameter& param, int nbins, double maxVal, double minVal);
void InvalidateParameters();
std::vector<std::string> GetListOfParameters();
/*********************/
/*Variable Functions*/
void BindVariable(Variable& var);
std::vector<std::string> GetListOfVariables();
/********************/
/*Scaler Functions*/
void BindScaler(Scaler& scaler);
void ResetScalers();
std::vector<std::string> GetListOfScalers();
/******************/
/*Cut Functions*/
void AddCut(const CutArgs& params, double min, double max);
void AddCut(const CutArgs& params, const std::vector<double>& xpoints, const std::vector<double>& ypoints);
void AddCut(const CutArgs& params, const std::vector<std::string>& subhistos, double min, double max);
void RemoveCut(const std::string& name);
std::vector<double> GetCutXPoints(const std::string& name);
std::vector<double> GetCutYPoints(const std::string& name);
std::vector<std::string> GetCutSubHistograms(const std::string& cutname);
std::vector<CutArgs> GetListOfCuts();
/**************/
private:
//Only used from within manager
void RemoveCutFromHistograms(const std::string& cutname);
void DrawCut(const std::string& name);
void CheckCuts();
bool IsCutValid(const std::string& name);
void ResetCutValidities();
static SpectrumManager* s_instance;
//Actual data
std::unordered_map<std::string, std::shared_ptr<Histogram>> m_histoMap;
std::unordered_map<std::string, std::shared_ptr<Cut>> m_cutMap;
std::unordered_map<std::string, std::shared_ptr<ParameterData>> m_paramMap;
std::unordered_map<std::string, std::shared_ptr<std::atomic<double>>> m_varMap;
std::unordered_map<std::string, std::shared_ptr<std::atomic<uint64_t>>> m_scalerMap;
std::unordered_map<std::string, std::shared_ptr<ScalerGraph>> m_graphMap;
HistogramArgs m_nullHistoResult; //For handling bad query
GraphArgs m_nullGraphResult; //For handling bad query
std::mutex m_managerMutex; //synchronization
};
}
#endif

View File

@ -1,455 +0,0 @@
/*
SpectrumSerializer.h
SpectrumSerializer class providing method to write/read spectra (histograms and cuts) to/from a .nav file. These are formated text files.
Note that by virtue of the way that cuts work, they are written first.
A couple of notes:
- Writing/reading data is a pretty expensive concept from a thread-locking perspective. Not recommended for use during active analysis.
- Deserializing (reading) by default removes all current histograms and cuts. This avoids any issues with collisions, but may not always be desireable.
- There is no intrinsic checking of whether a parameter for a cut/histogram to be loaded exists within the current project. If you load something and the histograms
don't fill, it is most likely due to the parameter not being defined in the current project.
GWM -- Feb 2022
*/
#include "SpectrumSerializer.h"
#include "SpectrumManager.h"
#include <fstream>
namespace Navigator {
SpectrumSerializer::SpectrumSerializer(const std::string& filepath) :
m_filename(filepath)
{
}
SpectrumSerializer::~SpectrumSerializer() {}
void SpectrumSerializer::SerializeData(const std::vector<HistogramArgs>& histoList, const std::vector<CutArgs>& cutList)
{
SpectrumManager& manager = SpectrumManager::GetInstance();
std::ofstream output(m_filename);
if (!output.is_open())
{
NAV_ERROR("Unable to open {0} to write data.", m_filename);
return;
}
output << "begin_cuts" << std::endl;
for (auto& cut : cutList)
{
if (cut.type == CutType::Cut1D)
{
std::vector<double> xpoints = manager.GetCutXPoints(cut.name);
output << "\tbegin_cut1D" << std::endl;
output << "\t\tname: " << cut.name << std::endl;
output << "\t\txparam: " << cut.x_par << std::endl;
output << "\t\tminValue: " << xpoints[0] << std::endl;
output << "\t\tmaxValue: " << xpoints[1] << std::endl;
output << "\tend_cut1D" << std::endl;
}
else if (cut.type == CutType::Cut2D)
{
std::vector<double> xpoints = manager.GetCutXPoints(cut.name);
std::vector<double> ypoints = manager.GetCutYPoints(cut.name);
output << "\tbegin_cut2D" << std::endl;
output << "\t\tname: " << cut.name << std::endl;
output << "\t\txparam: " << cut.x_par << std::endl;
output << "\t\typaram: " << cut.y_par << std::endl;
output << "\t\tbegin_xvalues" << std::endl;
for (const auto& value : xpoints)
{
output << "\t\t\t" << value << std::endl;
}
output << "\t\tend_xvalues" << std::endl;
output << "\t\tbegin_yvalues" << std::endl;
for (const auto& value : ypoints)
{
output << "\t\t\t" << value << std::endl;
}
output << "\t\tend_yvalues" << std::endl;
output << "\tend_cut2D" << std::endl;
}
else if (cut.type == CutType::CutSummaryAll)
{
std::vector<std::string> subhistos = manager.GetCutSubHistograms(cut.name);
std::vector<double> xpoints = manager.GetCutXPoints(cut.name);
output << "\tbegin_cutSummaryAll" << std::endl;
output << "\t\tname: " << cut.name << std::endl;
output << "\t\tminValue: " << xpoints[0] << std::endl;
output << "\t\tmaxValue: " << xpoints[1] << std::endl;
output << "\t\tbegin_parameters" << std::endl;
for (auto& par : subhistos)
{
output << "\t\t\t" << par << std::endl;
}
output << "\t\tend_parameters" << std::endl;
output << "\tend_cutSummaryAll" << std::endl;
}
else if (cut.type == CutType::CutSummaryAny)
{
std::vector<std::string> subhistos = manager.GetCutSubHistograms(cut.name);
std::vector<double> xpoints = manager.GetCutXPoints(cut.name);
output << "\tbegin_cutSummaryAny" << std::endl;
output << "\t\tname: " << cut.name << std::endl;
output << "\t\tminValue: " << xpoints[0] << std::endl;
output << "\t\tmaxValue: " << xpoints[1] << std::endl;
output << "\t\tbegin_parameters" << std::endl;
for (auto& par : subhistos)
{
output << "\t\t\t" << par << std::endl;
}
output << "\t\tend_parameters" << std::endl;
output << "\tend_cutSummaryAny" << std::endl;
}
}
output << "end_cuts" << std::endl;
output << "begin_histograms" << std::endl;
for (auto& params : histoList)
{
if (params.type == SpectrumType::Histo1D)
{
output << "\tbegin_histogram1D" << std::endl;
output << "\t\tname: " << params.name << std::endl;
output << "\t\txparam: " << params.x_par << std::endl;
output << "\t\tNxbins: " << params.nbins_x << std::endl;
output << "\t\tXMin: " << params.min_x << std::endl;
output << "\t\tXMax: " << params.max_x << std::endl;
output << "\t\tbegin_cutsdrawn" << std::endl;
for (const auto& name : params.cutsDrawnUpon)
{
output << "\t\t\t" << name << std::endl;
}
output << "\t\tend_cutsdrawn" << std::endl;
output << "\t\tbegin_cutsapplied" << std::endl;
for (const auto& name : params.cutsAppliedTo)
{
output << "\t\t\t" << name << std::endl;
}
output << "\t\tend_cutsapplied" << std::endl;
output << "\tend_histogram1D" << std::endl;
}
else if (params.type == SpectrumType::Histo2D)
{
output << "\tbegin_histogram2D" << std::endl;
output << "\t\tname: " << params.name << std::endl;
output << "\t\txparam: " << params.x_par << std::endl;
output << "\t\typaram: " << params.y_par << std::endl;
output << "\t\tNxbins: " << params.nbins_x << std::endl;
output << "\t\tXMin: " << params.min_x << std::endl;
output << "\t\tXMax: " << params.max_x << std::endl;
output << "\t\tNybins: " << params.nbins_y << std::endl;
output << "\t\tYMin: " << params.min_y << std::endl;
output << "\t\tYMax: " << params.max_y << std::endl;
output << "\t\tbegin_cutsdrawn" << std::endl;
for (const auto& name : params.cutsDrawnUpon)
{
output << "\t\t\t" << name << std::endl;
}
output << "\t\tend_cutsdrawn" << std::endl;
output << "\t\tbegin_cutsapplied" << std::endl;
for (const auto& name : params.cutsAppliedTo)
{
output << "\t\t\t" << name << std::endl;
}
output << "\t\tend_cutsapplied" << std::endl;
output << "\tend_histogram2D" << std::endl;
}
else if (params.type == SpectrumType::Summary)
{
output << "\tbegin_histogramSummary" << std::endl;
output << "\t\tname: " << params.name << std::endl;
output << "\t\tNxbins: " << params.nbins_x << std::endl;
output << "\t\tXMin: " << params.min_x << std::endl;
output << "\t\tXMax: " << params.max_x << std::endl;
output << "\t\tbegin_subhistos" << std::endl;
std::vector<std::string> subhistos = manager.GetSubHistograms(params.name);
for (auto& name : subhistos)
{
output << "\t\t\t" << name << std::endl;
}
output << "\t\tend_subhistos" << std::endl;
output << "\t\tbegin_cutsdrawn" << std::endl;
for (const auto& name : params.cutsDrawnUpon)
{
output << "\t\t\t" << name << std::endl;
}
output << "\t\tend_cutsdrawn" << std::endl;
output << "\t\tbegin_cutsapplied" << std::endl;
for (const auto& name : params.cutsAppliedTo)
{
output << "\t\t\t" << name << std::endl;
}
output << "\t\tend_cutsapplied" << std::endl;
output << "\tend_histogram1D" << std::endl;
}
}
output << "end_histograms" << std::endl;
NAV_INFO("Successfully saved data to {0}", m_filename);
output.close();
}
void SpectrumSerializer::DeserializeData()
{
SpectrumManager& manager = SpectrumManager::GetInstance();
manager.RemoveAllSpectra(); //When loading in, we remove all extant data, to avoid any potential collisions.
std::ifstream input(m_filename);
if (!input.is_open())
{
NAV_ERROR("Unable to open {0} to read data!", m_filename);
return;
}
std::string check;
double value_doub;
CutArgs cut_data, reset_cut;
std::vector<double> cut_xdata;
std::vector<double> cut_ydata;
std::vector<std::string> subhistos;
HistogramArgs hist_data, reset_hist;
while (input >> check)
{
if (check == "begin_cuts")
{
while (input >> check)
{
cut_data = reset_cut;
cut_xdata.clear();
cut_ydata.clear();
subhistos.clear();
if (check == "begin_cut1D")
{
cut_data.type = CutType::Cut1D;
input >> check >> cut_data.name;
input >> check >> cut_data.x_par;
input >> check >> value_doub;
cut_xdata.push_back(value_doub);
input >> check >> value_doub;
cut_xdata.push_back(value_doub);
input >> check;
manager.AddCut(cut_data, cut_xdata[0], cut_xdata[1]);
}
else if (check == "begin_cut2D")
{
cut_data.type = CutType::Cut2D;
input >> check >> cut_data.name;
input >> check >> cut_data.x_par;
input >> check >> cut_data.y_par;
while (input >> check)
{
if (check == "begin_xvalues")
continue;
else if (check == "end_xvalues")
break;
else
cut_xdata.push_back(std::stod(check));
}
while (input >> check)
{
if (check == "begin_yvalues")
continue;
else if (check == "end_yvalues")
break;
else
cut_ydata.push_back(std::stod(check));
}
input >> check;
manager.AddCut(cut_data, cut_xdata, cut_ydata);
}
else if (check == "begin_cutSummaryAll")
{
cut_data.type = CutType::CutSummaryAll;
input >> check >> cut_data.name;
input >> check >> value_doub;
cut_xdata.push_back(value_doub);
input >> check >> value_doub;
cut_xdata.push_back(value_doub);
while (input >> check)
{
if (check == "begin_parameters")
continue;
else if (check == "end_parameters")
break;
else
subhistos.push_back(check);
}
input >> check;
manager.AddCut(cut_data, subhistos, cut_xdata[0], cut_xdata[1]);
}
else if (check == "begin_cutSummaryAny")
{
cut_data.type = CutType::CutSummaryAny;
input >> check >> cut_data.name;
input >> check >> value_doub;
cut_xdata.push_back(value_doub);
input >> check >> value_doub;
cut_xdata.push_back(value_doub);
while (input >> check)
{
if (check == "begin_parameters")
continue;
else if (check == "end_parameters")
break;
else
subhistos.push_back(check);
}
input >> check;
manager.AddCut(cut_data, subhistos, cut_xdata[0], cut_xdata[1]);
}
else if (check == "end_cuts")
break;
else
{
NAV_ERROR("Deserialization error; unexpected check condition while parsing cut data! Current value: {0}", check);
input.close();
return;
}
}
}
else if (check == "begin_histograms")
{
while (input >> check)
{
hist_data = reset_hist;
if (check == "begin_histogram1D")
{
hist_data.type = SpectrumType::Histo1D;
input >> check >> hist_data.name;
input >> check >> hist_data.x_par;
input >> check >> hist_data.nbins_x;
input >> check >> hist_data.min_x;
input >> check >> hist_data.max_x;
while (input >> check)
{
if (check == "begin_cutsdrawn")
continue;
else if (check == "end_cutsdrawn")
break;
else
{
hist_data.cutsDrawnUpon.push_back(check);
}
}
while (input >> check)
{
if (check == "begin_cutsapplied")
continue;
else if (check == "end_cutsapplied")
break;
else
{
hist_data.cutsAppliedTo.push_back(check);
}
}
input >> check;
manager.AddHistogram(hist_data);
}
else if (check == "begin_histogram2D")
{
hist_data.type = SpectrumType::Histo2D;
input >> check >> hist_data.name;
input >> check >> hist_data.x_par;
input >> check >> hist_data.y_par;
input >> check >> hist_data.nbins_x;
input >> check >> hist_data.min_x;
input >> check >> hist_data.max_x;
input >> check >> hist_data.nbins_y;
input >> check >> hist_data.min_y;
input >> check >> hist_data.max_y;
while (input >> check)
{
if (check == "begin_cutsdrawn")
continue;
else if (check == "end_cutsdrawn")
break;
else
{
hist_data.cutsDrawnUpon.push_back(check);
}
}
while (input >> check)
{
if (check == "begin_cutsapplied")
continue;
else if (check == "end_cutsapplied")
break;
else
{
hist_data.cutsAppliedTo.push_back(check);
}
}
input >> check;
manager.AddHistogram(hist_data);
}
else if (check == "begin_histogramSummary")
{
subhistos.clear();
hist_data.type = SpectrumType::Summary;
input >> check >> hist_data.name;
input >> check >> hist_data.nbins_x;
input >> check >> hist_data.min_x;
input >> check >> hist_data.max_x;
while (input >> check)
{
if (check == "begin_subhistos")
continue;
else if (check == "end_subhistos")
break;
else
{
subhistos.push_back(check);
}
}
while (input >> check)
{
if (check == "begin_cutsdrawn")
continue;
else if (check == "end_cutsdrawn")
break;
else
{
hist_data.cutsDrawnUpon.push_back(check);
}
}
while (input >> check)
{
if (check == "begin_cutsapplied")
continue;
else if (check == "end_cutsapplied")
break;
else
{
hist_data.cutsAppliedTo.push_back(check);
}
}
input >> check;
manager.AddHistogramSummary(hist_data, subhistos);
}
else if (check == "end_histograms")
break;
else
{
NAV_ERROR("Deserialization error; unexpected check condition while parsing histogram data! Current value: {0}", check);
input.close();
return;
}
}
}
else
{
NAV_ERROR("Deserialization error; unexpected check condition at top level! Current value: {0}", check);
input.close();
return;
}
}
NAV_INFO("Successfully loaded data from {0}", m_filename);
input.close();
}
}

View File

@ -1,39 +0,0 @@
/*
SpectrumSerializer.h
SpectrumSerializer class providing method to write/read spectra (histograms and cuts) to/from a .nav file. These are formated text files.
Note that by virtue of the way that cuts work, they are written first.
A couple of notes:
- Writing/reading data is a pretty expensive concept from a thread-locking perspective. Not recommended for use during active analysis.
- Deserializing (reading) by default removes all current histograms and cuts. This avoids any issues with collisions, but may not always be desireable.
- There is no intrinsic checking of whether a parameter for a cut/histogram to be loaded exists within our framework. If you load something and the histograms
don't fill, it is most likely due to the parameter not being defined in the current project.
GWM -- Feb 2022
*/
#ifndef SPECTRUM_SERIALIZER_H
#define SPECTRUM_SERIALIZER_H
#include "Histogram.h"
#include "Cut.h"
namespace Navigator {
class NAV_API SpectrumSerializer
{
public:
SpectrumSerializer(const std::string& filepath);
~SpectrumSerializer();
void SerializeData(const std::vector<HistogramArgs>& histoList, const std::vector<CutArgs>& cutList);
void DeserializeData();
inline const std::string& GetFilename() { return m_filename; }
private:
std::string m_filename;
};
}
#endif

View File

@ -1,25 +0,0 @@
#ifndef TIMESTEP_H
#define TIMESTEP_H
namespace Navigator {
class Timestep
{
public:
Timestep(float time=0.0f) :
m_time(time)
{
}
void SetTime(float time) { m_time = time; }
operator float() const { return m_time; }
float GetElapsedSeconds() const { return m_time; }
float GetElapsedMilliseconds() const { return m_time * 1000.0f; }
private:
float m_time;
};
}
#endif

View File

@ -1,53 +0,0 @@
/*
Window.h
Abstract class for a window in Navigator. Based entirely upon the work of @TheCherno, see his Hazel repository. This exists primarily to allow for the option
to extend Navigator to other rendering backends, most likely Metal, or potentially DX12. See code in Platform for specific implementations (currently only for OpenGL).
GWM -- Feb 2022
*/
#ifndef WINDOW_H
#define WINDOW_H
#include "NavCore.h"
#include "Navigator/Events/Event.h"
namespace Navigator {
class NAV_API WindowProperties
{
public:
unsigned int width, height;
std::string name;
WindowProperties(const std::string& title="Navigator", unsigned int w=1280, unsigned int h=720) :
width(w), height(h), name(title)
{
}
};
class NAV_API Window
{
public:
using EventCallbackFunc = std::function<void(Event&)>;
virtual ~Window() {}
virtual void OnUpdate() = 0;
virtual unsigned int GetWidth() const = 0;
virtual unsigned int GetHeight() const = 0;
virtual std::string GetName() const = 0;
virtual void SetEventCallback(const EventCallbackFunc& function) = 0;
virtual void SetVSync(bool enabled) = 0;
virtual bool IsVSync() const = 0;
virtual void* GetNativeWindow() const =0;
static Window* Create(const WindowProperties& props = WindowProperties());
};
}
#endif

View File

@ -1,416 +0,0 @@
/*
EditorLayer.cpp
Application layer encapsulating the editor for Navigator. Written using the Dear ImGui library. Setup based off of @TheCherno's Hazel game engine.
EditorLayer essentially controls the state for UI related actions.
GWM -- Feb 2022
*/
#include "EditorLayer.h"
#include "imgui.h"
#include "misc/cpp/imgui_stdlib.h"
#include "implot.h"
#include "FileDialog.h"
#include "Navigator/Core/Application.h"
#include "Navigator/Core/SpectrumSerializer.h"
#include "Navigator/Core/SpectrumManager.h"
#include "IconsFontAwesome5.h"
namespace Navigator {
bool SortByString(const std::string& p1, const std::string& p2)
{
return p1 < p2;
}
EditorLayer::EditorLayer() :
Layer("EditorLayer"), m_removeHistogram(false), m_removeCut(false), m_exportHistogram(false)
{
}
EditorLayer::~EditorLayer() {}
void EditorLayer::OnAttach()
{
}
void EditorLayer::OnDetach()
{
}
void EditorLayer::OnUpdate(Timestep& step)
{
}
void EditorLayer::OnEvent(Event& e)
{
}
//These updates are used whenever a new object is added to the manager.
void EditorLayer::UpdateHistogramList()
{
m_histoList = SpectrumManager::GetInstance().GetListOfHistograms();
std::sort(m_histoList.begin(), m_histoList.end(), SortByName<HistogramArgs>);
}
void EditorLayer::UpdateCutList()
{
m_cutList = SpectrumManager::GetInstance().GetListOfCuts();
std::sort(m_cutList.begin(), m_cutList.end(), SortByName<CutArgs>);
}
void EditorLayer::UpdateParameterList()
{
m_paramList = SpectrumManager::GetInstance().GetListOfParameters();
std::sort(m_paramList.begin(), m_paramList.end(), SortByString);
}
//The main function
void EditorLayer::OnImGuiRender()
{
NAV_PROFILE_FUNCTION();
static bool startFlag = true; //first render retrieve base
if(startFlag)
{
UpdateParameterList();
UpdateHistogramList();
UpdateCutList();
startFlag = false;
}
// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
// because it would be confusing to have two docking targets within each others.
if (opt_fullscreen)
{
ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::SetNextWindowViewport(viewport->ID);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
}
// When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background.
if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
window_flags |= ImGuiWindowFlags_NoBackground;
// Important: note that we proceed even if Begin() returns false (aka window is collapsed).
// This is because we want to keep our DockSpace() active. If a DockSpace() is inactive,
// all active windows docked into it will lose their parent and become undocked.
// We cannot preserve the docking relationship between an active window and an inactive docking, otherwise
// any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("MyTestSpace", &dockspaceOpen, window_flags);
ImGui::PopStyleVar();
if (opt_fullscreen)
ImGui::PopStyleVar(2);
// DockSpace
ImGuiIO& io = ImGui::GetIO();
ImGuiStyle& style = ImGui::GetStyle();
float minWinSizeX = style.WindowMinSize.x;
style.WindowMinSize.x = 370.0f;
if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
{
ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
}
style.WindowMinSize.x = minWinSizeX;
if(ImGui::BeginMenuBar())
{
if(ImGui::BeginMenu("File"))
{
if(ImGui::MenuItem(ICON_FA_FOLDER_OPEN "\tOpen"))
{
m_fileDialog.OpenDialog(FileDialog::Type::OpenFile);
}
if(ImGui::MenuItem(ICON_FA_SAVE "\tSave"))
{
m_fileDialog.OpenDialog(FileDialog::Type::SaveFile);
}
if (ImGui::MenuItem(ICON_FA_TIMES_CIRCLE "\tExit"))
{
Application::Get().Close();
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Data Source"))
{
if (ImGui::MenuItem(ICON_FA_LINK "\tAttach Source"))
{
m_sourceDialog.OpenSourceDialog();
}
if (ImGui::MenuItem(ICON_FA_UNLINK "\tDetach Source"))
{
PhysicsStopEvent event;
m_callbackFunc(event);
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Add"))
{
if (ImGui::MenuItem(ICON_FA_CHART_BAR "\tSpectrum"))
{
m_spectrumDialog.SetSpectrumDialog();
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Remove"))
{
if (ImGui::MenuItem(ICON_FA_CHART_BAR "\tSpectrum"))
{
m_removeHistogram = true;
}
if (ImGui::MenuItem(ICON_FA_CUT "\tCut"))
{
m_removeCut = true;
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Export"))
{
if (ImGui::MenuItem(ICON_FA_SAVE "\tAs .csv"))
{
m_exportHistogram = true;
}
ImGui::EndMenu();
}
ImGui::EndMenuBar();
}
//Render all of our sub-windows, dialogs, panels, etc
auto fd_result = m_fileDialog.RenderFileDialog(".nav");
if (!fd_result.first.empty())
{
switch (fd_result.second)
{
case FileDialog::Type::OpenFile:
{
SpectrumSerializer serializer(fd_result.first);
serializer.DeserializeData();
UpdateHistogramList();
UpdateCutList();
break;
}
case FileDialog::Type::SaveFile:
{
NAV_INFO("Found a Save File! {0}", fd_result.first);
SpectrumSerializer serializer(fd_result.first);
serializer.SerializeData(m_histoList, m_cutList);
break;
}
}
}
if(m_spectrumDialog.ImGuiRenderSpectrumDialog(m_histoList, m_cutList, m_paramList))
UpdateHistogramList();
m_sourceDialog.ImGuiRenderSourceDialog();
RemoveHistogramDialog();
RemoveCutDialog();
ExportHistogramDialog();
if(m_spectrumPanel.OnImGuiRender(m_histoList, m_cutList, m_paramList))
{
UpdateCutList();
UpdateHistogramList();
}
if (ImGui::Begin(ICON_FA_CHART_BAR " Spectra"))
{
for (auto& params : m_histoList)
{
if (ImGui::TreeNode(params.name.c_str()))
{
ImGui::BulletText("%s", ("X Parameter: "+params.x_par).c_str());
ImGui::BulletText("X Bins: %d X Min: %f X Max: %f", params.nbins_x, params.min_x, params.max_x);
if (params.y_par != "None")
{
ImGui::BulletText("%s", ("Y Parameter: "+params.y_par).c_str());
ImGui::BulletText("Y Bins: %d Y Min: %f Y Max: %f", params.nbins_y, params.min_y, params.max_y);
}
if(params.cutsDrawnUpon.size() != 0 && ImGui::TreeNode("Cuts Drawn"))
{
for(auto& cut : params.cutsDrawnUpon)
ImGui::BulletText("%s", cut.c_str());
ImGui::TreePop();
}
if(params.cutsAppliedTo.size() != 0 && ImGui::TreeNode("Cuts Applied"))
{
for(auto& cut : params.cutsAppliedTo)
ImGui::BulletText("%s", cut.c_str());
ImGui::TreePop();
}
ImGui::TreePop();
}
}
}
ImGui::End();
if(ImGui::Begin(ICON_FA_CUT " Cuts"))
{
for(auto& params : m_cutList)
{
if(ImGui::TreeNode(params.name.c_str()))
{
ImGui::BulletText("%s", ("X Parameter: "+params.x_par).c_str());
if(params.y_par != "None")
ImGui::BulletText("%s", ("Y Parameter: "+params.y_par).c_str());
ImGui::TreePop();
}
}
}
ImGui::End();
ImGui::End();
}
//Simple dialogs, no need for separate class
void EditorLayer::RemoveHistogramDialog()
{
NAV_PROFILE_FUNCTION();
static std::string selectedGram = "";
if (m_removeHistogram)
{
selectedGram = "";
m_removeHistogram = false;
ImGui::OpenPopup("Remove Histogram");
}
if (ImGui::BeginPopupModal("Remove Histogram"))
{
if (ImGui::BeginCombo("Histogram", selectedGram.c_str()))
{
for (auto& gram : m_histoList)
{
if (ImGui::Selectable(gram.name.c_str(), gram.name == selectedGram, ImGuiSelectableFlags_DontClosePopups))
selectedGram = gram.name;
}
ImGui::EndCombo();
}
if (ImGui::Button("Ok"))
{
SpectrumManager::GetInstance().RemoveHistogram(selectedGram);
UpdateHistogramList();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void EditorLayer::RemoveCutDialog()
{
NAV_PROFILE_FUNCTION();
static std::string selectedCut = "";
if (m_removeCut)
{
selectedCut = "";
m_removeCut = false;
ImGui::OpenPopup("Remove Cut");
}
if (ImGui::BeginPopupModal("Remove Cut"))
{
if (ImGui::BeginCombo("Cut", selectedCut.c_str()))
{
for (auto& cut : m_cutList)
{
if (ImGui::Selectable(cut.name.c_str(), cut.name == selectedCut, ImGuiSelectableFlags_DontClosePopups))
selectedCut = cut.name;
}
ImGui::EndCombo();
}
if (ImGui::Button("Ok"))
{
SpectrumManager::GetInstance().RemoveCut(selectedCut);
UpdateHistogramList();
UpdateCutList();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void EditorLayer::ExportHistogramDialog()
{
NAV_PROFILE_FUNCTION();
static std::string filename = "";
static HistogramArgs selectedGram = HistogramArgs();
if(m_exportHistogram)
{
filename = "";
selectedGram = HistogramArgs();
m_exportHistogram = false;
ImGui::OpenPopup("Export Histogram");
}
if(ImGui::BeginPopupModal("Export Histogram"))
{
if(ImGui::BeginCombo("Histogram", selectedGram.name.c_str()))
{
for (auto& gram : m_histoList)
{
if (ImGui::Selectable(gram.name.c_str(), gram.name == selectedGram.name, ImGuiSelectableFlags_DontClosePopups))
selectedGram = gram;
}
ImGui::EndCombo();
}
ImGui::InputText("File", &filename);
ImGui::SameLine();
if(ImGui::Button("Open"))
{
m_fileDialog.OpenDialog(FileDialog::Type::SaveFile);
}
auto result = m_fileDialog.RenderFileDialog(".csv");
if(!result.first.empty() && result.second == FileDialog::Type::SaveFile)
filename = result.first;
if(ImGui::Button("Ok"))
{
ExportHistogram(selectedGram, filename);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if(ImGui::Button("Cancel"))
ImGui::CloseCurrentPopup();
ImGui::EndPopup();
}
}
void EditorLayer::ExportHistogram(HistogramArgs selectedGram, const std::string& filename)
{
std::ofstream output(filename);
if(!output.is_open())
{
NAV_ERROR("Unable to create export file {0}. Check pathing.", filename);
return;
}
std::vector<double> data = SpectrumManager::GetInstance().GetBinData(selectedGram.name);
output<<"Histogram Name,"<<selectedGram.name<<std::endl;
output<<"Min X,"<<selectedGram.min_x<<std::endl<<"Max X,"<<selectedGram.max_x<<std::endl;
if(selectedGram.y_par != "None")
output<<"Min Y,"<<selectedGram.min_y<<std::endl<<"Max Y,"<<selectedGram.max_y<<std::endl;
output<<"Nbins,"<<data.size()<<std::endl;
output<<"Bin,Counts"<<std::endl;
for(size_t i=0; i<data.size(); i++)
output<<i<<","<<data[i]<<std::endl;
output.close();
}
}

View File

@ -1,81 +0,0 @@
/*
EditorLayer.h
Application layer encapsulating the editor for Navigator. Written using the Dear ImGui library. Setup based off of @TheCherno's Hazel game engine.
EditorLayer essentially controls the state for UI related actions.
GWM -- Feb 2022
*/
#ifndef EDITOR_LAYER_H
#define EDITOR_LAYER_H
#include "Navigator/Core/Layer.h"
#include "Navigator/Events/Event.h"
#include "Navigator/Events/PhysicsEvent.h"
#include "Navigator/Core/Histogram.h"
#include "Navigator/Core/Cut.h"
#include "SpectrumPanel.h"
#include "FileDialog.h"
#include "SpectrumDialog.h"
#include "SourceDialog.h"
namespace Navigator {
class EditorLayer : public Layer
{
public:
using EventCallbackFunc = std::function<void(Event&)>;
EditorLayer();
~EditorLayer();
void SetEventCallbackFunc(const EventCallbackFunc& f) { m_callbackFunc = f; }
virtual void OnAttach() override;
virtual void OnDetach() override;
virtual void OnImGuiRender() override;
virtual void OnUpdate(Timestep& step) override;
virtual void OnEvent(Event& event) override;
private:
void RemoveCutDialog();
void RemoveHistogramDialog();
void ExportHistogramDialog();
void UpdateHistogramList();
void UpdateCutList();
void UpdateParameterList(); //Currently not really used, only once. Params all made at construction time of PhysicsLayer
void ExportHistogram(HistogramArgs selectedGram, const std::string& filename);
EventCallbackFunc m_callbackFunc;
SpectrumPanel m_spectrumPanel;
FileDialog m_fileDialog;
SpectrumDialog m_spectrumDialog;
SourceDialog m_sourceDialog;
std::vector<HistogramArgs> m_histoList;
std::vector<CutArgs> m_cutList;
std::vector<std::string> m_paramList;
//ImGui Settings
bool dockspaceOpen = true;
bool opt_fullscreen = true;
bool opt_fullscreen_persistant = true;
ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;
ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
bool m_removeHistogram;
bool m_removeCut;
bool m_exportHistogram;
};
template<typename T>
bool SortByName(const T& p1, const T& p2)
{
return p1.name < p2.name;
}
}
#endif

View File

@ -1,272 +0,0 @@
/*
FileDialog.cpp
File dialog window in ImGui using std::filesystem. This is slightly complicated, as file dialogs change function
based on the type of action one wants to perform. In our case we have OpenFile, SaveFile, and OpenDirectory. One can also
specify the kind of file (extension). Use FontAwesome icons.
Use style:
if(ImGui::Button())
Set...FileDialog(true);
std::string value = ImGuiRender...(extension);
GWM -- Feb 2022
*/
#include "FileDialog.h"
#include "misc/cpp/imgui_stdlib.h"
#include "IconsFontAwesome5.h"
namespace Navigator {
//Helper function to handle file size printing
std::string ConvertFileSystemSizeToString(std::uintmax_t value)
{
NAV_PROFILE_FUNCTION();
int i = 0;
double mantissa = (double)value;
for (; mantissa >= 1024.0; ++i)
mantissa /= 1024.0;
mantissa = std::ceil(mantissa * 10.0) / 10.0;
return std::to_string(int(mantissa)) + "BKMGTPE"[i];
}
FileDialog::FileDialog() :
m_currentPath(std::filesystem::current_path()), m_type(Type::None), m_selectedItem(""), m_openDialogFlag(false)
{
table_flags = ImGuiTableFlags_BordersH | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_RowBg;
select_flags = ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_DontClosePopups;
}
FileDialog::~FileDialog() {}
//Each type of action has its own render function
std::pair<std::string, FileDialog::Type> FileDialog::RenderFileDialog(const std::string& ext)
{
NAV_PROFILE_FUNCTION();
if (m_openDialogFlag)
{
m_selectedItem = "";
m_openDialogFlag = false;
m_currentPath = std::filesystem::current_path();
ImGui::OpenPopup("File Dialog");
}
std::string result = "";
if (ImGui::BeginPopupModal("File Dialog"))
{
switch (m_type)
{
case Type::OpenFile:
{
result = ImGuiRenderOpenFile(ext);
break;
}
case Type::SaveFile:
{
result = ImGuiRenderSaveFile(ext);
break;
}
case Type::OpenDir:
{
result = ImGuiRenderOpenDir();
break;
}
case Type::None: break;
}
ImGui::EndPopup();
}
return std::make_pair(result, m_type);
}
std::string FileDialog::ImGuiRenderOpenFile(const std::string& ext)
{
NAV_PROFILE_FUNCTION();
std::string result = "";
std::string text = "";
ImGui::Text("%s", ("Current Directory: " + m_currentPath.lexically_normal().string()).c_str());
ImGui::SameLine();
ImGui::Text("%s", ("Extension Filter: "+ext).c_str());
ImGui::InputText("Selected", &m_selectedItem);
if (ImGui::Button("Ok"))
{
std::filesystem::path filepath = m_currentPath / m_selectedItem;
result = filepath.string();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
ImGui::CloseCurrentPopup();
if (ImGui::BeginTable("File System", 2, table_flags, ImVec2(-1, -1)))
{
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Size");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(ICON_FA_FOLDER " ..", false, select_flags))
{
m_selectedItem.clear();
m_currentPath.append("..");
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
for (auto& entry : std::filesystem::directory_iterator(m_currentPath))
{
if (entry.is_directory())
{
ImGui::TableNextRow();
text = ICON_FA_FOLDER " " + std::filesystem::relative(entry.path(), m_currentPath).string();
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
{
m_selectedItem.clear();
m_currentPath /= entry.path();
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
}
else if (entry.path().filename().extension() == ext)
{
ImGui::TableNextRow();
text = ICON_FA_FILE " " + entry.path().filename().string();
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
m_selectedItem = entry.path().filename().string();
ImGui::TableNextColumn();
ImGui::Text("%s", ConvertFileSystemSizeToString(entry.file_size()).c_str());
}
}
ImGui::EndTable();
}
return result;
}
std::string FileDialog::ImGuiRenderSaveFile(const std::string& ext)
{
NAV_PROFILE_FUNCTION();
std::string result = "";
std::string text = "";
ImGui::Text("%s", ("Current Directory: "+m_currentPath.lexically_normal().string()).c_str());
ImGui::SameLine();
ImGui::Text("%s", ("Extension Filter: "+ext).c_str());
ImGui::InputText("Selected", &m_selectedItem);
if (ImGui::Button("Ok"))
{
std::filesystem::path filepath = m_currentPath / m_selectedItem;
result = filepath.string();
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
ImGui::CloseCurrentPopup();
if (ImGui::BeginTable("File System", 2, table_flags, ImVec2(-1, -1)))
{
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Size");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(ICON_FA_FOLDER " ..", false, select_flags))
{
m_selectedItem.clear();
m_currentPath.append("..");
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
for (auto& entry : std::filesystem::directory_iterator(m_currentPath))
{
if (entry.is_directory())
{
ImGui::TableNextRow();
text = ICON_FA_FOLDER " " + std::filesystem::relative(entry.path(), m_currentPath).string();
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
{
m_selectedItem.clear();
m_currentPath /= entry.path();
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
}
else if (entry.path().filename().extension() == ext)
{
ImGui::TableNextRow();
text = ICON_FA_FILE " " + entry.path().filename().string();
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
m_selectedItem = entry.path().filename().string();
ImGui::TableNextColumn();
ImGui::Text("%s", ConvertFileSystemSizeToString(entry.file_size()).c_str());
}
}
ImGui::EndTable();
}
return result;
}
std::string FileDialog::ImGuiRenderOpenDir()
{
NAV_PROFILE_FUNCTION();
std::string result = "";
std::string text = "";
ImGui::Text("%s", ("Current Directory: "+m_currentPath.lexically_normal().string()).c_str());
ImGui::InputText("Selected", &m_selectedItem);
if (ImGui::Button("Ok"))
{
result = m_selectedItem;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
ImGui::CloseCurrentPopup();
if (ImGui::BeginTable("File System", 2, table_flags, ImVec2(-1, -1)))
{
ImGui::TableSetupColumn("Name");
ImGui::TableSetupColumn("Size");
ImGui::TableHeadersRow();
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(ICON_FA_FOLDER " ..", false, select_flags))
{
m_currentPath.append("..");
m_selectedItem = m_currentPath.string();
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
for (auto& entry : std::filesystem::directory_iterator(m_currentPath))
{
ImGui::TableNextRow();
if (entry.is_directory())
{
text = ICON_FA_FOLDER " " + std::filesystem::relative(entry.path(), m_currentPath).string();
ImGui::TableNextColumn();
if (ImGui::Selectable(text.c_str(), false, select_flags))
{
m_currentPath /= entry.path();
m_selectedItem = m_currentPath.string();
}
ImGui::TableNextColumn();
ImGui::Text("N/A");
}
else
{
text = ICON_FA_FILE " " + entry.path().filename().string();
ImGui::TableNextColumn();
ImGui::Text("%s", text.c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", ConvertFileSystemSizeToString(entry.file_size()).c_str());
}
}
ImGui::EndTable();
}
return result;
}
}

View File

@ -1,60 +0,0 @@
/*
FileDialog.h
File dialog window in ImGui using std::filesystem. This is slightly complicated, as file dialogs change function
based on the type of action one wants to perform. In our case we have OpenFile, SaveFile, and OpenDirectory. One can also
specify the kind of file (extension). Use FontAwesome icons.
Use style:
if(ImGui::Button())
Set...FileDialog(true);
std::string value = ImGuiRender...(extension);
GWM -- Feb 2022
*/
#ifndef FILE_DIALOG_H
#define FILE_DIALOG_H
#include <filesystem>
#include "imgui.h"
namespace Navigator {
class NAV_API FileDialog
{
public:
enum class Type
{
OpenFile,
SaveFile,
OpenDir,
None
};
FileDialog();
~FileDialog();
inline void OpenDialog(Type type) { m_type = type; m_openDialogFlag = true; }
std::pair<std::string, Type> RenderFileDialog(const std::string& ext = "");
private:
std::string ImGuiRenderOpenFile(const std::string& ext);
std::string ImGuiRenderSaveFile(const std::string& ext);
std::string ImGuiRenderOpenDir();
std::filesystem::path m_currentPath;
Type m_type;
std::string m_selectedItem;
bool m_openDialogFlag;
ImGuiTableFlags table_flags;
ImGuiSelectableFlags select_flags;
};
}
#endif

View File

@ -1,121 +0,0 @@
/*
SourceDialog.cpp
Handles selection of data source type and location specification. Needs to be updated when new source
types are added to Navigator.
GWM -- Feb 2022
*/
#include "SourceDialog.h"
#include "Navigator/Events/PhysicsEvent.h"
#include "Navigator/Events/Event.h"
#include "Navigator/Core/Application.h"
#include "Navigator/Physics/Caen/CompassHit.h"
#include "imgui.h"
#include "misc/cpp/imgui_stdlib.h"
#include "IconsFontAwesome5.h"
namespace Navigator {
SourceDialog::SourceDialog() :
m_openFlag(false), m_chosenPort("51489"), m_chosenWindow(2000000)
{
}
SourceDialog::~SourceDialog()
{
}
void SourceDialog::ImGuiRenderSourceDialog()
{
NAV_PROFILE_FUNCTION();
static bool onlineFlag = false;
static bool offlineFlag = false;
static std::vector<DataSource::SourceType> availTypes = { DataSource::SourceType::CompassOnline, DataSource::SourceType::CompassOffline };
if (m_openFlag)
{
onlineFlag = false;
offlineFlag = false;
m_openFlag = false;
m_chosenType = DataSource::SourceType::None;
m_chosenLocation = "";
m_chosenPort = "51489";
m_chosenWindow = 3000000;
m_bitflags = 0;
m_channels_per_board = 16;
ImGui::OpenPopup(ICON_FA_LINK " Attach Source");
}
if (ImGui::BeginPopupModal(ICON_FA_LINK " Attach Source"))
{
if (ImGui::BeginCombo("Source Type", ConvertDataSourceTypeToString(m_chosenType).c_str()))
{
for (auto& type : availTypes)
{
if (ImGui::Selectable(ConvertDataSourceTypeToString(type).c_str(), type == m_chosenType, ImGuiSelectableFlags_DontClosePopups))
{
m_chosenType = type;
}
}
ImGui::EndCombo();
}
if (m_chosenType == DataSource::SourceType::CompassOnline)
{
ImGui::InputText("Hostname", &m_chosenLocation);
ImGui::InputText("Port", &m_chosenPort);
if (ImGui::RadioButton("Energy", (m_bitflags & CompassHeaders::Energy) != 0))
{
m_bitflags = m_bitflags ^ CompassHeaders::Energy;
}
ImGui::SameLine();
if (ImGui::RadioButton("Energy Short", (m_bitflags & CompassHeaders::EnergyShort) != 0))
{
m_bitflags = m_bitflags ^ CompassHeaders::EnergyShort;
}
ImGui::SameLine();
if (ImGui::RadioButton("Energy Calibrated", (m_bitflags & CompassHeaders::EnergyCalibrated) != 0))
{
m_bitflags = m_bitflags ^ CompassHeaders::EnergyCalibrated;
}
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
}
else if (m_chosenType == DataSource::SourceType::CompassOffline)
{
ImGui::InputText("Run Directory", &m_chosenLocation);
if (ImGui::Button("Choose Location"))
{
m_fileDialog.OpenDialog(FileDialog::Type::OpenDir);
}
auto temp = m_fileDialog.RenderFileDialog();
if (!temp.first.empty() && temp.second == FileDialog::Type::OpenDir)
m_chosenLocation = temp.first;
ImGui::InputInt("Channels Per Digitizer Board", &m_channels_per_board);
}
ImGui::InputInt("Coinc. Window (ps)", &m_chosenWindow);
if (ImGui::Button("Ok"))
{
if (m_chosenType == DataSource::SourceType::CompassOffline)
{
PhysicsStartEvent event(m_chosenLocation, m_chosenType, m_chosenWindow, m_chosenPort, false, 0U, m_channels_per_board);
Application::Get().OnEvent(event);
}
else if (m_chosenType == DataSource::SourceType::CompassOnline)
{
PhysicsStartEvent event(m_chosenLocation, m_chosenType, m_chosenWindow, m_chosenPort, true, m_bitflags, m_channels_per_board);
Application::Get().OnEvent(event);
}
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
}

View File

@ -1,38 +0,0 @@
/*
SourceDialog.h
Handles selection of data source type and location specification. Needs to be updated when new source
types are added to Navigator.
GWM -- Feb 2022
*/
#ifndef SOURCE_DIALOG_H
#define SOURCE_DIALOG_H
#include "FileDialog.h"
#include "Navigator/Physics/DataSource.h"
namespace Navigator {
class NAV_API SourceDialog
{
public:
SourceDialog();
~SourceDialog();
void ImGuiRenderSourceDialog();
inline void OpenSourceDialog() { m_openFlag = true; }
private:
bool m_openFlag;
DataSource::SourceType m_chosenType;
std::string m_chosenLocation;
std::string m_chosenPort;
FileDialog m_fileDialog;
uint16_t m_bitflags;
int m_chosenWindow;
int m_channels_per_board;
};
}
#endif

View File

@ -1,280 +0,0 @@
/*
SpectrumDialog.h
Handles creation of new spectra. Pretty much that simple.
GWM -- Feb 2022
*/
#include "SpectrumDialog.h"
#include "Navigator/Core/SpectrumManager.h"
#include "misc/cpp/imgui_stdlib.h"
#include "IconsFontAwesome5.h"
namespace Navigator {
SpectrumDialog::SpectrumDialog() :
m_openFlag(false), m_openCutFlag(false)
{
selectFlags = ImGuiSelectableFlags_DontClosePopups;
tableFlags = ImGuiTableFlags_BordersH | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_RowBg;
}
SpectrumDialog::~SpectrumDialog()
{
}
bool SpectrumDialog::ImGuiRenderSpectrumDialog(const std::vector<HistogramArgs>& histoList, const std::vector<CutArgs>& cutList, const std::vector<std::string>& paramList)
{
NAV_PROFILE_FUNCTION();
static std::string selectedCut = "";
bool result = false;
if (m_openFlag)
{
m_openFlag = false;
m_newParams = m_blank;
m_subhistos.clear();
ImGui::OpenPopup(ICON_FA_CHART_BAR " New Spectrum Dialog");
}
if (ImGui::BeginPopupModal(ICON_FA_CHART_BAR " New Spectrum Dialog"))
{
ImGui::InputText("Spectrum Name", &m_newParams.name);
if (ImGui::BeginCombo("Spectrum Type", ConvertSpectrumTypeToString(m_newParams.type).c_str()))
{
if (ImGui::Selectable("Histogram1D", m_newParams.type == SpectrumType::Histo1D, selectFlags))
m_newParams.type = SpectrumType::Histo1D;
else if (ImGui::Selectable("Histogram2D", m_newParams.type == SpectrumType::Histo2D, selectFlags))
m_newParams.type = SpectrumType::Histo2D;
else if (ImGui::Selectable("Summary", m_newParams.type == SpectrumType::Summary, selectFlags))
m_newParams.type = SpectrumType::Summary;
ImGui::EndCombo();
}
switch (m_newParams.type)
{
case SpectrumType::Histo1D: RenderDialog1D(paramList); break;
case SpectrumType::Histo2D: RenderDialog2D(paramList); break;
case SpectrumType::Summary: RenderDialogSummary(paramList); break;
case SpectrumType::None: break;
}
if (ImGui::TreeNode("Applied Cuts"))
{
for (auto& name : m_newParams.cutsAppliedTo)
{
ImGui::BulletText("%s", name.c_str());
}
ImGui::TreePop();
}
if (ImGui::Button("Apply Cuts"))
{
m_openCutFlag = true;
}
RenderCutDialog(cutList);
if (ImGui::Button("Ok"))
{
switch (m_newParams.type)
{
case SpectrumType::Histo1D: SpectrumManager::GetInstance().AddHistogram(m_newParams); break;
case SpectrumType::Histo2D: SpectrumManager::GetInstance().AddHistogram(m_newParams); break;
case SpectrumType::Summary: SpectrumManager::GetInstance().AddHistogramSummary(m_newParams, m_subhistos); break;
case SpectrumType::None: break;
}
ImGui::CloseCurrentPopup();
result = true;
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
ImGui::CloseCurrentPopup();
result = false;
}
ImGui::EndPopup();
}
return result;
}
void SpectrumDialog::RenderDialog1D(const std::vector<std::string>& paramList)
{
if (ImGui::BeginTable("SpecParamsTable", 4))
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginCombo("X Param.", m_newParams.x_par.c_str()))
{
for (auto& params : paramList)
{
if (ImGui::Selectable(params.c_str(), params == m_newParams.x_par, selectFlags))
m_newParams.x_par = params;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
ImGui::InputInt("X Bins", &m_newParams.nbins_x);
ImGui::TableNextColumn();
ImGui::InputDouble("Min X", &m_newParams.min_x);
ImGui::TableNextColumn();
ImGui::InputDouble("Max X", &m_newParams.max_x);
ImGui::EndTable();
}
}
void SpectrumDialog::RenderDialog2D(const std::vector<std::string>& paramList)
{
if (ImGui::BeginTable("SpecParamsTable", 4))
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginCombo("X Param.", m_newParams.x_par.c_str()))
{
for (auto& params : paramList)
{
if (ImGui::Selectable(params.c_str(), params == m_newParams.x_par, selectFlags))
m_newParams.x_par = params;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
ImGui::InputInt("X Bins", &m_newParams.nbins_x);
ImGui::TableNextColumn();
ImGui::InputDouble("Min X", &m_newParams.min_x);
ImGui::TableNextColumn();
ImGui::InputDouble("Max X", &m_newParams.max_x);
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::BeginCombo("Y Param.", m_newParams.y_par.c_str()))
{
for (auto& params : paramList)
{
if (ImGui::Selectable(params.c_str(), params == m_newParams.y_par, selectFlags))
m_newParams.y_par = params;
}
ImGui::EndCombo();
}
ImGui::TableNextColumn();
ImGui::InputInt("Y Bins", &m_newParams.nbins_y);
ImGui::TableNextColumn();
ImGui::InputDouble("Min Y", &m_newParams.min_y);
ImGui::TableNextColumn();
ImGui::InputDouble("Max Y", &m_newParams.max_y);
ImGui::EndTable();
}
}
void SpectrumDialog::RenderDialogSummary(const std::vector<std::string>& paramList)
{
if (ImGui::BeginTable("SpecParamsTable", 3))
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::InputInt("X Bins", &m_newParams.nbins_x);
ImGui::TableNextColumn();
ImGui::InputDouble("Min X", &m_newParams.min_x);
ImGui::TableNextColumn();
ImGui::InputDouble("Max X", &m_newParams.max_x);
ImGui::EndTable();
}
if (ImGui::TreeNode("Selected Parameters"))
{
for (auto& name : m_subhistos)
{
ImGui::BulletText("%s", name.c_str());
}
ImGui::TreePop();
}
if (ImGui::Button("Add Parameter"))
{
m_subhistos.clear();
ImGui::OpenPopup("Param List");
}
if (ImGui::BeginPopupModal("Param List"))
{
if (ImGui::Button("Ok"))
{
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
m_subhistos.clear();
ImGui::CloseCurrentPopup();
}
auto check_func = [this](const std::string& name)
{
for (auto& par : m_subhistos)
{
if (name == par)
return true;
}
return false;
};
if (ImGui::BeginTable("Parameters", 1, tableFlags, ImVec2(-1, -1)))
{
for (auto& param : paramList)
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
if (ImGui::Selectable(param.c_str(), check_func(param), selectFlags))
{
if (!check_func(param))
{
m_subhistos.push_back(param);
}
else
{
auto iter = std::remove(m_subhistos.begin(), m_subhistos.end(), param);
m_subhistos.erase(iter, m_subhistos.end());
}
}
}
ImGui::EndTable();
}
ImGui::EndPopup();
}
}
void SpectrumDialog::RenderCutDialog(const std::vector<CutArgs>& cutList)
{
static std::string selectedCut = "";
if (m_openCutFlag)
{
selectedCut = "";
m_openCutFlag = false;
ImGui::OpenPopup("Cut List");
}
if (ImGui::BeginPopup("Cut List"))
{
for (auto& cut : cutList)
{
if (ImGui::Selectable(cut.name.c_str(), cut.name == selectedCut, selectFlags))
selectedCut = cut.name;
}
ImGui::InputText("Selected Cut", &selectedCut);
if (ImGui::Button("Ok"))
{
m_newParams.cutsAppliedTo.push_back(selectedCut);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
}

View File

@ -1,45 +0,0 @@
/*
SpectrumDialog.h
Handles creation of new spectra. Pretty much that simple.
GWM -- Feb 2022
*/
#ifndef SPECTRUM_DIALOG_H
#define SPECTRUM_DIALOG_H
#include "Navigator/Core/Histogram.h"
#include "Navigator/Core/Cut.h"
#include "imgui.h"
namespace Navigator {
class NAV_API SpectrumDialog
{
public:
SpectrumDialog();
~SpectrumDialog();
bool ImGuiRenderSpectrumDialog(const std::vector<HistogramArgs>& histoList, const std::vector<CutArgs>& cutList, const std::vector<std::string>& paramList);
inline void SetSpectrumDialog() { m_openFlag = true; }
private:
void RenderDialog1D(const std::vector<std::string>& paramList);
void RenderDialog2D(const std::vector<std::string>& paramList);
void RenderDialogSummary(const std::vector<std::string>& paramList);
void RenderCutDialog(const std::vector<CutArgs>& cutList);
bool m_openFlag;
bool m_openCutFlag;
HistogramArgs m_newParams;
HistogramArgs m_blank;
std::vector<std::string> m_subhistos;
ImGuiSelectableFlags selectFlags;
ImGuiTableFlags tableFlags;
};
}
#endif

View File

@ -1,393 +0,0 @@
/*
SpectrumPanel.cpp
This is the big boi. Renders a panel holding all of the drawn plots. Good news is that in general only a few things really require
any modification if new types of plots are to be rendered, basically just the zoomed in spectrum rendering.
GWM -- Feb 2022
*/
#include "SpectrumPanel.h"
#include "Navigator/Core/SpectrumManager.h"
#include "misc/cpp/imgui_stdlib.h"
#include "IconsFontAwesome5.h"
namespace Navigator {
//Convert a StatResults struct from analysis to a std::string helper function
std::string GenerateStatString(const std::string& name, const StatResults& results, bool is2D = true)
{
NAV_PROFILE_FUNCTION();
std::stringstream stream;
stream << "Region: " << name << "\n" << "Integral: " << results.integral << "\n";
if (results.integral == 0.0)
return stream.str();
stream << "Centroid X: " << results.cent_x << "\nStd. Dev. X: " << results.sigma_x << "\nFWHM X: " << 2.355 * results.sigma_x << "\n";
if(is2D)
stream << "Centroid Y: " << results.cent_y << "\nStd. Dev. Y: " << results.sigma_y << "\nFWHM Y: " << 2.355 * results.sigma_y << "\n";
return stream.str();
}
SpectrumPanel::SpectrumPanel() :
m_zoomedFlag(false), m_cutModeFlag(false), m_acceptCutFlag(false), m_zoomedGram(), m_totalSlots(1), m_nRegions(0)
{
m_tableSizes[0] = 1; m_tableSizes[1] = 1;
}
SpectrumPanel::~SpectrumPanel() {}
//Main render function. Handles generating subplot regions as well as the zoomed in region
bool SpectrumPanel::OnImGuiRender(const std::vector<HistogramArgs>& histoList, const std::vector<CutArgs>& cutList, const std::vector<std::string>& paramList)
{
NAV_PROFILE_FUNCTION();
static bool acceptCutFlag = false;
m_result = false;
if (ImGui::Begin("Active View"))
{
if (histoList.size() > 0)
{
if (m_zoomedFlag && m_zoomedGram.type != SpectrumType::None)
{
RenderCutButton();
ImGui::SameLine();
if(ImGui::Button("Clear"))
{
SpectrumManager::GetInstance().ClearHistogram(m_zoomedGram.name);
}
ImGui::SameLine();
RenderRemoveRegionButton();
if (m_zoomedGram.type == SpectrumType::Histo2D || m_zoomedGram.type == SpectrumType::Summary)
{
float* scale = SpectrumManager::GetInstance().GetColorScaleRange(m_zoomedGram.name);
ImGui::DragFloatRange2("Min / Max", &(scale[0]), &(scale[1]), 0.01f);
ImPlot::ColormapScale("##HistogramScale", scale[0], scale[1], ImVec2(0, -1), ImPlotColormap_Viridis);
ImGui::SameLine();
}
if (ImPlot::BeginPlot(m_zoomedGram.name.c_str(), ImVec2(-1, -1)))
{
SpectrumManager::GetInstance().DrawHistogram(m_zoomedGram.name);
if (!m_cutModeFlag && ImPlot::IsPlotHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
m_zoomedFlag = false;
m_zoomedGram = HistogramArgs();
}
else if (m_cutModeFlag)
{
HandleCutMode();
}
if (ImPlot::IsPlotSelected()) {
auto select = ImPlot::GetPlotSelection();
if (ImGui::IsMouseClicked(ImPlot::GetInputMap().SelectCancel)) {
ImPlot::CancelPlotSelection();
m_integralRegions.emplace_back(select, "integralRegion_"+std::to_string(m_nRegions), m_zoomedGram.name);
m_nRegions++;
}
}
for (size_t i = 0; i < m_integralRegions.size(); i++)
{
auto& region = m_integralRegions[i];
if (m_zoomedGram.name == region.histogram_name)
{
ImPlot::DragRect(int(i), &region.region.X.Min, &region.region.Y.Min, &region.region.X.Max, &region.region.Y.Max, ImVec4(1, 0, 1, 1), ImPlotDragToolFlags_NoFit);
StatResults results = SpectrumManager::GetInstance().AnalyzeHistogramRegion(m_zoomedGram.name, region.region);
ImPlot::PlotText(GenerateStatString(region.name, results, m_zoomedGram.y_par != "None").c_str(), (region.region.X.Max + region.region.X.Min) * 0.5,
(region.region.Y.Min + region.region.Y.Max) * 0.5);
}
}
ImPlot::EndPlot();
}
RenderAcceptCutDialog();
}
else
{
ImGui::SliderInt2("Rows, Columns", m_tableSizes, 1, 3);
ImGui::SameLine();
if (ImGui::Button("Clear All"))
{
SpectrumManager::GetInstance().ClearHistograms();
}
m_totalSlots = m_tableSizes[0] * m_tableSizes[1];
m_selectedGrams.resize(m_totalSlots);
if (ImGui::BeginTable("Select Histograms", m_tableSizes[1]))
{
std::string label;
int this_gram;
for (int i = 0; i < m_tableSizes[0]; i++)
{
ImGui::TableNextRow();
for (int j = 0; j < m_tableSizes[1]; j++)
{
ImGui::TableNextColumn();
this_gram = i * m_tableSizes[1] + j;
label = "Histogram" + std::to_string(this_gram);
if (ImGui::BeginCombo(label.c_str(), m_selectedGrams[this_gram].name.c_str()))
{
for (auto& params : histoList)
{
if (ImGui::Selectable(params.name.c_str(), params.name == m_selectedGrams[this_gram].name))
m_selectedGrams[this_gram] = params;
}
ImGui::EndCombo();
}
}
}
ImGui::EndTable();
}
if (ImPlot::BeginSubplots("Histograms", m_tableSizes[0], m_tableSizes[1], ImVec2(-1, -1)))
{
int i = 0;
for (auto& spec : m_selectedGrams)
{
if (ImPlot::BeginPlot(spec.name.c_str()))
{
SpectrumManager::GetInstance().DrawHistogram(spec.name);
if (ImPlot::IsPlotHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
m_zoomedFlag = true;
m_zoomedGram = spec;
}
for (size_t i = 0; i < m_integralRegions.size(); i++)
{
auto& region = m_integralRegions[i];
if (spec.name == region.histogram_name)
{
ImPlot::DragRect(int(i), &region.region.X.Min, &region.region.Y.Min, &region.region.X.Max, &region.region.Y.Max, ImVec4(1, 0, 1, 1));
}
}
ImPlot::EndPlot();
}
i++;
}
ImPlot::EndSubplots();
}
}
}
}
ImGui::End();
return m_result;
}
void SpectrumPanel::HandleCutMode()
{
switch (m_zoomedGram.type)
{
case SpectrumType::Histo1D:
{
if (m_newCutX.size() == 2)
{
m_acceptCutFlag = true;
}
else if (ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
{
m_newCutX.push_back(ImPlot::GetPlotMousePos().x);
}
ImPlot::PlotVLines(m_newCutArgs.name.c_str(), m_newCutX.data(), int(m_newCutX.size()));
break;
}
case SpectrumType::Histo2D:
{
if (m_newCutX.size() >= 2 && ImPlot::IsPlotHovered() && ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
m_acceptCutFlag = true;
m_newCutX.push_back(m_newCutX[0]);
m_newCutY.push_back(m_newCutY[0]);
}
else if (ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
{
auto point = ImPlot::GetPlotMousePos();
m_newCutX.push_back(point.x);
m_newCutY.push_back(point.y);
}
ImPlot::PlotLine(m_newCutArgs.name.c_str(), m_newCutX.data(), m_newCutY.data(), int(m_newCutX.size()));
break;
}
case SpectrumType::Summary:
{
if (m_newCutX.size() == 2)
{
m_acceptCutFlag = true;
}
else if (ImPlot::IsPlotHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
{
m_newCutX.push_back(ImPlot::GetPlotMousePos().x);
}
ImPlot::PlotVLines(m_newCutArgs.name.c_str(), m_newCutX.data(), int(m_newCutX.size()));
break;
}
case SpectrumType::None:
{
m_cutModeFlag = false;
break;
}
}
}
void SpectrumPanel::RenderAcceptCutDialog()
{
if (m_acceptCutFlag)
{
m_acceptCutFlag = false;
m_cutModeFlag = false;
ImGui::OpenPopup("Accept Cut");
}
if (ImGui::BeginPopupModal("Accept Cut"))
{
ImGui::Text("Save this Cut?");
if (ImGui::Button("Yes"))
{
SpectrumManager& manager = SpectrumManager::GetInstance();
switch (m_newCutArgs.type)
{
case CutType::Cut1D:
{
std::sort(m_newCutX.begin(), m_newCutX.end());
manager.AddCut(m_newCutArgs, m_newCutX[0], m_newCutX[1]);
manager.AddCutToHistogramDraw(m_newCutArgs.name, m_zoomedGram.name);
break;
}
case CutType::Cut2D:
{
manager.AddCut(m_newCutArgs, m_newCutX, m_newCutY);
manager.AddCutToHistogramDraw(m_newCutArgs.name, m_zoomedGram.name);
break;
}
case CutType::CutSummaryAny:
{
std::sort(m_newCutX.begin(), m_newCutX.end());
std::vector<std::string> subhistos = manager.GetSubHistograms(m_zoomedGram.name);
manager.AddCut(m_newCutArgs, subhistos, m_newCutX[0], m_newCutX[1]);
manager.AddCutToHistogramDraw(m_newCutArgs.name, m_zoomedGram.name);
break;
}
case CutType::CutSummaryAll:
{
std::sort(m_newCutX.begin(), m_newCutX.end());
std::vector<std::string> subhistos = manager.GetSubHistograms(m_zoomedGram.name);
manager.AddCut(m_newCutArgs, subhistos, m_newCutX[0], m_newCutX[1]);
manager.AddCutToHistogramDraw(m_newCutArgs.name, m_zoomedGram.name);
break;
}
case CutType::None:
{
NAV_ERROR("Trying to add None type cut to manager at SpectrumPanel::RenderAcceptCutDialog!");
break;
}
}
ImGui::CloseCurrentPopup();
m_result = true;
}
ImGui::SameLine();
if (ImGui::Button("No"))
{
ImGui::CloseCurrentPopup();
m_result = false;
}
ImGui::EndPopup();
}
}
//Renders Cut button as well as dialog for creating cuts.
void SpectrumPanel::RenderCutButton()
{
if (ImGui::Button(ICON_FA_CUT " Draw Cut"))
{
m_newCutArgs = CutArgs();
m_newCutX.resize(0);
m_newCutY.resize(0);
ImGui::OpenPopup(ICON_FA_CUT " New Cut Dialog");
}
if (ImGui::BeginPopupModal(ICON_FA_CUT " New Cut Dialog"))
{
m_newCutArgs.x_par = m_zoomedGram.x_par;
m_newCutArgs.y_par = m_zoomedGram.y_par;
switch (m_zoomedGram.type)
{
case SpectrumType::Histo1D:
{
m_newCutArgs.type = CutType::Cut1D;
ImGui::BulletText("%s", ("X Parameter: " + m_newCutArgs.x_par).c_str());
break;
}
case SpectrumType::Histo2D:
{
m_newCutArgs.type = CutType::Cut2D;
ImGui::BulletText("%s", ("X Parameter: " + m_newCutArgs.x_par).c_str());
ImGui::BulletText("%s", ("Y Parameter: " + m_newCutArgs.y_par).c_str());
break;
}
case SpectrumType::Summary:
{
if (ImGui::RadioButton("CutSummaryAny", m_newCutArgs.type == CutType::CutSummaryAny))
m_newCutArgs.type = CutType::CutSummaryAny;
ImGui::SameLine();
if (ImGui::RadioButton("CutSummaryAll", m_newCutArgs.type == CutType::CutSummaryAll))
m_newCutArgs.type = CutType::CutSummaryAll;
break;
}
case SpectrumType::None: m_newCutArgs.type = CutType::None; break;
}
ImGui::InputText("Cut Name", &m_newCutArgs.name);
if (ImGui::Button("Accept & Draw"))
{
m_cutModeFlag = true;
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
//Simple button/dialog for removing integration regions
void SpectrumPanel::RenderRemoveRegionButton()
{
static std::string selectedRegion = "";
if (ImGui::Button("Delete Region"))
{
selectedRegion = "";
ImGui::OpenPopup("Remove Integral Region");
}
if (ImGui::BeginPopupModal("Remove Integral Region"))
{
if (ImGui::BeginCombo("Region", selectedRegion.c_str()))
{
for (auto& region : m_integralRegions)
{
if (region.histogram_name == m_zoomedGram.name)
{
if (ImGui::Selectable(region.name.c_str(), region.name == selectedRegion, ImGuiSelectableFlags_DontClosePopups))
selectedRegion = region.name;
}
}
ImGui::EndCombo();
}
if (ImGui::Button("Ok"))
{
RemoveSelectedRegion(selectedRegion);
ImGui::CloseCurrentPopup();
}
ImGui::SameLine();
if (ImGui::Button("Cancel"))
{
ImGui::CloseCurrentPopup();
}
ImGui::EndPopup();
}
}
void SpectrumPanel::RemoveSelectedRegion(const std::string& region)
{
for (size_t i=0; i<m_integralRegions.size(); i++)
{
if (m_integralRegions[i].name == region)
{
m_integralRegions.erase(m_integralRegions.begin() + i);
break;
}
}
}
}

View File

@ -1,63 +0,0 @@
/*
SpectrumPanel.h
This is the big boi. Renders a panel holding all of the drawn plots. Good news is that in general only a few things really require
any modification if new types of plots are to be rendered, basically just the zoomed in spectrum rendering.
GWM -- Feb 2022
*/
#ifndef SPECTRUM_PANEL_H
#define SPECTRUM_PANEL_H
#include "Navigator/Core/Histogram.h"
#include "Navigator/Core/Cut.h"
#include "imgui.h"
#include "implot.h"
namespace Navigator {
//Simple struct for holding a region of interest
struct NAV_API IntegrationRegion
{
IntegrationRegion(const ImPlotRect& rect, const std::string& n, const std::string& hist_n) :
region(rect), name(n), histogram_name(hist_n)
{
}
ImPlotRect region;
std::string name = "";
std::string histogram_name = "";
};
class NAV_API SpectrumPanel
{
public:
SpectrumPanel();
~SpectrumPanel();
bool OnImGuiRender(const std::vector<HistogramArgs>& histoList, const std::vector<CutArgs>& cutList, const std::vector<std::string>& paramList);
inline const std::string& GetZoomedOnHistogram() { return m_zoomedGram.name; }
inline const bool IsZoomed() { return m_zoomedFlag; }
private:
void HandleCutMode();
void RenderAcceptCutDialog();
void RenderCutButton();
void RenderRemoveRegionButton();
void RemoveSelectedRegion(const std::string& region);
std::vector<HistogramArgs> m_selectedGrams;
std::vector<IntegrationRegion> m_integralRegions;
bool m_zoomedFlag;
bool m_cutModeFlag;
bool m_acceptCutFlag;
bool m_result;
HistogramArgs m_zoomedGram;
int m_tableSizes[2];
int m_totalSlots;
int m_nRegions;
CutArgs m_newCutArgs;
std::vector<double> m_newCutX;
std::vector<double> m_newCutY;
};
}
#endif

View File

@ -1,59 +0,0 @@
/*
AppEvent.h
Events related to the main application (window events mostly). Again, based on @TheCherno's work.
GWM -- Feb 2022
*/
#ifndef APP_EVENT_H
#define APP_EVENT_H
#include "Event.h"
namespace Navigator {
// Window closing is pure event (no data)
class NAV_API WindowCloseEvent : public Event
{
public:
WindowCloseEvent() {};
EVENT_CATEGORY_SETUP(EventCategoryApp)
EVENT_TYPE_SETUP(WindowClose)
};
class NAV_API WindowResizeEvent : public Event
{
public:
WindowResizeEvent(int x, int y) :
m_xSize(x), m_ySize(y)
{
}
inline int GetXSize() { return m_xSize; }
inline int GetYSize() { return m_ySize; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " to size: (" << m_xSize << ", " << m_ySize << ")";
return ss.str();
}
EVENT_CATEGORY_SETUP(EventCategoryApp)
EVENT_TYPE_SETUP(WindowResize)
private:
int m_xSize, m_ySize;
};
class NAV_API AppUpdateEvent : public Event
{
public:
AppUpdateEvent() = default;
EVENT_CATEGORY_SETUP(EventCategoryApp)
EVENT_TYPE_SETUP(AppUpdate)
};
}
#endif

View File

@ -1,89 +0,0 @@
/*
Event.h
Event system for Navigator. Based entirely upon the work of @TheCherno in his game engine series, with additons specific to
Navigator. Abstract Event class and an EventDispatcher. EventDispatcher links a function to the event for handling. See Application::OnEvent for
an example of use.
GWM -- Feb 2022
*/
#ifndef EVENT_H
#define EVENT_H
#include "Navigator/Core/NavCore.h"
namespace Navigator {
enum class NAV_API EventType
{
None=0,
WindowClose, WindowResize, WindowFocus, WindowLostFocus, WindowMoved,
KeyPressed, KeyReleased, KeyTyped,
MouseButtonPressed, MouseButtonReleased, MouseScrolled, MouseMoved,
AppUpdate,
PhysicsStart, PhysicsStop, PhysicsParam
};
enum NAV_API EventCategory
{
EventCategoryNone=0,
EventCategoryApp=BIT(0),
EventCategoryInput=BIT(1),
EventCategoryKey=BIT(2),
EventCategoryMouse=BIT(3),
EventCategoryWindow=BIT(4),
EventCategoryPhysics=BIT(5),
};
//Some function generation automation to reduce code written for all events
#define EVENT_CATEGORY_SETUP(cat) virtual int GetCategoryFlags() const override { return cat; }
#define EVENT_TYPE_SETUP(type) static EventType GetStaticType() { return EventType::type; } \
virtual EventType GetEventType() const override { return GetStaticType(); } \
virtual const char* GetName() const override { return #type; }
class NAV_API Event
{
friend class EventDispatcher;
public:
virtual EventType GetEventType() const = 0;
virtual const char* GetName() const = 0;
virtual int GetCategoryFlags() const = 0;
virtual std::string ToString() const { return GetName(); }
inline bool IsCategory(EventCategory cat) const { return GetCategoryFlags() & cat; }
bool handledFlag = false;
};
class NAV_API EventDispatcher
{
public:
EventDispatcher(Event& e) :
m_event(e)
{
}
template<typename T, typename F>
bool Dispatch(const F& function)
{
if(m_event.GetEventType() == T::GetStaticType())
{
m_event.handledFlag = function(static_cast<T&>(m_event));
return true;
}
else
{
return false;
}
}
private:
Event& m_event;
};
//For easy printing
NAV_API inline std::ostream& operator<<(std::ostream& os, const Event& e)
{
return os << e.ToString();
}
}
#endif

View File

@ -1,95 +0,0 @@
/*
KeyEvent.h
Events related to key presses. Again, based on @TheCherno's work.
GWM -- Feb 2022
*/
#ifndef KEY_EVENT_H
#define KEY_EVENT_H
#include "Event.h"
namespace Navigator {
//Since so many key related events, have a base
class NAV_API KeyEvent : public Event
{
public:
inline int GetKeycode() const { return m_keycode; }
EVENT_CATEGORY_SETUP(EventCategoryKey | EventCategoryInput)
protected:
KeyEvent(int code) :
m_keycode(code)
{
}
int m_keycode;
};
class NAV_API KeyPressedEvent : public KeyEvent
{
public:
KeyPressedEvent(int code, int count) :
KeyEvent(code), m_repeatCount(count)
{
}
EVENT_TYPE_SETUP(KeyPressed)
inline int GetRepeatCount() const { return m_repeatCount; }
std::string ToString() const override
{
std::stringstream stream;
stream << GetName() << " with code "<<m_keycode << " pressed "<<m_repeatCount<<" times.";
return stream.str();
}
private:
int m_repeatCount;
};
class NAV_API KeyReleasedEvent : public KeyEvent
{
public:
KeyReleasedEvent(int code) :
KeyEvent(code)
{
};
EVENT_TYPE_SETUP(KeyReleased)
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " with code " << m_keycode;
return ss.str();
}
};
class NAV_API KeyTypedEvent : public KeyEvent
{
public:
KeyTypedEvent(int code) :
KeyEvent(code)
{
};
EVENT_TYPE_SETUP(KeyTyped)
unsigned int GetCharacter() { return (unsigned int)m_keycode; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " with code " << m_keycode;
return ss.str();
}
};
}
#endif

View File

@ -1,109 +0,0 @@
/*
KeyEvent.h
Events related to mouse. Again, based on @TheCherno's work.
GWM -- Feb 2022
*/
#ifndef MOUSE_EVENT_H
#define MOUSE_EVENT_H
#include "Event.h"
namespace Navigator {
class NAV_API MouseMovedEvent : public Event
{
public:
MouseMovedEvent(float x, float y) :
m_xPos(x), m_yPos(y)
{
}
inline float GetXPosition() { return m_xPos; }
inline float GetYPosition() { return m_yPos; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " to position (" << m_xPos << ", " << m_yPos << ")";
return ss.str();
}
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
EVENT_TYPE_SETUP(MouseMoved)
private:
float m_xPos, m_yPos;
};
class NAV_API MouseScrolledEvent : public Event
{
public:
MouseScrolledEvent(float x, float y) :
m_xOffset(x), m_yOffset(y)
{
}
inline float GetXOffset() { return m_xOffset; }
inline float GetYOffset() { return m_yOffset; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " to offset (" << m_xOffset << ", " << m_yOffset << ")";
return ss.str();
}
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
EVENT_TYPE_SETUP(MouseScrolled)
private:
float m_xOffset, m_yOffset;
};
class NAV_API MouseButtonPressedEvent : public Event
{
public:
MouseButtonPressedEvent(int code) :
m_buttonCode(code)
{
}
inline int GetButtonCode() { return m_buttonCode; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " with button code " << m_buttonCode;
return ss.str();
}
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
EVENT_TYPE_SETUP(MouseButtonPressed)
private:
int m_buttonCode;
};
class NAV_API MouseButtonReleasedEvent : public Event
{
public:
MouseButtonReleasedEvent(int code) :
m_buttonCode(code)
{
}
inline int GetButtonCode() { return m_buttonCode; }
std::string ToString() const override
{
std::stringstream ss;
ss << GetName() << " with button code " << m_buttonCode;
return ss.str();
}
EVENT_CATEGORY_SETUP(EventCategoryMouse | EventCategoryInput)
EVENT_TYPE_SETUP(MouseButtonReleased)
private:
int m_buttonCode;
};
}
#endif

View File

@ -1,85 +0,0 @@
/*
KeyEvent.h
Events related to physics processes. Again, based on @TheCherno's work.
GWM -- Feb 2022
Update to reflect new CAEN binary data format with headers to indicate data contents.
GWM -- May 2022
*/
#ifndef PHYSICS_EVENT_H
#define PHYSICS_EVENT_H
#include "Event.h"
#include "Navigator/Physics/DataSource.h"
namespace Navigator {
//When we start physics, need info for what kind of source we make
class NAV_API PhysicsStartEvent : public Event
{
public:
//Bitflags is a final option for random crap needed for a source. Currently used for compass online to indicate header state.
PhysicsStartEvent(const std::string& loc, DataSource::SourceType type, uint64_t window, const std::string& port = "51489", bool sortFlag=false, uint16_t bitflags = 0,
int channels_per_board=16) :
m_sourceLocation(loc), m_port(port), m_sourceType(type), m_coincidenceWindow(window), m_sortFlag(sortFlag), m_bitflags(bitflags), m_channels_per_board(channels_per_board)
{}
inline const std::string GetSourceLocation() const { return m_sourceLocation; }
inline const std::string GetSourcePort() const { return m_port; }
inline const DataSource::SourceType GetSourceType() const { return m_sourceType; }
inline const uint64_t GetCoincidenceWindow() const { return m_coincidenceWindow; }
inline const bool GetSortFlag() const { return m_sortFlag; }
inline const int GetChannelsPerBoard() const { return m_channels_per_board; }
inline const uint16_t GetBitFlags() const { return m_bitflags; }
std::string ToString() const override
{
return "Starting PhysicsEventBuilder with DataSource of type {0} at location {1}" + m_sourceLocation + ConvertDataSourceTypeToString(m_sourceType);
}
EVENT_CATEGORY_SETUP(EventCategoryPhysics);
EVENT_TYPE_SETUP(PhysicsStart);
private:
std::string m_sourceLocation;
std::string m_port;
DataSource::SourceType m_sourceType;
uint64_t m_coincidenceWindow;
bool m_sortFlag;
uint16_t m_bitflags;
int m_channels_per_board;
};
class NAV_API PhysicsStopEvent : public Event
{
public:
PhysicsStopEvent() {}
std::string ToString() const override
{
return "Stopping PhysicsEventBuilder";
}
EVENT_CATEGORY_SETUP(EventCategoryPhysics);
EVENT_TYPE_SETUP(PhysicsStop);
};
//Unused. Exists as a potential path of upgrade
class NAV_API PhysicsParamEvent : public Event
{
public:
PhysicsParamEvent() {}
std::string ToString() const override
{
return "Updating Parameter lists!";
}
EVENT_CATEGORY_SETUP(EventCategoryPhysics);
EVENT_TYPE_SETUP(PhysicsParam);
};
}
#endif

View File

@ -1,8 +0,0 @@
/*
ImGuiBuild.cpp
Here we link to the approprate ImGui backend implementaions.
If we want cases other than OpenGL, use switches on Metal,
DirectX, etc.
*/
#include "backends/imgui_impl_opengl3.cpp"
#include "backends/imgui_impl_glfw.cpp"

View File

@ -1,8 +0,0 @@
/*
ImGuiExtensions.cpp
Compile this to enable std library extensions for ImGui,
in particular using std::string with ImGui::InputText
GWM -- Feb 2022
*/
#include "misc/cpp/imgui_stdlib.cpp"

View File

@ -1,135 +0,0 @@
/*
ImGuiLayer.h
The layer containing all of the ImGui related setup and calls. Based on the work by @TheCherno in his game engine series.
Should always exist as an overlay in the Application LayerStack. Note that it currently is OpenGL specific based on
ImGui implementation/backends.
GWM -- Feb 2022
*/
#include "ImGuiLayer.h"
#include "Navigator/Core/Application.h"
#include "Navigator/Core/NavCore.h"
#include "imgui.h"
#include "implot.h"
#include "backends/imgui_impl_opengl3.h"
#include "backends/imgui_impl_glfw.h"
#include <GLFW/glfw3.h>
#include <glad/glad.h>
#include "IconsFontAwesome5.h"
namespace Navigator {
ImGuiLayer::ImGuiLayer() :
Layer("ImGuiLayer")
{
}
ImGuiLayer::~ImGuiLayer()
{
}
void ImGuiLayer::OnAttach()
{
NAV_PROFILE_FUNCTION();
IMGUI_CHECKVERSION();
NAV_INFO("Creating ImGui Context...");
ImGui::CreateContext();
ImPlot::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
//Viewports are real wonky on Linux and sometimes on MacOS
//Can currently cause assertion failure on checking number of monitors in ImGui sanity checks.
io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
ImGui::StyleColorsDark(); //Hacker mode
ImPlot::StyleColorsDark();
ImGuiStyle& style = ImGui::GetStyle();
if(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
style.WindowRounding = 0.0f;
style.Colors[ImGuiCol_WindowBg].w = 1.0f;
}
//Setup our fonts. We have Roboto for text and FontAwesome for our icons.
//Note the .ttf files are found in NavProject, or in the bin dir. This is because
//the actual program (NavProject) is launched from either the bin/ ... /NaProject or the NavProject directory
//io.Fonts->AddFontDefault();
ImFontConfig latin_config;
latin_config.RasterizerMultiply = 1.3f;
ImFontConfig config;
config.MergeMode = true;
//config.GlyphMinAdvanceX = 13.0f; // Use if you want to make the icon monospaced
config.PixelSnapH = true;
static const ImWchar icon_ranges[] = { ICON_MIN_FA, ICON_MAX_FA, 0 };
io.Fonts->AddFontFromFileTTF("Assets/fonts/Roboto-Regular.ttf", 16.0f, &latin_config, io.Fonts->GetGlyphRangesDefault());
io.Fonts->AddFontFromFileTTF("Assets/fonts/fa-solid-900.ttf", 16.0f, &config, icon_ranges);
//ImPlot styling
ImPlot::GetStyle().FillAlpha = 0.75;
Application& app = Application::Get();
GLFWwindow* window = static_cast<GLFWwindow*>(app.GetWindow().GetNativeWindow());
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init("#version 410"); //GLSL version
NAV_INFO("ImGui Finished initializing.");
}
void ImGuiLayer::OnDetach()
{
NAV_PROFILE_FUNCTION();
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImPlot::DestroyContext();
ImGui::DestroyContext();
}
void ImGuiLayer::Begin()
{
NAV_PROFILE_FUNCTION();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
}
void ImGuiLayer::End()
{
NAV_PROFILE_FUNCTION();
Application& app = Application::Get();
ImGuiIO& io = ImGui::GetIO();
io.DisplaySize = ImVec2((float)app.GetWindow().GetWidth(), (float)app.GetWindow().GetHeight());
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
if(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
{
GLFWwindow* backup_current_context = glfwGetCurrentContext();
ImGui::UpdatePlatformWindows();
ImGui::RenderPlatformWindowsDefault();
glfwMakeContextCurrent(backup_current_context);
}
}
void ImGuiLayer::OnImGuiRender()
{
//Demo's used to figure out how to do things.
//Should not be on for actual NavProject for
//real use
//static bool show = true;
//ImGui::ShowDemoWindow(&show);
//ImPlot::ShowDemoWindow();
}
}

View File

@ -1,36 +0,0 @@
/*
ImGuiLayer.h
The layer containing all of the ImGui related setup and calls. Based on the work by @TheCherno in his game engine series.
Should always exist as an overlay in the Application LayerStack. Note that it currently is OpenGL specific based on
ImGui implementation/backends.
GWM -- Feb 2022
*/
#ifndef IMGUI_LAYER_H
#define IMGUI_LAYER_H
#include "Navigator/Core/NavCore.h"
#include "Navigator/Core/Layer.h"
namespace Navigator {
class NAV_API ImGuiLayer : public Layer
{
public:
ImGuiLayer();
~ImGuiLayer();
virtual void OnAttach() override;
virtual void OnDetach() override;
virtual void OnImGuiRender() override;
void Begin();
void End();
private:
float m_time;
};
}
#endif

View File

@ -1,35 +0,0 @@
/*
AnalysisStack.cpp
Container for the analyses in the PhysicsLayer. Really just a specialized wrapper around std::vector.
GWM -- Feb 2022
*/
#include "AnalysisStack.h"
namespace Navigator {
AnalysisStack::AnalysisStack() {}
AnalysisStack::~AnalysisStack()
{
for(AnalysisStage* stage : m_stack)
delete stage;
}
void AnalysisStack::PushStage(AnalysisStage* stage)
{
m_stack.emplace(m_stack.begin()+m_insertIndex, stage);
m_insertIndex++;
}
void AnalysisStack::PopStage(AnalysisStage* stage)
{
auto iter = std::find(m_stack.begin(), m_stack.end(), stage);
if(iter != m_stack.end())
{
m_stack.erase(iter);
m_insertIndex--;
}
}
}

View File

@ -1,35 +0,0 @@
/*
AnalysisStack.h
Container for the analyses in the PhysicsLayer. Really just a specialized wrapper around std::vector.
GWM -- Feb 2022
*/
#ifndef ANALYSIS_STACK_H
#define ANALYSIS_STACK_H
#include "AnalysisStage.h"
#include "Navigator/Core/NavCore.h"
namespace Navigator {
class NAV_API AnalysisStack
{
public:
AnalysisStack();
~AnalysisStack();
void PushStage(AnalysisStage* stage);
void PopStage(AnalysisStage* stage);
std::vector<AnalysisStage*>::iterator begin() { return m_stack.begin(); }
std::vector<AnalysisStage*>::iterator end() { return m_stack.end(); }
private:
std::vector<AnalysisStage*> m_stack; //The analysis stack owns the analysis stages
unsigned int m_insertIndex=0;
};
}
#endif

View File

@ -1,19 +0,0 @@
/*
AnalysisStage.h
Represents a step in a chain of analyses that are run on a NavEvent. These stages are where NavParameters are set and validated. Users should use this base class
to create their own analyses and inject them into their project. See template NavProject for an example of use.
GWM -- Feb 2022
*/
#include "AnalysisStage.h"
namespace Navigator {
AnalysisStage::AnalysisStage(const std::string& name) :
m_name(name)
{
}
AnalysisStage::~AnalysisStage() {}
}

View File

@ -1,32 +0,0 @@
/*
AnalysisStage.h
Represents a step in a chain of analyses that are run on a NavEvent. These stages are where Parameters are set and validated. Users should use this base class
to create their own analyses and inject them into their project. See template NavProject for an example of use.
GWM -- Feb 2022
*/
#ifndef ANALYSIS_STAGE_H
#define ANALYSIS_STAGE_H
#include "Navigator/Core/NavCore.h"
#include "Navigator/Core/Parameter.h"
#include "NavData.h"
namespace Navigator {
class NAV_API AnalysisStage
{
public:
AnalysisStage(const std::string& name="AnalysisStage");
virtual ~AnalysisStage();
virtual void AnalyzePhysicsEvent(const NavEvent& event) {};
inline std::string GetName() { return m_name; }
private:
std::string m_name;
};
}
#endif

View File

@ -1,210 +0,0 @@
/*
CompassFile.cpp
Wrapper class around a shared pointer to an ifstream. Here the shared pointer is used
to overcome limitations of the ifstream class, namely that it is written such that ifstream
cannot be modified by move semantics. Contains all information needed to parse a single binary
CompassFile. Currently has a class wide defined buffer size; may want to make this user input
in the future.
Written by G.W. McCann Oct. 2020
Modified for Navigator; not really any significant changes. Just some simple changes, removal of unused data.
GWM -- Feb 2022
Update to reflect new CAEN binary data format with headers to indicate data contents.
GWM -- May 2022
*/
#include "CompassFile.h"
namespace Navigator {
CompassFile::CompassFile() :
m_filename(""), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_smap(nullptr), m_hitUsedFlag(true), m_file(std::make_shared<std::ifstream>()), m_eofFlag(false)
{
}
CompassFile::CompassFile(const std::string& filename) :
m_filename(""), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_smap(nullptr), m_hitUsedFlag(true), m_file(std::make_shared<std::ifstream>()), m_eofFlag(false)
{
Open(filename);
}
CompassFile::CompassFile(const std::string& filename, int bsize) :
m_filename(""), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_smap(nullptr), m_hitUsedFlag(true),
m_bufsize(bsize), m_file(std::make_shared<std::ifstream>()), m_eofFlag(false)
{
Open(filename);
}
CompassFile::~CompassFile()
{
Close();
}
void CompassFile::Open(const std::string& filename)
{
NAV_PROFILE_FUNCTION();
m_eofFlag = false;
m_hitUsedFlag = true;
m_filename = filename;
m_file->open(m_filename, std::ios::binary | std::ios::in);
m_file->seekg(0, std::ios_base::end);
m_size = (unsigned int)m_file->tellg();
if(m_size == 0)
{
m_eofFlag = true;
}
else
{
m_file->seekg(0, std::ios_base::beg);
ReadHeader();
m_nHits = m_size / m_hitsize;
m_buffersize = m_hitsize * m_bufsize;
m_hitBuffer.resize(m_buffersize);
}
}
void CompassFile::Close()
{
if(IsOpen())
m_file->close();
}
void CompassFile::ReadHeader()
{
if(!IsOpen())
{
NAV_WARN("Unable to get hit size from file {0}, sending invalid value.", m_filename);
return;
}
char* header = new char[2];
m_file->read(header, 2);
m_header = *((uint16_t*)header);
m_hitsize = 16; //default hitsize 16 bytes
if (Compass_IsEnergy(m_header))
m_hitsize += 2;
if (Compass_IsEnergyShort(m_header))
m_hitsize += 2;
if (Compass_IsEnergyCalibrated(m_header))
m_hitsize += 8;
if (Compass_IsWaves(m_header))
{
m_hitsize += 5;
char* firstHit = new char[m_hitsize]; //Read chunk of first hit
m_file->read(firstHit, m_hitsize);
firstHit += m_hitsize - 4; //Move to the Nsamples value
uint32_t nsamples = *((uint32_t*)firstHit);
m_hitsize += nsamples * 2; //Each sample is two bytes
m_file->seekg(0, std::ios_base::beg);
m_file->read(header, 2);
delete[] firstHit;
}
delete[] header;
}
/*
GetNextHit() is the function which... gets the next hit
Has to check if the buffer needs refilled/filled for the first time
Upon pulling a hit, sets the UsedFlag to false, letting the next level know
that the hit should be free game.
If the file cannot be opened, signals as though file is EOF
*/
bool CompassFile::GetNextHit()
{
NAV_PROFILE_FUNCTION();
if(!IsOpen()) return true;
if((m_bufferIter == nullptr || m_bufferIter == m_bufferEnd) && !IsEOF())
{
GetNextBuffer();
}
if(!IsEOF())
{
ParseNextHit();
m_hitUsedFlag = false;
}
return m_eofFlag;
}
/*
GetNextBuffer() ... self-explanatory name
Note tht this is where the EOF flag is set. The EOF is only singaled
after the LAST buffer is completely read (i.e literally no more data). ifstream sets its eof
bit upon pulling the last buffer, but this class waits until that entire
last buffer is read to singal EOF (the true end of file).
*/
void CompassFile::GetNextBuffer()
{
NAV_PROFILE_FUNCTION();
if(m_file->eof())
{
m_eofFlag = true;
return;
}
m_file->read(m_hitBuffer.data(), m_hitBuffer.size());
m_bufferIter = m_hitBuffer.data();
m_bufferEnd = m_bufferIter + m_file->gcount(); //one past the last datum
}
void CompassFile::ParseNextHit()
{
NAV_PROFILE_FUNCTION();
m_currentHit.board = *((uint16_t*)m_bufferIter);
m_bufferIter += 2;
m_currentHit.channel = *((uint16_t*)m_bufferIter);
m_bufferIter += 2;
m_currentHit.timestamp = *((uint64_t*)m_bufferIter);
m_bufferIter += 8;
if (Compass_IsEnergy(m_header))
{
m_currentHit.energy = *((uint16_t*)m_bufferIter);
m_bufferIter += 2;
}
if (Compass_IsEnergyCalibrated(m_header))
{
m_currentHit.energyCalibrated = *((uint64_t*)m_bufferIter);
m_bufferIter += 8;
}
if (Compass_IsEnergyShort(m_header))
{
m_currentHit.energyShort = *((uint16_t*)m_bufferIter);
m_bufferIter += 2;
}
m_currentHit.flags = *((uint32_t*)m_bufferIter);
m_bufferIter += 4;
if (Compass_IsWaves(m_header))
{
m_currentHit.waveCode = *((uint8_t*)m_bufferIter);
m_bufferIter += 1;
m_currentHit.Ns = *((uint32_t*)m_bufferIter);
m_bufferIter += 4;
if (m_currentHit.samples.size() != m_currentHit.Ns)
m_currentHit.samples.resize(m_currentHit.Ns);
for (size_t i = 0; i < m_currentHit.samples.size(); i++)
{
m_currentHit.samples[i] = *((uint16_t*)m_bufferIter);
m_bufferIter += 2;
}
}
if(m_smap != nullptr)
{ //memory safety
int gchan = m_currentHit.channel + m_currentHit.board*16;
m_currentHit.timestamp += m_smap->GetShift(gchan);
}
}
}

View File

@ -1,83 +0,0 @@
/*
CompassFile.h
Wrapper class around a shared pointer to an ifstream. Here the shared pointer is used
to overcome limitations of the ifstream class, namely that it is written such that ifstream
cannot be modified by move semantics. Contains all information needed to parse a single binary
CompassFile. Currently has a class wide defined buffer size; may want to make this user input
in the future.
Written by G.W. McCann Oct. 2020
Modified for Navigator; not really any significant changes. Just some simple changes, removal of unused data.
GWM -- Feb 2022
Update to reflect new CAEN binary data format with headers to indicate data contents.
GWM -- May 2022
*/
#ifndef COMPASSFILE_H
#define COMPASSFILE_H
#include "Navigator/Core/NavCore.h"
#include "CompassHit.h"
#include "Navigator/Physics/ShiftMap.h"
namespace Navigator {
class NAV_API CompassFile
{
public:
CompassFile();
CompassFile(const std::string& filename);
CompassFile(const std::string& filename, int bsize);
~CompassFile();
void Open(const std::string& filename);
void Close();
bool GetNextHit();
inline bool IsOpen() const { return m_file->is_open(); };
inline CompassHit GetCurrentHit() const { return m_currentHit; }
inline std::string GetName() const { return m_filename; }
inline bool CheckHitHasBeenUsed() const { return m_hitUsedFlag; } //query to find out if we've used the current hit
inline void SetHitHasBeenUsed() { m_hitUsedFlag = true; } //flip the flag to indicate the current hit has been used
inline bool IsEOF() const { return m_eofFlag; } //see if we've read all available data
inline bool* GetUsedFlagPtr() { return &m_hitUsedFlag; }
inline void AttachShiftMap(ShiftMap* map) { m_smap = map; }
inline unsigned int GetSize() const { return m_size; }
inline unsigned int GetNumberOfHits() const { return m_nHits; }
private:
void ReadHeader();
void ParseNextHit();
void GetNextBuffer();
using Buffer = std::vector<char>;
using FilePointer = std::shared_ptr<std::ifstream>; //to make this class copy/movable
std::string m_filename;
Buffer m_hitBuffer;
char* m_bufferIter;
char* m_bufferEnd;
ShiftMap* m_smap; //NOT owned by CompassFile. DO NOT delete
bool m_hitUsedFlag;
int m_bufsize = 200000; //size of the buffer in hits
int m_hitsize; //size of a CompassHit in bytes (without alignment padding)
uint16_t m_header;
int m_buffersize;
CompassHit m_currentHit;
FilePointer m_file;
bool m_eofFlag;
unsigned int m_size; //size of the file in bytes
unsigned int m_nHits; //number of hits in the file (m_size/m_hitsize)
};
}
#endif

View File

@ -1,24 +0,0 @@
#include "CompassHit.h"
namespace Navigator {
bool Compass_IsEnergy(uint16_t header)
{
return (header & CompassHeaders::Energy) != 0;
}
bool Compass_IsEnergyShort(uint16_t header)
{
return (header & CompassHeaders::EnergyShort) != 0;
}
bool Compass_IsEnergyCalibrated(uint16_t header)
{
return (header & CompassHeaders::EnergyCalibrated) != 0;
}
bool Compass_IsWaves(uint16_t header)
{
return (header & CompassHeaders::EnergyCalibrated) != 0;
}
}

View File

@ -1,47 +0,0 @@
/*
CompassHit.h
Simple struct representing data from the CAEN CoMPASS DAQ. Note here I do not have any of the non-standard data available (calibrated energy, waveform data, etc.)
GWM -- Feb 2022
Update to reflect new CAEN binary data format with headers to indicate data contents.
GWM -- May 2022
*/
#ifndef COMPASS_HIT_H
#define COMPASS_HIT_H
#include "Navigator/Core/NavCore.h"
namespace Navigator {
struct NAV_API CompassHit
{
uint16_t board = 0;
uint16_t channel = 0;
uint64_t timestamp = 0;
uint16_t energy = 0;
uint16_t energyShort = 0;
uint64_t energyCalibrated = 0;
uint32_t flags = 0;
uint8_t waveCode = 0;
uint32_t Ns = 0;
std::vector<uint16_t> samples;
};
//New to CoMPASS Data Format: Headers indicating what data is present.
enum CompassHeaders
{
Energy = 0x0001,
EnergyShort = 0x0002,
EnergyCalibrated = 0x0004,
Waves = 0x0008
};
bool Compass_IsEnergy(uint16_t header);
bool Compass_IsEnergyShort(uint16_t header);
bool Compass_IsEnergyCalibrated(uint16_t header);
bool Compass_IsWaves(uint16_t header);
}
#endif

View File

@ -1,149 +0,0 @@
/*
CompassOnlineSource.cpp
A data source for online CAEN CoMPASS Data. Uses TCPClient to connect to the CoMPASS server. Data is then converted
from the CAEN CoMPASS format to the native NavData format. Note that here we use syncrhonous since we
need to know if the buffer is/was filled, however we use non-blocking since we don't want the entire process to hang on attempting a connection or waiting
for data to come over the pipe. We handle the case of an un-filled buffer internally.
IMPORTANT
Navigator wants a unqiue ID on each hit. To do this we use the idiom:
id = board_number * nchannels_per_board + channel_number
This requires two things: that the class variable m_nchannels_per_board be set to match your physical digitizers, and that ALL of your
digitizers have the SAME number of channels. By default CompassRun assumes 16 channels per board, as this is what is used with the SE-SPS setup at FoxLab.
If you use a different set of boards, CHANGE THIS VALUE! If you use mixed boards, you will need to invent a new id scheme altogether.
ADDITIONALLY
CoMPASS servers provide no stream side information on the state of a transfer (verified via communication w/ CAEN). That is: there are no headers or enders on the data transfers.
This forces us to use the size of a single CoMPASS datum (CompassHit) to determine the state of a transfer. If the read buffer size is not a whole multiple of CompassHits, the data
was determined to be fragmented. This has a huge drawback: in general there is no guarantee that the first byte of the transferred data is the first byte of a CompassHit. This means that
Navigator MUST be connected to the CoMPASS server BEFORE starting the aquisition. Otherwise the program could endup in a state of scrambled unpacking (also verified w/ CAEN).
Maybe we can get them to change this? Headers reeaaally should exist for transfers like this.
GWM -- April 2022
*/
#include "CompassOnlineSource.h"
namespace Navigator {
CompassOnlineSource::CompassOnlineSource(const std::string& hostname, const std::string& port, uint16_t header, int channels_per_board) :
DataSource(), m_bufferIter(nullptr), m_bufferEnd(nullptr), m_header(header), m_nchannels_per_board(channels_per_board)
{
InitConnection(hostname, port);
}
CompassOnlineSource::~CompassOnlineSource() {}
void CompassOnlineSource::InitConnection(const std::string& hostname, const std::string& port)
{
NAV_PROFILE_FUNCTION();
m_datasize = 16; //base size of CoMPASS data
if (Compass_IsEnergy(m_header))
m_datasize += 2;
if (Compass_IsEnergyShort(m_header))
m_datasize += 2;
if (Compass_IsEnergyCalibrated(m_header))
m_datasize += 8;
if (Compass_IsWaves(m_header))
NAV_ERROR("Navigator does not support reading CoMPASS wave data for an online source!");
m_validFlag = false;
m_connection.Connect(hostname, port);
if (m_connection.IsOpen())
{
m_validFlag = true;
}
}
const NavData& CompassOnlineSource::GetData()
{
NAV_PROFILE_FUNCTION();
size_t range = m_bufferEnd - m_bufferIter; //how much buffer we have left
if (!IsValid())
{
NAV_ERROR("Attempting to access invalid source at CompassOnlineSource!");
m_datum = NavData();
return m_datum;
}
else if (m_bufferIter == nullptr || range < m_datasize || m_bufferIter == m_bufferEnd) //If no buffer/buffer completely used/buffer fragmented fill
{
FillBuffer();
}
if (m_bufferIter != m_bufferEnd && range >= m_datasize)//If buffer and enough data for a hit, get it
GetHit();
else
{
m_datum = NavData();
return m_datum;
}
m_datum.longEnergy = m_currentHit.energy;
m_datum.shortEnergy = m_currentHit.energyShort;
m_datum.calEnergy = m_currentHit.energyCalibrated;
m_datum.timestamp = m_currentHit.timestamp;
m_datum.id = m_currentHit.board * m_nchannels_per_board + m_currentHit.channel;
return m_datum;
}
void CompassOnlineSource::FillBuffer()
{
NAV_PROFILE_FUNCTION();
if (!m_connection.IsOpen()) //Make sure connection is still cool
{
m_validFlag = false;
return;
}
std::vector<char> recieved = m_connection.Read();
//If we didn't finish the last buffer toss all of the stuff we used and then append the recieved data
//Otherwise, copy over the recieved buffer. Note lack of vector::resize, vector::reserve. Intentional for performance.
//The amount of copying/resizing is best handled by the std (according to multiple references)
if (m_bufferIter != m_bufferEnd)
{
size_t pos = m_bufferEnd - m_bufferIter;
m_currentBuffer.erase(m_currentBuffer.begin(), m_currentBuffer.begin() + (m_currentBuffer.size() - pos)); //remove used bytes
m_currentBuffer.insert(m_currentBuffer.end(), recieved.begin(), recieved.end());
}
else
m_currentBuffer = recieved;
m_bufferIter = m_currentBuffer.data();
m_bufferEnd = m_currentBuffer.data() + m_currentBuffer.size();
}
void CompassOnlineSource::ReadHeader()
{
}
void CompassOnlineSource::GetHit()
{
NAV_PROFILE_FUNCTION();
m_currentHit.board = *((uint16_t*)m_bufferIter);
m_bufferIter += 2;
m_currentHit.channel = *((uint16_t*)m_bufferIter);
m_bufferIter += 2;
m_currentHit.timestamp = *((uint64_t*)m_bufferIter);
m_bufferIter += 8;
if (Compass_IsEnergy(m_header))
{
m_currentHit.energy = *((uint16_t*)m_bufferIter);
m_bufferIter += 2;
}
if (Compass_IsEnergyCalibrated(m_header))
{
m_currentHit.energyCalibrated = *((uint16_t*)m_bufferIter);
m_bufferIter += 8;
}
if (Compass_IsEnergyShort(m_header))
{
m_currentHit.energyShort = *((uint16_t*)m_bufferIter);
m_bufferIter += 2;
}
m_currentHit.flags = *((uint32_t*)m_bufferIter);
m_bufferIter += 4;
}
}

View File

@ -1,68 +0,0 @@
/*
CompassOnlineSource.h
A data source for online CAEN CoMPASS Data. Uses TCPClient to connect to the CoMPASS server. Data is then converted
from the CAEN CoMPASS format to the native NavData format. Note that here we use syncrhonous since we
need to know if the buffer is/was filled, however we use non-blocking since we don't want the entire process to hang on attempting a connection or waiting
for data to come over the pipe. We handle the case of an un-filled buffer internally.
IMPORTANT
Navigator wants a unqiue ID on each hit. To do this we use the idiom:
id = board_number * nchannels_per_board + channel_number
This requires two things: that the class variable m_nchannels_per_board be set to match your physical digitizers, and that ALL of your
digitizers have the SAME number of channels. By default CompassRun assumes 16 channels per board, as this is what is used with the SE-SPS setup at FoxLab.
If you use a different set of boards, CHANGE THIS VALUE! If you use mixed boards, you will need to invent a new id scheme altogether.
ADDITIONALLY
CoMPASS servers provide no stream side information on the state of a transfer (verified via communication w/ CAEN). That is: there are no headers or enders on the data transfers.
This forces us to use the size of a single CoMPASS datum (CompassHit) to determine the state of a transfer. If the read buffer size is not a whole multiple of CompassHits, the data
was determined to be fragmented. This has a huge drawback: in general there is no guarantee that the first byte of the transferred data is the first byte of a CompassHit. This means that
Navigator MUST be connected to the CoMPASS server BEFORE starting the aquisition. Otherwise the program could endup in a state of scrambled unpacking (also verified w/ CAEN).
Maybe we can get them to change this? Headers reeaaally should exist for transfers like this.
GWM -- April 2022
Update to reflect new CAEN binary data format with headers to indicate data contents. Note that as prev. mentioned, no headers, so cannot rely on data stream to indicate state. State must be selected
by user at UI when creating source. Cannot support waves atm. No way to predict size of first event to calibrate the number of samples for the stream (or guarantee that they will be constant for duration
of Navigator's runtime). Best to use the CoMPASSPlot for waves.
GWM -- May 2022
*/
#ifndef COMPASS_ONLINE_SOURCE_H
#define COMPASS_ONLINE_SOURCE_H
#include "Navigator/Physics/DataSource.h"
#include "Navigator/Utils/TCPClient.h"
#include "CompassHit.h"
namespace Navigator {
class CompassOnlineSource : public DataSource
{
public:
CompassOnlineSource(const std::string& hostname, const std::string& port, uint16_t header, int channels_per_board=16);
virtual ~CompassOnlineSource() override;
virtual const NavData& GetData() override;
private:
void InitConnection(const std::string& hostname, const std::string& port);
void FillBuffer();
void GetHit();
void ReadHeader();
std::vector<char> m_currentBuffer;
uint16_t m_header;
int m_datasize; //size of CoMPASS hit in bytes, set by header arg
const int m_nchannels_per_board = 16; //IMPORTANT: Used for ID'ing channels uniquely. If you use boards with 32 or 8 or 64 channels you must change this! If you mix boards with
//different numbers of channels, you will have to find a different id solution.
char* m_bufferIter;
char* m_bufferEnd;
CompassHit m_currentHit;
TCPClient m_connection;
};
}
#endif

View File

@ -1,156 +0,0 @@
/*
CompassRun.cpp
Class designed as abstraction of a collection of binary files that represent the total data in a single
Compass data run. It handles the user input (shift maps, file collection etc.) and creates a list of
CompassFiles from which to draw data. It then draws data from these files, organizes them in time,
and writes to a ROOT file for further processing.
Written by G.W. McCann Oct. 2020
Updated to also handle scaler data. -- GWM Oct. 2020
Modifed and updated for use in Navigator. Obviously stripped out any ROOT code. Also, now uses the very nice std::filesystem
library to handle filepathing. Also, removed scalers (for now).
GWM -- Feb 2022
Update to reflect new CAEN binary data format with headers to indicate data contents.
GWM -- May 2022
*/
#include "CompassRun.h"
namespace Navigator {
CompassRun::CompassRun() :
DataSource(), m_directory(""), m_startIndex(0), m_nchannels_per_board(16)
{
}
CompassRun::CompassRun(const std::string& dir, int channels_per_board) :
DataSource(), m_directory(dir), m_startIndex(0), m_nchannels_per_board(channels_per_board)
{
CollectFiles();
}
CompassRun::~CompassRun() {}
void CompassRun::CollectFiles()
{
NAV_PROFILE_FUNCTION();
int nfiles=0;
for(auto& item : std::filesystem::directory_iterator(m_directory))
{
if(item.path().extension() == m_extension)
nfiles++;
}
m_datafiles.clear();
m_datafiles.reserve(nfiles);
for(auto& item : std::filesystem::directory_iterator(m_directory))
{
if(item.path().extension() == m_extension)
{
m_datafiles.emplace_back(item.path().string());
}
}
long total_hits=0;
for(auto& file : m_datafiles)
{
if(!file.IsOpen())
{
NAV_ERROR("Unable to open file with name {0}", file.GetName());
m_validFlag = false;
return;
}
if(m_smap.IsValid())
file.AttachShiftMap(&m_smap);
total_hits += file.GetNumberOfHits();
}
if(m_datafiles.size() == 0)
{
NAV_WARN("Unable to find any files with extension {0} in directory {1}. CompassRun killed.", m_extension, m_directory);
m_validFlag = false;
}
else
{
NAV_INFO("Succesfully opened {0} files with {1} total hits", nfiles, total_hits);
m_validFlag = true;
}
}
/*
GetHitsFromFiles() is the function which actually retrieves and sorts the data from the individual
files. There are several tricks which allow this to happen. First is that, after sorting, it is impossible
to determine which file the data originally came from (short of parsing the name of the file against board/channel).
However, we need to let the file know that we want it to pull the next hit. To do this, a pointer to the UsedFlag of the file
is retrieved along with the data. This flag is flipped so that on the next hit cycle a new hit is pulled. Second is the use
of a rolling start index. Once a file has gone EOF, we no longer need it. If this is the first file in the list, we can just skip
that index all together. In this way, the loop can go from N times to N-1 times.
*/
bool CompassRun::GetHitsFromFiles()
{
NAV_PROFILE_FUNCTION();
std::pair<CompassHit, bool*> earliestHit = std::make_pair(CompassHit(), nullptr);
for(unsigned int i=m_startIndex; i<m_datafiles.size(); i++)
{
if(m_datafiles[i].CheckHitHasBeenUsed())
m_datafiles[i].GetNextHit();
if(m_datafiles[i].IsEOF())
{
if(i == m_startIndex)
m_startIndex++;
continue;
}
else if(i == m_startIndex)
{
earliestHit = std::make_pair(m_datafiles[i].GetCurrentHit(), m_datafiles[i].GetUsedFlagPtr());
}
else if(m_datafiles[i].GetCurrentHit().timestamp < earliestHit.first.timestamp)
{
earliestHit = std::make_pair(m_datafiles[i].GetCurrentHit(), m_datafiles[i].GetUsedFlagPtr());
}
}
if(earliestHit.second == nullptr)
return false; //Make sure that there actually was a hit
m_hit = earliestHit.first;
*earliestHit.second = true;
return true;
}
const NavData& CompassRun::GetData()
{
NAV_PROFILE_FUNCTION();
if(!IsValid())
{
NAV_ERROR("Trying to access CompassRun data when invalid, bug detected!");
m_datum = NavData();
return m_datum;
}
if (!GetHitsFromFiles())
{
m_validFlag = false;
m_datum = NavData();
}
else
{
//Convert data from CoMPASS format to universal Navigator format.
m_datum.longEnergy = m_hit.energy;
m_datum.shortEnergy = m_hit.energyShort;
m_datum.calEnergy = m_hit.energyCalibrated;
m_datum.timestamp = m_hit.timestamp;
m_datum.id = m_hit.board * m_nchannels_per_board + m_hit.channel;
}
return m_datum;
}
}

View File

@ -1,66 +0,0 @@
/*
CompassRun.h
Class designed as abstraction of a collection of binary files that represent the total data in a single
Compass data run. It handles the user input (shift maps, file collection etc.) and creates a list of
CompassFiles from which to draw data. It then draws data from these files, organizes them in time,
and writes to a ROOT file for further processing.
Written by G.W. McCann Oct. 2020
Modifed and updated for use in Navigator. Obviously stripped out any ROOT code. Also, now uses the very nice std::filesystem
library to handle filepathing. One change of great import: Navigator wants a unqiue ID on each hit. To do this we use the idiom:
id = board_number * nchannels_per_board + channel_number
This requires two things: that the class variable m_nchannels_per_board be set to match your physical digitizers, and that ALL of your
digitizers have the SAME number of channels. By default CompassRun assumes 16 channels per board, as this is what is used with the SE-SPS setup at FoxLab.
If you use a different set of boards, CHANGE THIS VALUE! If you use mixed boards, you will need to invent a new id scheme altogether.
GWM -- Feb 2022
Update to reflect new CAEN binary data format with headers to indicate data contents.
GWM -- May 2022
*/
#ifndef COMPASSRUN_H
#define COMPASSRUN_H
#include "Navigator/Core/NavCore.h"
#include "Navigator/Physics/DataSource.h"
#include "CompassFile.h"
#include "Navigator/Physics/ShiftMap.h"
#include <filesystem>
namespace Navigator {
class NAV_API CompassRun : public DataSource
{
public:
CompassRun();
CompassRun(const std::string& dir, int channels_per_board=16);
virtual ~CompassRun();
virtual const NavData& GetData() override;
inline void SetDirectory(const std::string& dir) { m_directory = dir; CollectFiles(); }
inline void SetShiftMap(const std::string& filename) { m_smap.SetFile(filename); }
private:
void CollectFiles();
bool GetHitsFromFiles();
std::filesystem::path m_directory;
const std::string m_extension = ".bin";
std::vector<CompassFile> m_datafiles;
unsigned int m_startIndex; //this is the file we start looking at; increases as we finish files.
int m_nchannels_per_board; //IMPORTANT: Used for ID'ing channels uniquely. If you use boards with 32 or 8 or 64 channels you must change this! If you mix boards with
//different numbers of channels, you will have to find a different id solution.
ShiftMap m_smap;
CompassHit m_hit;
unsigned int m_totalHits;
};
}
#endif

View File

@ -1,39 +0,0 @@
/*
DataSource.cpp
Abstract data source class. In Navigator a DataSource can be either an online (live) source or an offline (file) source. By default,
Navigator has classes to handle CAEN CoMPASS data sources (files and online); other sources may be implemented as more use cases for Navigator become
apparent.
GWM -- Feb 2022
*/
#include "DataSource.h"
#include "Caen/CompassRun.h"
#include "Caen/CompassOnlineSource.h"
namespace Navigator {
//loc=either an ip address or a file location, port=address port, or unused in case of file
DataSource* CreateDataSource(const std::string& location, const std::string& port, uint16_t header, int channels_per_board, DataSource::SourceType type)
{
switch(type)
{
case DataSource::SourceType::CompassOffline : return new CompassRun(location, channels_per_board);
case DataSource::SourceType::CompassOnline : return new CompassOnlineSource(location, port, header, channels_per_board);
case DataSource::SourceType::None : return nullptr;
}
NAV_WARN("Invalid DataSourceType at CreateDataSource!");
return nullptr;
}
std::string ConvertDataSourceTypeToString(DataSource::SourceType type)
{
switch(type)
{
case DataSource::SourceType::None: return "None";
case DataSource::SourceType::CompassOnline : return "CompassOnline";
case DataSource::SourceType::CompassOffline : return "CompassOffline";
}
return "None";
}
}

View File

@ -1,46 +0,0 @@
/*
DataSource.h
Abstract data source class. In Navigator a DataSource can be either an online (live) source or an offline (file) source. By default,
Navigator has classes to handle CAEN CoMPASS data sources (files and online); other sources may be implemented as more use cases for Navigator become
apparent.
GWM -- Feb 2022
*/
#ifndef DATA_SOURCE_H
#define DATA_SOURCE_H
#include "Navigator/Core/NavCore.h"
#include "NavData.h"
namespace Navigator {
class NAV_API DataSource
{
public:
enum class SourceType //Need to know what kind of sources are available.
{
None,
CompassOnline,
CompassOffline
};
DataSource() :
m_validFlag(false)
{
}
virtual ~DataSource() {};
virtual const NavData& GetData() = 0;
inline bool IsValid() { return m_validFlag; }
protected:
bool m_validFlag;
NavData m_datum;
};
NAV_API DataSource* CreateDataSource(const std::string& location, const std::string& port, uint16_t bitflags, int channels_per_board, DataSource::SourceType type);
NAV_API std::string ConvertDataSourceTypeToString(DataSource::SourceType type);
}
#endif

View File

@ -1,31 +0,0 @@
/*
NavData.h
Simple data structures for the event building process in Navigator. Way to create uniform data from different sources. Note that the current paradigm
is very heavily tied to digital data aquisition systems. Any attempt to use Navigator for analog systems would require a deep overhaul of the event processing.
Most likely link to something like nscldaq.
GWM -- Feb 2022
Update to reflect new CAEN binary data format with headers to indicate data contents.
GWM -- May 2022
*/
#ifndef NAVDATA_H
#define NAVDATA_H
namespace Navigator {
struct NavData
{
uint32_t longEnergy;
uint32_t shortEnergy;
uint64_t calEnergy;
uint64_t timestamp;
uint32_t id;
};
using NavEvent = std::vector<NavData>;
}
#endif

View File

@ -1,65 +0,0 @@
/*
PhysicsEventBuilder.h
Class for taking in raw NavData and converting into a NavEvent. NavEvent is just a std::vector of NavData, where
the held NavData all falls within a time window called the coincidence window. As soon as a NavData is given that falls outside
of this window, the current event is shifted to the ready event and the AddDatumToEvent function returns true. The ready event can
then be retrieved. The hit that triggered the end of event then is used to start the new event. The current pattern is strongly associated
with digital electronics concepts for nuclear data aquisition systems.
GWM -- Feb 2022
*/
#include "PhysicsEventBuilder.h"
namespace Navigator {
PhysicsEventBuilder::PhysicsEventBuilder(uint64_t windowSize) :
m_sortFlag(false), m_coincWindow(windowSize), m_bufferIndex(0)
{
}
PhysicsEventBuilder::~PhysicsEventBuilder()
{
}
bool PhysicsEventBuilder::AddDatum(const NavData& datum)
{
NAV_PROFILE_FUNCTION();
if (datum.timestamp == 0) //Ignore empty data (need a valid timestamp)
return false;
m_dataBuffer[m_bufferIndex] = datum;
m_bufferIndex++;
if (m_bufferIndex < s_maxDataBuffer) //If we haven't filled the buffer keep going
return false;
else if (m_sortFlag)
std::sort(m_dataBuffer.begin(), m_dataBuffer.end(), [](NavData& i, NavData& j) { return i.timestamp < j.timestamp; });
//Generate our ready events
m_readyEvents.clear();
uint64_t eventStartTime = m_dataBuffer[0].timestamp;
NavEvent event;
event.push_back(m_dataBuffer[0]);
for (auto& data : m_dataBuffer)
{
if (data.timestamp - eventStartTime < m_coincWindow) //are we within active window
{
event.push_back(data);
}
else // found one that falls outside
{
m_readyEvents.push_back(event);
event.clear();
eventStartTime = data.timestamp;
event.push_back(data);
}
}
m_bufferIndex = 0;
return true;
}
const std::vector<NavEvent>& PhysicsEventBuilder::GetReadyEvents() const
{
return m_readyEvents;
}
}

View File

@ -1,45 +0,0 @@
/*
PhysicsEventBuilder.h
Class for taking in raw NavData and converting into a NavEvent. NavEvent is just a std::vector of NavData, where
the held NavData all falls within a time window called the coincidence window. As soon as a NavData is given that falls outside
of this window, the current event is shifted to the ready event and the AddDatumToEvent function returns true. The ready event can
then be retrieved. The hit that triggered the end of event then is used to start the new event. The current pattern is strongly associated
with digital electronics concepts for nuclear data aquisition systems.
GWM -- Feb 2022
*/
#ifndef PHYSICS_EVENT_BUILDER_H
#define PHYSICS_EVENT_BUILDER_H
#include "NavData.h"
namespace Navigator {
class NAV_API PhysicsEventBuilder
{
public:
PhysicsEventBuilder(uint64_t windowSize);
~PhysicsEventBuilder();
inline void SetCoincidenceWindow(uint64_t windowSize) { m_coincWindow = windowSize; }
inline void SetSortFlag(bool flag) { m_sortFlag = flag; }
inline void ClearAll() // reset all internal structures
{
m_bufferIndex = 0;
m_readyEvents.clear();
}
bool AddDatum(const NavData& datum);
const std::vector<NavEvent>& GetReadyEvents() const;
private:
bool m_sortFlag;
static constexpr int s_maxDataBuffer = 1000;
std::array<NavData, s_maxDataBuffer> m_dataBuffer;
int m_bufferIndex;
std::vector<NavEvent> m_readyEvents;
uint64_t m_coincWindow;
};
}
#endif

View File

@ -1,188 +0,0 @@
/*
PhysicsLayer.cpp
The layer of the application representing all physics. This includes the data source and the thread upon which data is piped, event built, and analyzed.
PhysicsLayer receives a PhysicsStartEvent or PhysicsStopEvent to handle changes to the state of the secondary thread. As it handles the thread, it must have synchronization.
This is handled on two levels. The first is a mutex that synchronizes access to the DataSource. The second is an atomic boolean flag which is used to control the state of the physics thread.
PhysicsLayer also owns the AnalysisStack for the application.
GWM -- Feb 2022
*/
#include "PhysicsLayer.h"
#include "Navigator/Core/SpectrumManager.h"
#include "NavData.h"
namespace Navigator {
PhysicsLayer::PhysicsLayer() :
m_activeFlag(false), m_source(nullptr), m_eventBuilder(0), m_physThread(nullptr)
{
}
PhysicsLayer::~PhysicsLayer()
{
NAV_PROFILE_FUNCTION();
if (m_activeFlag)
{
DetachDataSource();
DestroyPhysThread();
}
}
void PhysicsLayer::OnAttach()
{
/* For debugging
NavParameter par("joseph");
par.SetValue(8);
NAV_INFO("Does the par exist? {0}", ParameterMap::GetInstance().IsParameterValid("joseph"));
NAV_INFO("What is its value? {0}", ParameterMap::GetInstance().GetParameterValue("joseph"));
*/
}
void PhysicsLayer::OnDetach()
{
}
void PhysicsLayer::OnEvent(Event& event)
{
NAV_PROFILE_FUNCTION();
EventDispatcher dispatch(event);
dispatch.Dispatch<PhysicsStartEvent>(BIND_EVENT_FUNCTION(PhysicsLayer::OnPhysicsStartEvent));
dispatch.Dispatch<PhysicsStopEvent>(BIND_EVENT_FUNCTION(PhysicsLayer::OnPhysicsStopEvent));
}
bool PhysicsLayer::OnPhysicsStartEvent(PhysicsStartEvent& event)
{
NAV_PROFILE_FUNCTION();
//If the Physics thread is active, kill it
if(m_activeFlag)
{
DetachDataSource();
DestroyPhysThread();
}
AttachDataSource(event);
//If we succesfully attached, fire up a new phys thread
if(m_activeFlag)
{
NAV_INFO("Starting new analysis thread...");
m_physThread = new std::thread(&PhysicsLayer::RunSource, std::ref(*this));
}
return true;
}
bool PhysicsLayer::OnPhysicsStopEvent(PhysicsStopEvent& event)
{
NAV_PROFILE_FUNCTION();
if (m_activeFlag)
{
DetachDataSource();
DestroyPhysThread();
}
return true;
}
void PhysicsLayer::PushStage(AnalysisStage* stage)
{
m_physStack.PushStage(stage);
}
void PhysicsLayer::OnUpdate(Timestep& step) {}
/*Threaded functions*/
void PhysicsLayer::DestroyPhysThread()
{
NAV_PROFILE_FUNCTION();
NAV_INFO("Destroying the analysis thread...");
//Join the thread back to the parent (finish up the thread)
if(m_physThread != nullptr && m_physThread->joinable())
{
m_physThread->join();
}
//Free the thread memory
if(m_physThread != nullptr)
{
delete m_physThread;
m_physThread = nullptr;
}
NAV_INFO("Thread destroyed.");
}
void PhysicsLayer::AttachDataSource(PhysicsStartEvent& event)
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_sourceMutex); //Shouldn't matter for this, but safety first
m_source.reset(CreateDataSource(event.GetSourceLocation(), event.GetSourcePort(), event.GetBitFlags(), event.GetChannelsPerBoard(), event.GetSourceType()));
m_eventBuilder.SetCoincidenceWindow(event.GetCoincidenceWindow());
m_eventBuilder.SetSortFlag(event.GetSortFlag());
m_eventBuilder.ClearAll(); //Protect against stopping mid-event
if (m_source->IsValid())
{
NAV_INFO("Attach successful. Enabling data pull...");
m_activeFlag = true;
}
else
{
NAV_ERROR("DataSource attach failed... check for error conditions.");
m_source.reset(nullptr);
}
}
void PhysicsLayer::DetachDataSource()
{
NAV_PROFILE_FUNCTION();
std::scoped_lock<std::mutex> guard(m_sourceMutex);
NAV_INFO("Detaching physics data source...");
m_activeFlag = false;
m_source.reset(nullptr);
NAV_INFO("Detach succesful.");
}
void PhysicsLayer::RunSource()
{
NAV_PROFILE_FUNCTION();
SpectrumManager& manager = SpectrumManager::GetInstance();
std::vector<NavEvent> events;
NavData datum;
while(m_activeFlag)
{
//Scope to encapsulate access to the data source
{
std::scoped_lock<std::mutex> guard(m_sourceMutex);
if (m_source == nullptr || !m_source->IsValid())
{
return;
}
/*
Looks funny, but two conditions lead to !IsValid(). Either source prev. shutdown,
OR we reached end of source, indicated after prev. data grab
*/
datum = m_source->GetData();
if(!m_source->IsValid())
{
NAV_INFO("End of data source.");
return;
}
}
//Pass data from source to event builder. True from AddDatum indicates events are ready
if (m_eventBuilder.AddDatum(datum))
{
events = m_eventBuilder.GetReadyEvents();
for (auto& event : events)
{
for (auto& stage : m_physStack)
stage->AnalyzePhysicsEvent(event);
//Now that the analysis stack has filled all our NavParameters with data, update the histogram counts
manager.UpdateHistograms();
//Invalidate all parameters to get ready for next event
manager.InvalidateParameters();
}
}
}
}
}

View File

@ -1,64 +0,0 @@
/*
PhysicsLayer.h
The layer of the application representing all physics. This includes the data source and the thread upon which data is piped, event built, and analyzed.
PhysicsLayer receives a PhysicsStartEvent or PhysicsStopEvent to handle changes to the state of the secondary thread. As it handles the thread, it must have synchronization.
This is handled on two levels. The first is a mutex that synchronizes access to the DataSource. The second is an atomic boolean flag which is used to control the state of the physics thread.
PhysicsLayer also owns the AnalysisStack for the application.
GWM -- Feb 2022
*/
#ifndef PHYSICS_LAYER_H
#define PHYSICS_LAYER_H
#include "Navigator/Core/NavCore.h"
#include "Navigator/Core/Layer.h"
#include "Navigator/Events/PhysicsEvent.h"
#include "AnalysisStack.h"
#include "AnalysisStage.h"
#include "DataSource.h"
#include "PhysicsEventBuilder.h"
#include <thread>
#include <mutex>
#include <atomic>
namespace Navigator {
class NAV_API PhysicsLayer : public Layer
{
public:
PhysicsLayer();
virtual ~PhysicsLayer();
virtual void OnAttach() override;
virtual void OnUpdate(Timestep& step) override;
virtual void OnDetach() override;
virtual void OnImGuiRender() override {};
virtual void OnEvent(Event& event) override;
bool OnPhysicsStartEvent(PhysicsStartEvent& event);
bool OnPhysicsStopEvent(PhysicsStopEvent& event);
void PushStage(AnalysisStage* stage);
private:
void DestroyPhysThread();
void AttachDataSource(PhysicsStartEvent& event);
void DetachDataSource();
void RunSource();
AnalysisStack m_physStack;
std::atomic<bool> m_activeFlag; //safe read/write across thread, but more expensive
std::mutex m_sourceMutex;
std::unique_ptr<DataSource> m_source;
PhysicsEventBuilder m_eventBuilder;
std::thread* m_physThread;
};
}
#endif

View File

@ -1,85 +0,0 @@
/*
ShiftMap.h
New class to act a go-between for timestamp shifts to channels. Takes in a
formated file containing data for shifts and then stores them in an unordered_map.
Key is a global compass channel (board#*16 + channel). Shifts in ps.
Written by G.W. McCann Oct. 2020
Not currently implemented for Navigator though it could still be useful. Leave this here as a maybe upgrade path.
GWM -- Feb 2022
*/
#include "ShiftMap.h"
namespace Navigator {
ShiftMap::ShiftMap() :
m_filename(""), m_validFlag(false)
{
}
ShiftMap::ShiftMap(const std::string& filename) :
m_filename(filename), m_validFlag(false)
{
ParseFile();
}
ShiftMap::~ShiftMap() {}
void ShiftMap::SetFile(const std::string& filename)
{
m_filename = filename;
ParseFile();
}
uint64_t ShiftMap::GetShift(int gchan)
{
if(!m_validFlag)
return 0;
auto iter = m_map.find(gchan);
if(iter == m_map.end())
return 0;
else
return iter->second;
}
void ShiftMap::ParseFile()
{
m_validFlag = false;
std::ifstream input(m_filename);
if(!input.is_open())
return;
int board, channel, gchan;
uint64_t shift;
std::string junk, temp;
std::getline(input, junk);
std::getline(input, junk);
while(input>>board)
{
input>>temp;
input>>shift;
if(temp == "all") //keyword to set all channels in this board to same shift
{
for(int i=0; i<16; i++)
{
gchan = board*16 + i;
m_map[gchan] = shift;
}
}
else
{
channel = stoi(temp);
gchan = channel + board*16;
m_map[gchan] = shift;
}
}
m_validFlag = true;
}
}

View File

@ -1,43 +0,0 @@
/*
ShiftMap.h
New class to act a go-between for timestamp shifts to channels. Takes in a
formated file containing data for shifts and then stores them in an unordered_map.
Key is a global compass channel (board#*16 + channel). Shifts in ps.
Written by G.W. McCann Oct. 2020
Not currently implemented for Navigator though it could still be useful. Leave this here as a maybe upgrade path.
GWM -- Feb 2022
*/
#ifndef SHIFTMAP_H
#define SHIFTMAP_H
#include "Navigator/Core/NavCore.h"
namespace Navigator {
class NAV_API ShiftMap
{
public:
ShiftMap();
ShiftMap(const std::string& filename);
~ShiftMap();
void SetFile(const std::string& filename);
inline bool IsValid() { return m_validFlag; }
inline std::string GetFilename() { return m_filename; }
uint64_t GetShift(int gchan);
private:
void ParseFile();
std::string m_filename;
bool m_validFlag;
std::unordered_map<int, uint64_t> m_map;
};
}
#endif

View File

@ -1,22 +0,0 @@
/*
GraphicsContext.h
Abstraction of graphical rendering context. Exists to allow Navigator the felxibility to be backend api agnostic, though currently
only OpenGL is implemented. Entirely based upon the work done by @TheCherno in his game engine series. See his content for more details.
GWM -- Feb 2022
*/
#ifndef GRAPHICS_CONTEXT_H
#define GRAPHICS_CONTEXT_H
namespace Navigator {
class NAV_API GraphicsContext
{
public:
virtual void Init() = 0;
virtual void SwapBuffers() = 0;
};
}
#endif

View File

@ -1,13 +0,0 @@
/*
RenderCommand.cpp
Render call abstraction. Exists to allow Navigator the felxibility to be backend api agnostic, though currently
only OpenGL is implemented. Entirely based upon the work done by @TheCherno in his game engine series. See his content for more details.
GWM -- Feb 2022
*/
#include "RenderCommand.h"
#include "Platform/OpenGL/OpenGLRendererAPI.h"
namespace Navigator {
RendererAPI* RenderCommand::s_api = new OpenGLRendererAPI();
}

View File

@ -1,29 +0,0 @@
/*
RenderCommand.h
Render call abstraction. Exists to allow Navigator the felxibility to be backend api agnostic, though currently
only OpenGL is implemented. Entirely based upon the work done by @TheCherno in his game engine series. See his content for more details.
GWM -- Feb 2022
*/
#ifndef RENDER_COMMAND_H
#define RENDER_COMMAND_H
#include "Navigator/Core/NavCore.h"
#include "RendererAPI.h"
namespace Navigator {
class NAV_API RenderCommand
{
public:
inline static void SetClearColor(const glm::vec4& color_array) { s_api->SetClearColor(color_array); }
inline static void Clear() { s_api->Clear(); }
inline static float GetFrameTime() { return s_api->GetFrameTime(); }
private:
static RendererAPI* s_api;
};
}
#endif

View File

@ -1,12 +0,0 @@
/*
RenderAPI.h
Render API abstraction. Exists to allow Navigator the felxibility to be backend api agnostic, though currently
only OpenGL is implemented. Entirely based upon the work done by @TheCherno in his game engine series. See his content for more details.
GWM -- Feb 2022
*/
#include "RendererAPI.h"
namespace Navigator {
RendererAPI::API RendererAPI::s_api = RendererAPI::API::OpenGL;
}

View File

@ -1,37 +0,0 @@
/*
RenderAPI.h
Render API abstraction. Exists to allow Navigator the felxibility to be backend api agnostic, though currently
only OpenGL is implemented. Entirely based upon the work done by @TheCherno in his game engine series. See his content for more details.
GWM -- Feb 2022
*/
#ifndef RENDERER_API_H
#define RENDERER_API_H
#include "Navigator/Core/NavCore.h"
#include "glm/vec4.hpp"
namespace Navigator {
class NAV_API RendererAPI
{
public:
enum class API
{
None = 0,
OpenGL = 1 //Currently only have OpenGL
};
virtual void Clear() = 0;
virtual void SetClearColor(const glm::vec4& color) = 0;
virtual float GetFrameTime() = 0;
inline static API GetAPI() { return s_api; }
private:
static API s_api;
};
}
#endif

View File

@ -1,260 +0,0 @@
/*
Instrumentor.h
Instrumentation classes for generating JSON trace files for use in Google Chrome's tracer. Based entirely upon the work done by @TheCherno in his game engine series. See his Hazel
repository for more details.
To activate the profiling, switch the NAV_PROFILE macro to a value greater than 0. By default the instrumentaion is disabled (duh). Should only be used for development cases.
GWM -- May 2022
*/
#ifndef INSTRUMENTOR_H
#define INSTRUMENTOR_H
#include <algorithm>
#include <chrono>
#include <fstream>
#include <iomanip>
#include <string>
#include <thread>
#include <mutex>
#include <sstream>
#include "Navigator/Core/Logger.h"
#include "Timer.h"
namespace Navigator {
using FloatingPointMicroseconds = std::chrono::duration<double, std::micro>;
struct ProfileResult
{
std::string name;
FloatingPointMicroseconds start;
std::chrono::microseconds elapsedTime;
std::thread::id threadID;
};
struct InstrumentationSession
{
std::string name;
};
class Instrumentor
{
public:
Instrumentor(const Instrumentor&) = delete;
Instrumentor(Instrumentor&&) = delete;
void BeginSession(const std::string& name, const std::string filename="inst_results.json")
{
std::scoped_lock<std::mutex> guard(m_instMutex);
if (m_session)
{
/*
* Can only ever have one session rolling. If for whatever reason attempt to open second, handle gracefully
*/
if (Logger::GetLogger()) //Ensure logger exists
{
NAV_ERROR("Attempting to create new instr. session ({0}) while session ({1}) is running! Setting to session {0}", name, m_session->name);
}
InternalEndSession();
}
m_outputFile.open(filename);
if (m_outputFile.is_open())
{
m_session = new InstrumentationSession({ name });
WriteHeader();
}
else if(Logger::GetLogger())
{
NAV_ERROR("Unable to open instr. output file named {0}!", filename);
}
}
void EndSession()
{
std::scoped_lock<std::mutex> guard(m_instMutex);
InternalEndSession();
}
void WriteProfile(const ProfileResult& result)
{
std::scoped_lock<std::mutex> guard(m_instMutex);
std::stringstream json_string;
json_string << std::setprecision(3) << std::fixed;
json_string << ",{";
json_string << "\"cat\":\"function\",";
json_string << "\"dur\":" << (result.elapsedTime.count()) << ',';
json_string << "\"name\":\"" << result.name << "\",";
json_string << "\"ph\":\"X\",";
json_string << "\"pid\":0,";
json_string << "\"tid\":" << result.threadID << ",";
json_string << "\"ts\":" << result.start.count();
json_string << "}";
if (m_session)
{
m_outputFile << json_string.str();
m_outputFile.flush();
}
}
static Instrumentor& Get()
{
static Instrumentor s_instance;
return s_instance;
}
private:
Instrumentor() :
m_session(nullptr)
{
}
~Instrumentor()
{
EndSession();
}
void InternalEndSession()
{
if (m_session)
{
WriteFooter();
m_outputFile.close();
delete m_session;
m_session = nullptr;
}
}
void WriteHeader()
{
m_outputFile << "{\"otherData\": {},\"traceEvents\":[{}";
m_outputFile.flush();
}
void WriteFooter()
{
m_outputFile << "]}";
m_outputFile.flush();
}
std::mutex m_instMutex;
InstrumentationSession* m_session;
std::ofstream m_outputFile;
};
class InstrumentationTimer
{
public:
InstrumentationTimer(const char* name) :
m_name(name), m_stopped(false)
{
m_startTime = Clock::now();
}
~InstrumentationTimer()
{
if (!m_stopped)
Stop();
}
void Stop()
{
auto stopTime = Clock::now();
FloatingPointMicroseconds start = FloatingPointMicroseconds((m_startTime).time_since_epoch());
auto elapsedTime = std::chrono::time_point_cast<std::chrono::microseconds>(stopTime).time_since_epoch() - std::chrono::time_point_cast<std::chrono::microseconds>(m_startTime).time_since_epoch();
m_stopped = true;
Instrumentor::Get().WriteProfile({ m_name, start, elapsedTime, std::this_thread::get_id() });
}
private:
using Time = std::chrono::steady_clock::time_point;
using Clock = std::chrono::steady_clock;
const char* m_name;
Time m_startTime;
bool m_stopped;
};
namespace InstrumentorUtils {
template<size_t N>
struct ChangeResult
{
char data[N];
};
template<size_t N, size_t M>
constexpr auto CleanupOutputString(const char(&expr)[N], const char(&remove)[M])
{
ChangeResult<N> result = {};
size_t srcIndex = 0;
size_t destIndex = 0;
while (srcIndex < N)
{
size_t matchIndex = 0;
while (matchIndex < M - 1 && srcIndex + matchIndex < N - 1 && expr[srcIndex + matchIndex] == remove[matchIndex])
matchIndex++;
if (matchIndex == M - 1)
srcIndex += matchIndex;
result.data[destIndex++] = expr[srcIndex] == '"' ? '\'' : expr[srcIndex];
srcIndex++;
}
return result;
}
}
}
//Macros for ease of use
#define NAV_PROFILE 0
#if NAV_PROFILE
//Attempt to resolve function signature with precompiler
#if defined(__GNUC__) || (defined(__MWERKS__) && (__MWERKS__ >= 0x3000)) || (defined(__ICC) && (__ICC >= 600)) || defined(__ghs__)
#define NAV_FUNC_SIG __PRETTY_FUNCTION__
#elif defined(__DMC__) && (__DMC__ >= 0x810)
#define NAV_FUNC_SIG __PRETTY_FUNCTION__
#elif (defined(__FUNCSIG__) || (_MSC_VER))
#define NAV_FUNC_SIG __FUNCSIG__
#elif (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)) || (defined(__IBMCPP__) && (__IBMCPP__ >= 500))
#define NAV_FUNC_SIG __FUNCTION__
#elif defined(__BORLANDC__) && (__BORLANDC__ >= 0x550)
#define NAV_FUNC_SIG __FUNC__
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)
#define NAV_FUNC_SIG __func__
#elif defined(__cplusplus) && (__cplusplus >= 201103)
#define NAV_FUNC_SIG __func__
#else
#define NAV_FUNC_SIG "NAV_FUNC_SIG unknown!"
#endif
#define NAV_PROFILE_BEGIN_SESSION(name, filename) ::Navigator::Instrumentor::Get().BeginSession(name, filename);
#define NAV_PROFILE_END_SESSION() ::Navigator::Instrumentor::Get().EndSession();
#define NAV_PROFILE_SCOPE_LINE2(name, line) constexpr auto fixedName##line = ::Navigator::InstrumentorUtils::CleanupOutputString(name, "__cdecl ");\
::Navigator::InstrumentationTimer timer##line(fixedName##line.data)
#define NAV_PROFILE_SCOPE_LINE(name, line) NAV_PROFILE_SCOPE_LINE2(name, line)
#define NAV_PROFILE_SCOPE(name) NAV_PROFILE_SCOPE_LINE(name, __LINE__)
#define NAV_PROFILE_FUNCTION() NAV_PROFILE_SCOPE(NAV_FUNC_SIG)
#else
#define NAV_PROFILE_BEGIN_SESSION(name, filename)
#define NAV_PROFILE_END_SESSION()
#define NAV_PROFILE_SCOPE_LINE2(name, line)
#define NAV_PROFILE_SCOPE_LINE(name, line)
#define NAV_PROFILE_SCOPE(name)
#define NAV_PROFILE_FUNCTION()
#endif
#endif

View File

@ -1,85 +0,0 @@
/*
TCPClient.h
Navigator's TCP client rep using asio. Contains very basic ability to read and write from a tcp socket in a non-blocking, synchronous method.
See asio docs for more details and examples on how to use.
GWM -- April 2022
Note: the write functionality has not been verified. Should be fine, but test before using.
*/
#include "TCPClient.h"
namespace Navigator {
TCPClient::TCPClient() :
m_socket(m_context)
{
m_readBuffer.resize(s_readBufferSize);
}
TCPClient::TCPClient(const std::string& host, const std::string& port) :
m_socket(m_context)
{
m_readBuffer.resize(s_readBufferSize);
Connect(host, port);
}
TCPClient::~TCPClient()
{
Close();
}
void TCPClient::Connect(const std::string& host, const std::string& port)
{
try
{
asio::ip::tcp::resolver resolver(m_context);
auto end_points = resolver.resolve(host, port);
asio::connect(m_socket, end_points);
m_socket.non_blocking(true); //Set the connection as non-blocking
}
catch (asio::system_error& e)
{
NAV_ERROR("Unable to connect to remote port for TCPClient! Error Code: {0}", e.what());
}
}
std::vector<char> TCPClient::Read()
{
asio::error_code code;
size_t length = m_socket.read_some(asio::buffer(m_readBuffer, m_readBuffer.size()), code);
if (code == asio::error::eof)
{
NAV_WARN("Server has closed connection. Closing the TCPClient");
Close();
}
else if (code && code != asio::error::would_block)
{
NAV_ERROR("TCPClient::Read recieved unexpected error from host. Error message: {0}", code.message());
NAV_WARN("Closing the socket.");
Close();
}
return std::vector<char>(m_readBuffer.begin(), m_readBuffer.begin()+length);
}
//untested, not currently used.
size_t TCPClient::Write(const std::vector<char>& data)
{
asio::error_code code;
m_writeBuffer = data;
size_t length = m_socket.write_some(asio::buffer(m_writeBuffer, m_writeBuffer.size()), code);
if (code == asio::error::eof)
{
NAV_WARN("Server has closed connection. Closing the TCPClient.");
Close();
}
else if(code && code != asio::error::would_block)
{
NAV_ERROR("TCPClient::Write recieved unexpected error from host. Error message: {0}", code.message());
NAV_WARN("Closing the socket");
Close();
}
return length;
}
}

View File

@ -1,41 +0,0 @@
/*
TCPClient.h
Navigator's TCP client rep using asio. Contains very basic ability to read and write from a tcp socket in a non-blocking, synchronous method.
See asio docs for more details and examples on how to use.
GWM -- April 2022
Note: the write functionality has not been verified. Should be fine, but test before using.
*/
#ifndef TCPCLIENT_H
#define TCPCLIENT_H
#include <asio.hpp>
namespace Navigator {
class TCPClient
{
public:
TCPClient();
TCPClient(const std::string& host, const std::string& port);
~TCPClient();
void Connect(const std::string& host, const std::string& port);
std::vector<char> Read();
size_t Write(const std::vector<char>& data);
inline void Close() { if(IsOpen()) m_socket.close(); }
inline bool IsOpen() { return m_socket.is_open(); }
private:
std::vector<char> m_readBuffer;
std::vector<char> m_writeBuffer;
static constexpr size_t s_readBufferSize = 24000; // reserve some space for the read buffer
asio::io_context m_context;
asio::ip::tcp::socket m_socket;
};
}
#endif

View File

@ -1,323 +0,0 @@
/*
TestServerLayer.h
This set of classes represents a test layer containing a TCP server and client connection class. This is only to be used as a way to debug
online data sources. You can have this server post data to a port on a localhost and then have the analysis thread connect to that port and analyze the
imitated data. Default setup is for an OnlineCompassSource, but of course this could be easily modified for your test of choice. For more details,
see the asio Tutorials, specifically the asynchronous daytime server.
Here we use async methods, as we do not want the whole project to be hanging on creating a succesfull client/server connection. (Also cause it was fun
to learn)
GWM -- April 2022
NOTE: There are NO security features on this server/connection. Please use with care on a protected network/firewalled machine.
*/
#include "TestServerLayer.h"
namespace Navigator {
/* TCPConnection */
TCPConnection::TCPConnection(asio::io_context& context) :
m_socket(context), m_buffer(36)
{
}
//This function is kinda misnamed in our scheme. Should be Write to make more clear.
void TCPConnection::Start()
{
CreateBinaryBuffer(); //Generate Buffer
//CreateBinaryBufferFragmented(); //Generate fragmented buffer
//Actually write the buffer to the socket. Use std::bind to set a callback function for error handling or any other server side actions
asio::async_write(m_socket, asio::buffer(m_buffer, m_buffer.size()),
std::bind(&TCPConnection::HandleWrite, this, std::placeholders::_1, std::placeholders::_2));
}
//Server-side connection actions upon attempting write, only on for debugging
void TCPConnection::HandleWrite(const asio::error_code& ec, size_t bytes)
{
/*
static int i = 0;
if(!ec)
NAV_INFO("Number written: {0}", ++i);
*/
//NAV_INFO("Writer result: Asio Error -- {0} Amount transferred={1}", ec.message(), bytes);
}
/*
Create C-style binary buffer from the data struct. This is to mimic the raw data source,
which will have no padding, or any other normal struct related features (and note that the intrisic
ordering from compass ensures that padding would be placed)
*/
void TCPConnection::CreateBinaryBuffer()
{
m_hit.board = 8;
m_hit.channel = 1;
m_hit.timestamp = m_hit.timestamp + s_timestep;
m_hit.energy = 512;
m_hit.energyShort = 0;
m_hit.flags = 0;
m_hit.Ns = 0;
char* data_pointer;
int buffer_position = 0;
data_pointer = (char*)&m_hit.board;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.channel;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.timestamp;
for (int i = 0; i < 8; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.energy;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.energyShort;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.flags;
for (int i = 0; i < 4; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.Ns;
for (int i = 0; i < 4; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
}
/*
Create C-style binary buffer from the data struct. This is to mimic the raw data source,
which will have no padding, or any other normal struct related features (and note that the intrisic
ordering from compass ensures that padding would be placed). Here we also fragment a hit (split over 2 buffers)
to test ability to handle real life conditions
Note -- as implemented highlights one issue with CAEN CoMPASS sources. No headers are left in the stream,
so one must be CERTAIN to attach Navigator to the socket before running, otherwise fragments could lead to
scrambled unpacking order. (i.e. sometimes this will work for the test and sometimes it won't)
*/
void TCPConnection::CreateBinaryBufferFragmented()
{
static std::atomic<bool> even = true;
m_hit.board = 8;
m_hit.channel = 1;
m_hit.timestamp = m_hit.timestamp + s_timestep;
m_hit.energy = 512;
m_hit.energyShort = 0;
m_hit.flags = 0;
m_hit.Ns = 0;
char* data_pointer;
int buffer_position = 0;
if (even)
{
data_pointer = (char*)&m_hit.board;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.channel;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.timestamp;
for (int i = 0; i < 8; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.energy;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.energyShort;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.flags;
for (int i = 0; i < 4; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.board;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.channel;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
m_hit.timestamp += s_timestep;
data_pointer = (char*)&m_hit.timestamp;
for (int i = 0; i < 8; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
even = false;
}
else
{
data_pointer = (char*)&m_hit.energy;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.energyShort;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.flags;
for (int i = 0; i < 4; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.board;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.channel;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.timestamp;
for (int i = 0; i < 8; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.energy;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.energyShort;
for (int i = 0; i < 2; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
data_pointer = (char*)&m_hit.flags;
for (int i = 0; i < 4; i++)
{
m_buffer[buffer_position] = *(data_pointer + i);
buffer_position++;
}
}
}
/* TCPServer */
TCPServer::TCPServer(asio::io_context& context) :
m_contextRef(context), m_acceptor(context, asio::ip::tcp::endpoint(asio::ip::tcp::v4(), 51489)), m_isAccepted(false)
{
}
//Attempt to create a connection
std::shared_ptr<TCPConnection> TCPServer::StartAccept()
{
std::shared_ptr<TCPConnection> new_connection = TCPConnection::CreateConnection(m_contextRef);
m_acceptor.async_accept(new_connection->Socket(), std::bind(&TCPServer::HandleAccept, this, new_connection, std::placeholders::_1));
return new_connection;
}
//If connection successful, attempt to write as a test.
void TCPServer::HandleAccept(std::shared_ptr<TCPConnection> connection, const asio::error_code& ec)
{
if (!ec)
{
m_isAccepted = true;
connection->Start();
}
else
{
m_isAccepted = false;
NAV_INFO("TCPServer HandleAccept found Error: {0}", ec.message());
}
StartAccept();
}
/* TestServerLayer */
TestServerLayer::TestServerLayer() :
Layer("TestServer"), m_server(nullptr), m_connection(nullptr)
{
}
TestServerLayer::~TestServerLayer()
{
if (m_server)
delete m_server;
}
//Create a server, get the connection, poll actions
void TestServerLayer::OnAttach()
{
try
{
m_server = new TCPServer(m_context);
m_connection = m_server->StartAccept();
m_context.poll();
}
catch (std::exception& e)
{
NAV_INFO("ServerLayer Error: {0}", e.what());
}
}
void TestServerLayer::OnDetach()
{
}
//Tell to write, then poll actions
void TestServerLayer::OnUpdate(Timestep& step)
{
m_connection->Start();
m_context.poll();
}
}

View File

@ -1,78 +0,0 @@
/*
TestServerLayer.h
This set of classes represents a test layer containing a TCP server and client connection class. This is only to be used as a way to debug
online data sources. You can have this server post data to a port on a localhost and then have the analysis thread connect to that port and analyze the
imitated data. Default setup is for an OnlineCompassSource, but of course this could be easily modified for your test of choice. For more details,
see the asio Tutorials, specifically the asynchronous daytime server.
Here we use async methods, as we do not want the whole project to be hanging on creating a succesfull client/server connection. (Also cause it was fun
to learn)
GWM -- April 2022
NOTE: There are NO security features on this server/connection. Please use with care on a protected network/firewalled machine.
*/
#ifndef TEST_SERVER_LAYER_H
#define TEST_SERVER_LAYER_H
#include "Navigator/Core/Layer.h"
#include "Navigator/Physics/Caen/CompassHit.h"
#include "asio.hpp"
namespace Navigator {
/*Server-side TCP Connection to the open port*/
class TCPConnection
{
public:
TCPConnection(asio::io_context& context);
inline static std::shared_ptr<TCPConnection> CreateConnection(asio::io_context& context) { return std::make_shared<TCPConnection>(context); }
inline asio::ip::tcp::socket& Socket() { return m_socket; }
void Start();
private:
void HandleWrite(const asio::error_code& ec, size_t bytes);
void CreateBinaryBuffer();
void CreateBinaryBufferFragmented();
asio::ip::tcp::socket m_socket;
std::vector<char> m_buffer;
CompassHit m_hit;
static constexpr uint64_t s_timestep = 2000000;
};
/*Server itself*/
class TCPServer
{
public:
TCPServer(asio::io_context& context);
inline bool IsAccepted() { return m_isAccepted; }
std::shared_ptr<TCPConnection> StartAccept();
private:
void HandleAccept(std::shared_ptr<TCPConnection> connection, const asio::error_code& error);
asio::io_context& m_contextRef;
asio::ip::tcp::acceptor m_acceptor;
bool m_isAccepted;
};
class TestServerLayer : public Layer
{
public:
TestServerLayer();
virtual ~TestServerLayer();
virtual void OnAttach() override;
virtual void OnDetach() override;
virtual void OnUpdate(Timestep& step) override;
private:
asio::io_context m_context;
TCPServer* m_server;
std::shared_ptr<TCPConnection> m_connection;
};
}
#endif

View File

@ -1,46 +0,0 @@
#ifndef TIMER_H
#define TIMER_H
#include <chrono>
namespace Navigator {
class Timer
{
public:
Timer(const char* name) :
m_name(name), m_stopped(false)
{
m_startTime = Clock::now();
}
~Timer()
{
if (!m_stopped)
Stop();
}
void Stop()
{
auto stopTime = Clock::now();
int64_t start = std::chrono::time_point_cast<std::chrono::microseconds>(m_startTime).time_since_epoch().count();
int64_t stop = std::chrono::time_point_cast<std::chrono::microseconds>(stopTime).time_since_epoch().count();
float duration = (stop - start)*0.001f;
m_stopped = true;
NAV_INFO("{1} -- Duration: {0} ms", m_name, duration);
}
private:
using Time = std::chrono::steady_clock::time_point;
using Clock = std::chrono::steady_clock;
const char* m_name;
Time m_startTime;
bool m_stopped;
};
}
#endif

View File

@ -1,41 +0,0 @@
/*
OpenGLContext.h
Implementation of OpenGL rendering context. Entirely based upon the work done by @TheCherno in his game engine series. See his content for more details.
GWM -- Feb 2022
*/
#include "OpenGLContext.h"
#include "GLFW/glfw3.h"
#include "glad/glad.h"
namespace Navigator {
OpenGLContext::OpenGLContext(GLFWwindow* window) :
m_windowHandle(window)
{
}
OpenGLContext::~OpenGLContext() {}
void OpenGLContext::Init()
{
NAV_PROFILE_FUNCTION();
glfwMakeContextCurrent(m_windowHandle);
int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
//Report graphics status
NAV_TRACE("Loaded OpenGL with glad Init status {0}", status);
NAV_INFO("Loaded OpenGL renderer");
NAV_INFO("Vendor: {0}", glGetString(GL_VENDOR));
NAV_INFO("Renderer: {0}", glGetString(GL_RENDERER));
NAV_INFO("Version: {0}", glGetString(GL_VERSION));
}
void OpenGLContext::SwapBuffers()
{
glfwSwapBuffers(m_windowHandle);
}
}

View File

@ -1,31 +0,0 @@
/*
OpenGLContext.h
Implementation of OpenGL rendering context. Entirely based upon the work done by @TheCherno in his game engine series. See his content for more details.
GWM -- Feb 2022
*/
#ifndef OPEGL_CONTEXT_H
#define OPEGL_CONTEXT_H
#include "Navigator/Core/NavCore.h"
#include "Navigator/Renderer/GraphicsContext.h"
struct GLFWwindow;
namespace Navigator {
class NAV_API OpenGLContext : public GraphicsContext
{
public:
OpenGLContext(GLFWwindow* window);
~OpenGLContext();
virtual void Init() override;
virtual void SwapBuffers() override;
private:
GLFWwindow* m_windowHandle;
};
}
#endif

View File

@ -1,29 +0,0 @@
/*
OpenGLRendererAPI.h
Implementation of OpenGL API in Navigator context. Note here only a few things exist. We don't need to implement much ourselves,
ImGui handles a lot of the heavy lifting for us. Based entirely upon @TheCherno's work in his game engine series.
GWM -- Feb 2022
*/
#include "OpenGLRendererAPI.h"
#include "glad/glad.h"
#include "GLFW/glfw3.h"
namespace Navigator {
void OpenGLRendererAPI::Clear()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void OpenGLRendererAPI::SetClearColor(const glm::vec4& color_array)
{
glClearColor(color_array[0], color_array[1], color_array[2], color_array[3]);
}
float OpenGLRendererAPI::GetFrameTime()
{
return (float)glfwGetTime();
}
}

View File

@ -1,26 +0,0 @@
/*
OpenGLRendererAPI.h
Implementation of OpenGL API in Navigator context. Note here only a few things exist. We don't need to implement much ourselves,
ImGui handles a lot of the heavy lifting for us. Based entirely upon @TheCherno's work in his game engine series.
GWM -- Feb 2022
*/
#ifndef OPENGL_RENDERER_API_H
#define OPENGL_RENDERER_API_H
#include "Navigator/Core/NavCore.h"
#include "Navigator/Renderer/RendererAPI.h"
namespace Navigator {
class NAV_API OpenGLRendererAPI : public RendererAPI
{
public:
virtual void Clear() override;
virtual void SetClearColor(const glm::vec4& color_array) override;
virtual float GetFrameTime() override;
};
}
#endif

View File

@ -1,180 +0,0 @@
/*
OpenGLWindow.h
Implementation of a window with OpenGL context. Not really OpenGL specific, other than in creation of GraphicsContext.
Bulk of creation can be used in any api/context (glfw compatible with Cocoa, X11, or Windows). Based entirely upon the
work of @TheCherno in his game engine series.
GWM -- Feb 2022
*/
#include "OpenGLWindow.h"
#include "OpenGLContext.h"
#include "Navigator/Core/NavCore.h"
#include "Navigator/Events/AppEvent.h"
#include "Navigator/Events/KeyEvent.h"
#include "Navigator/Events/MouseEvent.h"
namespace Navigator {
static bool s_glfwInitialized = false;
static void GLFWErrorCallback(int error, const char* description)
{
NAV_ERROR("GLFW Error Code {0}: {1}", error, description);
}
Window* Window::Create(const WindowProperties& props) { return new OpenGLWindow(props); }
OpenGLWindow::OpenGLWindow(const WindowProperties& props)
{
Init(props);
}
OpenGLWindow::~OpenGLWindow()
{
Shutdown();
}
void OpenGLWindow::Init(const WindowProperties& props)
{
NAV_PROFILE_FUNCTION();
m_data.width = props.width;
m_data.height = props.height;
m_data.name = props.name;
NAV_INFO("Creating OpenGLWindow with height {0} width {1} and name {2}", m_data.height, m_data.width, m_data.name);
if(!s_glfwInitialized)
{
int passed = glfwInit();
NAV_INFO("Initializing GLFW ... Returned value {0}", passed);
glfwSetErrorCallback(GLFWErrorCallback);
}
//Apple specific. OpenGL is technically deprecated, so a little extra work to force the correct version
#ifdef __APPLE__
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE,GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT,true);
#endif
m_window = glfwCreateWindow((int)m_data.width, (int)m_data.height, m_data.name.c_str(), nullptr, nullptr);
m_context = new OpenGLContext(m_window); //This is the only seriously OpenGL specific code
m_context->Init();
glfwSetWindowUserPointer(m_window, &m_data);
SetVSync(true);
//Set all of the callback functions for the window.
glfwSetWindowSizeCallback(m_window, [](GLFWwindow* window, int width, int height)
{
Data& data = *(Data*) glfwGetWindowUserPointer(window);
data.width = width;
data.height = height;
WindowResizeEvent event(width, height);
data.event_callback_func(event);
});
glfwSetWindowCloseCallback(m_window, [](GLFWwindow* window)
{
Data& data = *(Data*) glfwGetWindowUserPointer(window);
WindowCloseEvent event;
data.event_callback_func(event);
});
glfwSetKeyCallback(m_window, [](GLFWwindow* window, int key, int scancode, int action, int mods)
{
Data& data = *(Data*) glfwGetWindowUserPointer(window);
switch(action)
{
case GLFW_PRESS:
{
KeyPressedEvent event(key, 0);
data.event_callback_func(event);
break;
}
case GLFW_RELEASE:
{
KeyReleasedEvent event(key);
data.event_callback_func(event);
break;
}
case GLFW_REPEAT:
{
//GLFW doesnt have a hold count, so here we just pass 1 to indicate a hold is happening.
KeyPressedEvent event(key, 1);
data.event_callback_func(event);
break;
}
}
});
glfwSetCharCallback(m_window, [](GLFWwindow* window, unsigned int character)
{
Data& data = *(Data*) glfwGetWindowUserPointer(window);
KeyTypedEvent event(character);
data.event_callback_func(event);
});
glfwSetMouseButtonCallback(m_window, [](GLFWwindow* window, int button, int action, int mods)
{
Data& data = *(Data*) glfwGetWindowUserPointer(window);
switch(action)
{
case GLFW_PRESS:
{
MouseButtonPressedEvent event(button);
data.event_callback_func(event);
}
case GLFW_RELEASE:
{
MouseButtonReleasedEvent event(button);
data.event_callback_func(event);
}
}
});
glfwSetScrollCallback(m_window, [](GLFWwindow* window, double xoffset, double yoffset)
{
Data& data = *(Data*) glfwGetWindowUserPointer(window);
MouseScrolledEvent event((float)xoffset, (float)yoffset);
data.event_callback_func(event);
});
glfwSetCursorPosCallback(m_window, [](GLFWwindow* window, double xpos, double ypos)
{
Data& data = *(Data*) glfwGetWindowUserPointer(window);
MouseMovedEvent event((float)xpos, (float)ypos);
data.event_callback_func(event);
});
}
void OpenGLWindow::Shutdown()
{
glfwDestroyWindow(m_window);
}
void OpenGLWindow::OnUpdate()
{
glfwPollEvents();
m_context->SwapBuffers();
}
void OpenGLWindow::SetVSync(bool enabled)
{
if(enabled)
{
glfwSwapInterval(1);
}
else
{
glfwSwapInterval(0);
}
m_data.vsyncFlag = enabled;
}
bool OpenGLWindow::IsVSync() const
{
return m_data.vsyncFlag;
}
}

View File

@ -1,56 +0,0 @@
/*
OpenGLWindow.h
Implementation of a window with OpenGL context. Not really OpenGL specific, other than in creation of GraphicsContext.
Bulk of creation can be used in any api/context (glfw compatible with Cocoa, X11, or Windows). Based entirely upon the
work of @TheCherno in his game engine series.
GWM -- Feb 2022
*/
#ifndef OPENGL_WINDOW_H
#define OPENGL_WINDOW_H
#include "Navigator/Core/NavCore.h"
#include "Navigator/Core/Window.h"
#include "Navigator/Renderer/GraphicsContext.h"
#include "GLFW/glfw3.h"
namespace Navigator {
class NAV_API OpenGLWindow : public Window
{
public:
OpenGLWindow(const WindowProperties& props);
virtual ~OpenGLWindow();
void OnUpdate() override;
inline void SetEventCallback(const EventCallbackFunc& function) override { m_data.event_callback_func = function; }
inline unsigned int GetWidth() const override { return m_data.width; }
inline unsigned int GetHeight() const override { return m_data.height; }
inline std::string GetName() const override { return m_data.name; }
void SetVSync(bool enabled) override;
bool IsVSync() const override;
inline virtual void* GetNativeWindow() const override { return m_window; }
private:
virtual void Init(const WindowProperties& props);
virtual void Shutdown();
GraphicsContext* m_context;
GLFWwindow* m_window;
struct Data
{
int height, width;
std::string name;
bool vsyncFlag;
EventCallbackFunc event_callback_func;
};
Data m_data;
};
}
#endif

View File

@ -1 +0,0 @@
#include "navpch.h"

View File

@ -1,22 +0,0 @@
#ifndef NAVPCH_H
#define NAVPCH_H
#include <iostream>
#include <fstream>
#include <memory>
#include <utility>
#include <algorithm>
#include <functional>
#include <string>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#include <cstdint>
#include "Navigator/Core/Logger.h"
#include "Navigator/Utils/Instrumentor.h"
#endif

@ -1 +0,0 @@
Subproject commit 7d6ff1f4ba51e7a2b142be39457768abece1549c

@ -1 +0,0 @@
Subproject commit f0a1e1c7c0387ad16358c81eb52528f190df625c

View File

@ -1,311 +0,0 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
project "GLAD"
kind "StaticLib"
language "C"
staticruntime "on"
targetdir ("bin/" .. outputdir .. "/%{prj.name}")
objdir ("bin-int/" .. outputdir .. "/%{prj.name}")
files {
"include/glad/glad.h",
"include/KHR/khrplatform.h",
"src/glad.c"
}
includedirs {
"include"
}
filter "system:windows"
systemversion "latest"
filter "system:linux or macosx"
systemversion "latest"
pic "on"
filter "system:macosx"
systemversion "latest"
sysincludedirs { "include" }
filter "configurations:Debug"
runtime "Debug"
symbols "on"
filter "configurations:Release"
runtime "Release"
optimize "on"

File diff suppressed because it is too large Load Diff

@ -1 +0,0 @@
Subproject commit facec25e0e1f1ef91ad8db3c5327d518f49901fd

@ -1 +0,0 @@
Subproject commit cc98465e3508535ba8c7f6208df934c156a018dc

@ -1 +0,0 @@
Subproject commit cbe4e3cd7c9789b514eeaf9a65d8f674d984be3e

@ -1 +0,0 @@
Subproject commit d34204b5a762dcef600440f8d7502633626cd217

@ -1 +0,0 @@
Subproject commit 298a200f69d66114adde2d5d8ad34f2cce5a5b69