1
0
Fork 0
mirror of https://github.com/gwm17/Specter.git synced 2024-11-26 12:18:51 -05:00

Started commenting code properly. Core done.

This commit is contained in:
Gordon McCann 2022-02-27 22:20:38 -05:00
parent b805cf9292
commit 060183e26f
21 changed files with 330 additions and 20 deletions

View File

@ -1,3 +1,10 @@
/*
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 #ifndef NAVIGATOR_H
#define NAVIGATOR_H #define NAVIGATOR_H

View File

@ -1,3 +1,13 @@
/*
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 "Application.h"
#include "Renderer/RenderCommand.h" #include "Renderer/RenderCommand.h"
#include "Editor/EditorLayer.h" #include "Editor/EditorLayer.h"
@ -12,12 +22,12 @@ namespace Navigator {
s_instance = this; s_instance = this;
m_window = std::unique_ptr<Window>(Window::Create()); m_window = std::unique_ptr<Window>(Window::Create());
m_window->SetEventCallback(BIND_EVENT_FUNCTION(Application::OnEvent)); m_window->SetEventCallback(BIND_EVENT_FUNCTION(Application::OnEvent)); //Allow window to pass events back
m_physicsLayer = new PhysicsLayer(); m_physicsLayer = new PhysicsLayer();
PushLayer(m_physicsLayer); PushLayer(m_physicsLayer);
EditorLayer* editor = new EditorLayer(); //memory handled by layer stack EditorLayer* editor = new EditorLayer(); //memory handled by layer stack
editor->SetEventCallbackFunc(BIND_EVENT_FUNCTION(Application::OnEvent)); editor->SetEventCallbackFunc(BIND_EVENT_FUNCTION(Application::OnEvent)); //Allow editor to pass events back
PushLayer(editor); PushLayer(editor);
m_imgui_layer = new ImGuiLayer(); m_imgui_layer = new ImGuiLayer();
PushOverlay(m_imgui_layer); PushOverlay(m_imgui_layer);

View File

@ -1,3 +1,13 @@
/*
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 #ifndef APPLICATION_H
#define APPLICATION_H #define APPLICATION_H
@ -41,11 +51,17 @@ namespace Navigator {
PhysicsLayer* m_physicsLayer; PhysicsLayer* m_physicsLayer;
bool m_runFlag; bool m_runFlag;
//Dark grey background
glm::vec4 m_bckgnd_color = {0.1f, 0.1f, 0.1f, 1.0f}; glm::vec4 m_bckgnd_color = {0.1f, 0.1f, 0.1f, 1.0f};
static Application* s_instance; 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(); Application* CreateApplication();
} }

View File

@ -1,3 +1,19 @@
/*
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.
CutParams 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 CutParams, 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 "Cut.h"
#include "implot.h" #include "implot.h"

View File

@ -1,11 +1,25 @@
#ifndef CUT_MAP_H /*
#define CUT_MAP_H 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.
CutParams 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 CutParams, 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 "NavCore.h"
#include "imgui.h" #include "imgui.h"
#include <thread>
namespace Navigator { namespace Navigator {
struct NAV_API CutParams struct NAV_API CutParams
@ -78,7 +92,6 @@ namespace Navigator {
private: private:
std::vector<double> m_xpoints; std::vector<double> m_xpoints;
std::vector<double> m_ypoints; std::vector<double> m_ypoints;
const ImVec4 colorVec = {1.0, 0.0, 0.0, 0.5};
}; };
} }

View File

@ -1,3 +1,26 @@
/*
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.
HistogramParameters 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 HistogramParameters, 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 "Histogram.h"
#include "implot.h" #include "implot.h"
@ -64,11 +87,13 @@ namespace Navigator {
m_binCounts[i] = 0; 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) StatResults Histogram1D::AnalyzeRegion(double x_min, double x_max, double y_min, double y_max)
{ {
int bin_min, bin_max; int bin_min, bin_max;
StatResults results; StatResults results;
//We clamp to the boundaries of the histogram
if (x_min <= m_params.min_x) if (x_min <= m_params.min_x)
bin_min = 0; bin_min = 0;
else else
@ -96,6 +121,7 @@ namespace Navigator {
/* /*
2D Histogram class 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 HistogramParameters& params) : Histogram2D::Histogram2D(const HistogramParameters& params) :
Histogram(params) Histogram(params)
@ -144,6 +170,12 @@ namespace Navigator {
} }
//Can only be used within an ImGui / ImPlot context!! //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() void Histogram2D::Draw()
{ {
ImPlot::SetupAxes(m_params.x_par.c_str(), m_params.y_par.c_str()); ImPlot::SetupAxes(m_params.x_par.c_str(), m_params.y_par.c_str());
@ -167,6 +199,7 @@ namespace Navigator {
StatResults results; StatResults results;
//We clamp to the boundaries of the histogram
if (x_min <= m_params.min_x) if (x_min <= m_params.min_x)
xbin_min = 0; xbin_min = 0;
else else

View File

@ -1,3 +1,26 @@
/*
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.
HistogramParameters 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 HistogramParameters, 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 #ifndef HISTOGRAM_H
#define HISTOGRAM_H #define HISTOGRAM_H

View File

@ -1,3 +1,9 @@
/*
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" #include "Layer.h"
namespace Navigator { namespace Navigator {

View File

@ -1,3 +1,9 @@
/*
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 #ifndef LAYER_H
#define LAYER_H #define LAYER_H

View File

@ -1,3 +1,12 @@
/*
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" #include "LayerStack.h"
namespace Navigator { namespace Navigator {

View File

@ -1,3 +1,12 @@
/*
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 #ifndef LAYER_STACK_H
#define LAYER_STACK_H #define LAYER_STACK_H
@ -17,11 +26,12 @@ namespace Navigator {
void PushOverlay(Layer* layer); void PushOverlay(Layer* layer);
void PopOverlay(Layer* layer); void PopOverlay(Layer* layer);
//helpers for iterator for loops
std::vector<Layer*>::iterator begin() { return m_stack.begin(); } std::vector<Layer*>::iterator begin() { return m_stack.begin(); }
std::vector<Layer*>::iterator end() { return m_stack.end(); } std::vector<Layer*>::iterator end() { return m_stack.end(); }
private: private:
std::vector<Layer*> m_stack; //These layers are owned by the LayerStack! std::vector<Layer*> m_stack; //These layers are owned by the LayerStack!
unsigned int m_insertIndex=0; unsigned int m_insertIndex=0; //used to keep track of where to put layers vs. overlays.
}; };
} }

View File

@ -1,3 +1,13 @@
/*
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 "Logger.h"
#include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/sinks/stdout_color_sinks.h"

View File

@ -1,3 +1,13 @@
/*
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 #ifndef LOGGER_H
#define LOGGER_H #define LOGGER_H
@ -22,6 +32,7 @@ namespace Navigator {
} }
//Macros for clean code. Different logging levels.
#define NAV_CRITICAL(...) ::Navigator::Logger::GetLogger()->critical(__VA_ARGS__) #define NAV_CRITICAL(...) ::Navigator::Logger::GetLogger()->critical(__VA_ARGS__)
#define NAV_WARN(...) ::Navigator::Logger::GetLogger()->warn(__VA_ARGS__) #define NAV_WARN(...) ::Navigator::Logger::GetLogger()->warn(__VA_ARGS__)
#define NAV_ERROR(...) ::Navigator::Logger::GetLogger()->error(__VA_ARGS__) #define NAV_ERROR(...) ::Navigator::Logger::GetLogger()->error(__VA_ARGS__)

View File

@ -1,14 +1,17 @@
#ifndef NAVCORE_H #ifndef NAVCORE_H
#define NAVCORE_H #define NAVCORE_H
/*
Have to handle Windows dll import/export behavior. Otherwise, NAV_API
is just an empty expression.
*/
#ifdef NAV_WINDOWS #ifdef NAV_WINDOWS
#ifdef NAV_EXPORT #ifdef NAV_EXPORT
#define NAV_API __declspec(dllexport) #define NAV_API __declspec(dllexport)
#else #else
#define NAV_API __declspec(dllimport) #define NAV_API __declspec(dllimport)
#endif #endif
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning (disable: 4127) // condition expression is constant #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: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when NAV_API is set to__declspec(dllexport)
@ -29,6 +32,7 @@
//Bit field setter //Bit field setter
#define BIT(x) (1<<x) #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)...); } #define BIND_EVENT_FUNCTION(x) [this](auto&&... args) -> decltype(auto) { return this->x(std::forward<decltype(args)>(args)...); }
#endif #endif

View File

@ -1,3 +1,27 @@
/*
Parameter.cpp
Contains two data related structures, ParameterData and NavParameter. 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). NavParameter 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:
- NavParameter contains no guarantee of thread safety. THIS IS BY DESIGN. NavParameters 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 NavParameter outside of the AnalysisStage context.
- NavParameters must be bound to the active SpectrumManager. This is done using the BindParameter function of the manager; NavParameters are keyed based on their name string.
If two NavParameters 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 NavParameter named "x1" where x1 is set to 1.0, and then have a later stage called LaterStage with another NavParameter named x1, it implicitly has the value 1.0
due to it being set in the previous stage.
- Each NavParameter 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" #include "Parameter.h"
namespace Navigator { namespace Navigator {

View File

@ -1,17 +1,42 @@
/*
Parameter.h
Contains two data related structures, ParameterData and NavParameter. 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). NavParameter 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:
- NavParameter contains no guarantee of thread safety. THIS IS BY DESIGN. NavParameters 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 NavParameter outside of the AnalysisStage context.
- NavParameters must be bound to the active SpectrumManager. This is done using the BindParameter function of the manager; NavParameters are keyed based on their name string.
If two NavParameters 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 NavParameter named "x1" where x1 is set to 1.0, and then have a later stage called LaterStage with another NavParameter named x1, it implicitly has the value 1.0
due to it being set in the previous stage.
- Each NavParameter 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_MAP_H #ifndef PARAMETER_MAP_H
#define PARAMETER_MAP_H #define PARAMETER_MAP_H
#include "NavCore.h" #include "NavCore.h"
#include <thread>
namespace Navigator { namespace Navigator {
//Underlying data
struct NAV_API ParameterData struct NAV_API ParameterData
{ {
double value=0.0; double value=0.0;
bool validFlag=false; bool validFlag=false;
}; };
//Interface to parameter data
class NAV_API NavParameter class NAV_API NavParameter
{ {

View File

@ -1,3 +1,13 @@
/*
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 "SpectrumManager.h"
#include "implot.h" #include "implot.h"
@ -14,10 +24,12 @@ namespace Navigator {
{ {
} }
/*************Histogram Functions Begin*************/
void SpectrumManager::AddHistogram(const HistogramParameters& params) void SpectrumManager::AddHistogram(const HistogramParameters& params)
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
if (params.y_par == "None") if (params.y_par == "None") //Check dimensionality
m_histoMap[params.name].reset(new Histogram1D(params)); m_histoMap[params.name].reset(new Histogram1D(params));
else else
m_histoMap[params.name].reset(new Histogram2D(params)); m_histoMap[params.name].reset(new Histogram2D(params));
@ -45,6 +57,7 @@ namespace Navigator {
iter->second->AddCutToBeApplied(cutname); 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() void SpectrumManager::UpdateHistograms()
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -52,7 +65,7 @@ namespace Navigator {
for (auto& pair : m_histoMap) for (auto& pair : m_histoMap)
{ {
cutFlag = true; cutFlag = true;
for (auto& cutname : pair.second->GetParameters().cutsAppliedTo) for (auto& cutname : pair.second->GetParameters().cutsAppliedTo) //check the event against cuts
{ {
if (!IsInsideCut(cutname)) if (!IsInsideCut(cutname))
{ {
@ -101,7 +114,7 @@ namespace Navigator {
if (iter != m_histoMap.end()) if (iter != m_histoMap.end())
{ {
iter->second->Draw(); iter->second->Draw();
for (auto& cutname : iter->second->GetParameters().cutsDrawnUpon) for (auto& cutname : iter->second->GetParameters().cutsDrawnUpon) //Draw all cuts made upon the histogram
DrawCut(cutname); DrawCut(cutname);
} }
} }
@ -116,6 +129,7 @@ namespace Navigator {
return m_nullHistoResult; return m_nullHistoResult;
} }
//For 2D spectra, we want to allow zooming along the z-axis (color)
float* SpectrumManager::GetColorScaleRange(const std::string& name) float* SpectrumManager::GetColorScaleRange(const std::string& name)
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -140,6 +154,7 @@ namespace Navigator {
return std::vector<double>(); return std::vector<double>();
} }
//Pass through for stats
StatResults SpectrumManager::AnalyzeHistogramRegion(const std::string& name, const ImPlotRect& region) StatResults SpectrumManager::AnalyzeHistogramRegion(const std::string& name, const ImPlotRect& region)
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -150,6 +165,8 @@ namespace Navigator {
return StatResults(); 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<HistogramParameters> SpectrumManager::GetListOfHistograms() std::vector<HistogramParameters> SpectrumManager::GetListOfHistograms()
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -162,6 +179,12 @@ namespace Navigator {
return list; return list;
} }
/*************Histogram Functions End*************/
/*************Parameter Functions Begin*************/
//Bind a NavParameter instance to the manager. If the Parameter doesn't exist, make a new one, otherwise attach to extant memory
void SpectrumManager::BindParameter(NavParameter& param) void SpectrumManager::BindParameter(NavParameter& param)
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -174,6 +197,7 @@ namespace Navigator {
param.m_pdata = m_paramMap[param.GetName()]; param.m_pdata = m_paramMap[param.GetName()];
} }
//Once an analysis pass is done and histograms filled, reset all parameters
void SpectrumManager::InvalidateParameters() void SpectrumManager::InvalidateParameters()
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -184,6 +208,7 @@ namespace Navigator {
} }
} }
//Similar to GetListOfHistograms, see that documentation
std::vector<std::string> SpectrumManager::GetListOfParameters() std::vector<std::string> SpectrumManager::GetListOfParameters()
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -197,11 +222,15 @@ namespace Navigator {
return list; return list;
} }
/*************Parameter Functions End*************/
/*************Cut Functions Begin*************/
void SpectrumManager::RemoveCut(const std::string& name) void SpectrumManager::RemoveCut(const std::string& name)
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
m_cutMap.erase(name); m_cutMap.erase(name);
RemoveCutFromHistograms(name); RemoveCutFromHistograms(name); //Once a cut is gone, remove all references to it.
} }
std::vector<double> SpectrumManager::GetCutXPoints(const std::string& name) std::vector<double> SpectrumManager::GetCutXPoints(const std::string& name)
@ -224,6 +253,7 @@ namespace Navigator {
return null_result; return null_result;
} }
//Similar to GetListOfHistograms, see that documentation
std::vector<CutParams> SpectrumManager::GetListOfCuts() std::vector<CutParams> SpectrumManager::GetListOfCuts()
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -234,13 +264,15 @@ namespace Navigator {
return list; return list;
} }
/*************Cut Functions End*************/
/* /*
Private Functions Private Functions
Can only be called from within the SpectrumManager, therefore the lock should already have been aquired by Can only be called from within the SpectrumManager, therefore the lock should already have been aquired by
whatever parent function calls them whatever parent function calls them. No explicit synchronization.
*/ */
//private helper function; does not need thread-locked //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) void SpectrumManager::RemoveCutFromHistograms(const std::string& cutname)
{ {
for (auto& gram : m_histoMap) for (auto& gram : m_histoMap)
@ -263,6 +295,7 @@ namespace Navigator {
} }
} }
//Obv. only need to draw a cut if its parent histogram is drawn.
void SpectrumManager::DrawCut(const std::string& name) void SpectrumManager::DrawCut(const std::string& name)
{ {
auto iter = m_cutMap.find(name); auto iter = m_cutMap.find(name);
@ -270,6 +303,7 @@ namespace Navigator {
iter->second->Draw(); iter->second->Draw();
} }
//Check if event passes a cut. Only used when filling histograms.
bool SpectrumManager::IsInsideCut(const std::string& name) bool SpectrumManager::IsInsideCut(const std::string& name)
{ {
bool result = false; bool result = false;

View File

@ -1,3 +1,13 @@
/*
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 #ifndef SPECTRUM_MANAGER_H
#define SPECTRUM_MANAGER_H #define SPECTRUM_MANAGER_H
@ -19,6 +29,7 @@ namespace Navigator {
inline static SpectrumManager& GetInstance() { return *s_instance; } inline static SpectrumManager& GetInstance() { return *s_instance; }
//To clear all managed spectra. Note that Parameters are left untouched.
inline void RemoveAllSpectra() inline void RemoveAllSpectra()
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -26,6 +37,7 @@ namespace Navigator {
m_cutMap.clear(); m_cutMap.clear();
} }
/*Histogram Functions*/
void AddHistogram(const HistogramParameters& params); void AddHistogram(const HistogramParameters& params);
void RemoveHistogram(const std::string& name); void RemoveHistogram(const std::string& name);
void AddCutToHistogramDraw(const std::string& cutname, const std::string& histoname); void AddCutToHistogramDraw(const std::string& cutname, const std::string& histoname);
@ -39,11 +51,15 @@ namespace Navigator {
std::vector<double> GetBinData(const std::string& name); std::vector<double> GetBinData(const std::string& name);
StatResults AnalyzeHistogramRegion(const std::string& name, const ImPlotRect& region); StatResults AnalyzeHistogramRegion(const std::string& name, const ImPlotRect& region);
std::vector<HistogramParameters> GetListOfHistograms(); std::vector<HistogramParameters> GetListOfHistograms();
/********************/
/*Parameter Functions*/
void BindParameter(NavParameter& param); void BindParameter(NavParameter& param);
void InvalidateParameters(); void InvalidateParameters();
std::vector<std::string> GetListOfParameters(); std::vector<std::string> GetListOfParameters();
/********************/
/*Cut Functions*/
inline void AddCut(const CutParams& params, double min, double max) inline void AddCut(const CutParams& params, double min, double max)
{ {
std::lock_guard<std::mutex> guard(m_managerMutex); std::lock_guard<std::mutex> guard(m_managerMutex);
@ -58,21 +74,24 @@ namespace Navigator {
std::vector<double> GetCutXPoints(const std::string& name); std::vector<double> GetCutXPoints(const std::string& name);
std::vector<double> GetCutYPoints(const std::string& name); std::vector<double> GetCutYPoints(const std::string& name);
std::vector<CutParams> GetListOfCuts(); std::vector<CutParams> GetListOfCuts();
/**************/
private: private:
//Only used from within manager
void RemoveCutFromHistograms(const std::string& cutname); void RemoveCutFromHistograms(const std::string& cutname);
void DrawCut(const std::string& name); void DrawCut(const std::string& name);
bool IsInsideCut(const std::string& name); bool IsInsideCut(const std::string& name);
static SpectrumManager* s_instance; 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<Histogram>> m_histoMap;
std::unordered_map<std::string, std::shared_ptr<Cut>> m_cutMap; 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<ParameterData>> m_paramMap;
HistogramParameters m_nullHistoResult; HistogramParameters m_nullHistoResult; //For handling bad query
std::mutex m_managerMutex; std::mutex m_managerMutex; //synchronization
}; };
} }

View File

@ -1,3 +1,16 @@
/*
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 "SpectrumSerializer.h"
#include "SpectrumManager.h" #include "SpectrumManager.h"
@ -122,7 +135,7 @@ namespace Navigator {
void SpectrumSerializer::DeserializeData() void SpectrumSerializer::DeserializeData()
{ {
SpectrumManager& manager = SpectrumManager::GetInstance(); SpectrumManager& manager = SpectrumManager::GetInstance();
manager.RemoveAllSpectra(); manager.RemoveAllSpectra(); //When loading in, we remove all extant data, to avoid any potential collisions.
std::ifstream input(m_filename); std::ifstream input(m_filename);
if (!input.is_open()) if (!input.is_open())

View File

@ -1,3 +1,16 @@
/*
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 #ifndef SPECTRUM_SERIALIZER_H
#define SPECTRUM_SERIALIZER_H #define SPECTRUM_SERIALIZER_H

View File

@ -1,3 +1,11 @@
/*
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 #ifndef WINDOW_H
#define WINDOW_H #define WINDOW_H