Skip to content

Commit

Permalink
Merge pull request #153 from ABRG-Models/issue/150_array_with_GraphVi…
Browse files Browse the repository at this point in the history
…sual

Issue/150 array with graph visual
  • Loading branch information
sebjameswml authored Dec 18, 2023
2 parents e1a1d2a + 95ea155 commit 382d1d6
Show file tree
Hide file tree
Showing 13 changed files with 277 additions and 90 deletions.
18 changes: 10 additions & 8 deletions examples/graph2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ int main()
static constexpr bool setup_axes = true;

try {
morph::vvec<float> absc = {-.5, -.4, -.3, -.2, -.1, 0, .1, .2, .3, .4, .5, .6, .7, .8};
morph::vvec<float> data = absc.pow(3);
morph::vvec<float> _absc = {-.5, -.4, -.3, -.2, -.1, 0, .1, .2, .3, .4, .5, .6, .7, .8};
morph::vvec<float> data = _absc.pow(3);
std::deque<float> absc (_absc.size());
std::copy (_absc.begin(), _absc.end(), absc.begin());
auto gvup = std::make_unique<morph::GraphVisual<float>> (morph::vec<float>({0,0,0}));
v.bindmodel (gvup);

Expand All @@ -55,16 +57,16 @@ int main()
gvup->setdata (absc, data, ds);
ds.markerstyle = morph::markerstyle::square;
ds.setcolour ({0.0, 1.0, 0.0});
gvup->setdata (absc, absc.pow(4), ds);
gvup->setdata (absc, _absc.pow(4), ds);
} else {
gvup->policy = morph::stylepolicy::allcolour; // markers, lines, both, allcolour
// The code here demonstrates how to include unicode characters (ss2 is "superscript 2")
using morph::unicode;
gvup->setdata (absc, absc, "y=x");
gvup->setdata (absc, absc.pow(2)+0.05f, "y=x" + unicode::toUtf8(unicode::ss2));
gvup->setdata (absc, absc.pow(3)+0.1f, "y=x" + unicode::toUtf8(unicode::ss3));
gvup->setdata (absc, absc.pow(4)+0.15f, "y=x" + unicode::toUtf8(unicode::ss4));
gvup->setdata (absc, absc.pow(5)+0.2f, "y=x" + unicode::toUtf8(unicode::ss5));
gvup->setdata (absc, _absc.pow(2)+0.05f, "y=x" + unicode::toUtf8(unicode::ss2));
gvup->setdata (absc, _absc.pow(3)+0.1f, "y=x" + unicode::toUtf8(unicode::ss3));
gvup->setdata (absc, _absc.pow(4)+0.15f, "y=x" + unicode::toUtf8(unicode::ss4));
gvup->setdata (absc, _absc.pow(5)+0.2f, "y=x" + unicode::toUtf8(unicode::ss5));
}

if constexpr (setup_axes) {
Expand All @@ -86,7 +88,7 @@ int main()
v.waitevents (0.018);
// Don't update this fast. That's crazy!
if ((rcount++)%20 == 0) {
gv->update (absc, absc.pow(2)*addn, 1);
gv->update (absc, _absc.pow(2)*addn, 1);
addn += 0.2f;
}
// want gv->update (datasets); // to update all at once. THEN I'm done.
Expand Down
4 changes: 3 additions & 1 deletion examples/graph3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ int main()
auto gv = std::make_unique<morph::GraphVisual<float>>(morph::vec<float>({0,0,0}));
v.bindmodel (gv);
morph::vvec<float> data = absc.pow(3);
morph::vec<float, 14> ardata;
ardata.set_from (static_cast<std::vector<float>>(data));

ds.linecolour = {1.0, 0.0, 0.0};
ds.linewidth = 0.015f;
ds.markerstyle = morph::markerstyle::triangle;
ds.markercolour = {0.0, 0.0, 1.0};
gv->setdata (absc, data, ds);
gv->setdata (absc, ardata, ds);

gv->axisstyle = morph::axisstyle::L;

Expand Down
2 changes: 1 addition & 1 deletion morph/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# Header installation
install(
FILES Quaternion.h tools.h BezCoord.h BezCurve.h BezCurvePath.h ReadCurves.h AllocAndRead.h MorphDbg.h mathconst.h MathAlgo.h MathImpl.h number_type.h Hex.h HexGrid.h HdfData.h Process.h RD_Base.h DirichVtx.h DirichDom.h ShapeAnalysis.h NM_Simplex.h Anneal.h Config.h vec.h vvec.h TransformMatrix.h colour.h ColourMap.h ColourMap_Lists.h Scale.h Random.h RecurrentNetworkTools.h RecurrentNetwork.h Winder.h expression_sfinae.h base64.h
FILES Quaternion.h tools.h BezCoord.h BezCurve.h BezCurvePath.h ReadCurves.h AllocAndRead.h MorphDbg.h mathconst.h MathAlgo.h MathImpl.h number_type.h Hex.h HexGrid.h HdfData.h Process.h RD_Base.h DirichVtx.h DirichDom.h ShapeAnalysis.h NM_Simplex.h Anneal.h Config.h vec.h vvec.h TransformMatrix.h colour.h ColourMap.h ColourMap_Lists.h Scale.h Random.h RecurrentNetworkTools.h RecurrentNetwork.h Winder.h trait_tests.h base64.h
Mnist.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include/morph
)
# There are also headers in sub directories
Expand Down
42 changes: 29 additions & 13 deletions morph/GraphVisual.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ namespace morph {
this->ord2_scale.reset();
this->setlimits_x (this->datamin_x, this->datamax_x*2.0f);
if (!this->ord1.empty()) {
// vvec, vvec, datasetstyle
this->setdata (this->absc1, this->ord1, this->ds_ord1);
}
if (!this->ord2.empty()) {
Expand Down Expand Up @@ -366,8 +367,10 @@ namespace morph {
}

//! Update the data for the graph, recomputing the vertices when done.
void update (const std::vector<Flt>& _abscissae,
const std::vector<Flt>& _data, const size_t data_idx)
template <typename Ctnr1, typename Ctnr2>
std::enable_if_t<morph::is_copyable_container<Ctnr1>::value
&& morph::is_copyable_container<Ctnr2>::value, void>
update (const Ctnr1& _abscissae, const Ctnr2& _data, const size_t data_idx)
{
size_t dsize = _data.size();

Expand All @@ -386,11 +389,13 @@ namespace morph {
// May need a re-autoscaling option somewhere in here.

// Transfor the data into temporary containers sd and ad
std::vector<Flt> sd (dsize, Flt{0});
std::vector<Flt> ad (dsize, Flt{0});
this->ord1_scale.transform (_data, sd);
this->abscissa_scale.transform (_abscissae, ad);


std::vector<Flt> sd (dsize, Flt{0});
this->ord1_scale.transform (_data, sd);

// Now sd and ad can be used to construct dataCoords x/y. They are used to
// set the position of each datum into dataCoords
for (size_t i = 0; i < dsize; ++i) {
Expand All @@ -416,8 +421,11 @@ namespace morph {
}

//! update() overload that allows you also to set the data label
void update (const std::vector<Flt>& _abscissae,
const std::vector<Flt>& _data, std::string datalabel, const size_t data_idx)
template < template <typename, typename> typename Container,
typename T,
typename Allocator=std::allocator<T> >
void update (const Container<T, Allocator>& _abscissae,
const Container<T, Allocator>& _data, std::string datalabel, const size_t data_idx)
{
if (data_idx >= this->datastyles.size()) {
std::cout << "Can't add change data label at graphDataCoords index " << data_idx << std::endl;
Expand Down Expand Up @@ -461,8 +469,11 @@ namespace morph {

//! Set a dataset into the graph using default styles, incrementing colour and
//! marker shape as more datasets are included in the graph.
void setdata (const std::vector<Flt>& _abscissae, const std::vector<Flt>& _data,
const std::string name = "", const morph::axisside axisside = morph::axisside::left)
template <typename Ctnr1, typename Ctnr2>
std::enable_if_t<morph::is_copyable_container<Ctnr1>::value
&& morph::is_copyable_container<Ctnr2>::value, void>
setdata (const Ctnr1& _abscissae, const Ctnr2& _data,
const std::string name = "", const morph::axisside axisside = morph::axisside::left)
{
DatasetStyle ds(this->policy);
ds.axisside = axisside;
Expand All @@ -488,9 +499,11 @@ namespace morph {

//! Set a dataset into the graph. Provide abscissa and ordinate and a dataset
//! style. The locations of the markers for each dataset are computed and stored
//! in this->graohDataCoords, one vector for each dataset.
void setdata (const std::vector<Flt>& _abscissae,
const std::vector<Flt>& _data, const DatasetStyle& ds)
//! in this->graphDataCoords, one vector for each dataset.
template <typename Ctnr1, typename Ctnr2>
std::enable_if_t<morph::is_copyable_container<Ctnr1>::value
&& morph::is_copyable_container<Ctnr2>::value, void>
setdata (const Ctnr1& _abscissae, const Ctnr2& _data, const DatasetStyle& ds)
{
if (_abscissae.size() != _data.size()) {
std::stringstream ee;
Expand Down Expand Up @@ -531,8 +544,8 @@ namespace morph {

if (dsize > 0) {
// Transform the data into temporary containers sd and ad
std::vector<Flt> sd (dsize, Flt{0});
std::vector<Flt> ad (dsize, Flt{0});
std::vector<Flt> sd (dsize, Flt{0});
if (ds.axisside == morph::axisside::left) {
this->ord1_scale.transform (_data, sd);
} else {
Expand Down Expand Up @@ -600,7 +613,10 @@ namespace morph {

protected:
//! Compute the scaling of ord1_scale and abscissa_scale according to the scalingpolicies
void compute_scaling (const std::vector<Flt>& _abscissae, const std::vector<Flt>& _data, const morph::axisside axisside)
template <typename Ctnr1, typename Ctnr2>
std::enable_if_t<morph::is_copyable_container<Ctnr1>::value
&& morph::is_copyable_container<Ctnr2>::value, void>
compute_scaling (const Ctnr1& _abscissae, const Ctnr2& _data, const morph::axisside axisside)
{
morph::range<Flt> data_maxmin = morph::MathAlgo::maxmin (_data);
morph::range<Flt> absc_maxmin = morph::MathAlgo::maxmin (_abscissae);
Expand Down
12 changes: 5 additions & 7 deletions morph/MathAlgo.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,16 @@ namespace morph {
/*******************************************************************/

/*!
* Functions whose implementations are in MathImpl, and which differ depending
* on whether T is a scalar type or a vector-like object (such as std::vector
* Functions whose implementations are in MathImpl, and which differ depending on whether
* the container's value_type is a scalar type or a vector-like object (such as std::vector
* or std::list).
*
* Don't confuse this with C++11's std::minmax, which does something similar,
* but won't do a max/min length of vector search like this does.
*/
template < template <typename, typename> typename Container,
typename T,
typename Allocator=std::allocator<T> >
static morph::range<T> maxmin (const Container<T, Allocator>& vec) {
return MathImpl<number_type<T>::value>::maxmin (vec);
template <typename Container, std::enable_if_t<morph::is_copyable_container<Container>::value, int> = 0>
static morph::range<typename Container::value_type> maxmin (const Container& vec) {
return MathImpl<number_type<typename Container::value_type>::value>::maxmin (vec);
}

/*!
Expand Down
16 changes: 7 additions & 9 deletions morph/MathImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#include <morph/vec.h>
#include <morph/range.h>
#include <morph/mathconst.h>
#include <morph/expression_sfinae.h>
#include <morph/trait_tests.h>

namespace morph {

Expand All @@ -47,11 +47,10 @@ namespace morph {
struct MathImpl
{
//! Resizable and Fixed size vector maxmin implementations are common
template < template <typename, typename> typename Container,
typename T,
typename Allocator=std::allocator<T> >
static morph::range<T> maxmin (const Container<T, Allocator>& values)
template <typename Container, std::enable_if_t<morph::is_copyable_container<Container>::value, int> = 0>
static morph::range<typename Container::value_type> maxmin (const Container& values)
{
using T = typename Container::value_type;
// Example to get the type of the container T.
// See https://stackoverflow.com/questions/44521991/type-trait-to-get-element-type-of-stdarray-or-c-style-array
using T_el = std::remove_reference_t<decltype(*std::begin(std::declval<T&>()))>;
Expand Down Expand Up @@ -233,11 +232,10 @@ namespace morph {
struct MathImpl<1>
{
//! Scalar maxmin implementation
template < template <typename, typename> typename Container,
typename T,
typename Allocator=std::allocator<T> >
static morph::range<T> maxmin (const Container<T, Allocator>& values)
template <typename Container, std::enable_if_t<morph::is_copyable_container<Container>::value, int> = 0>
static morph::range<typename Container::value_type> maxmin (const Container& values)
{
using T = typename Container::value_type;
morph::range<T> r (std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest());
for (auto v : values) {
r.max = v > r.max ? v : r.max;
Expand Down
39 changes: 16 additions & 23 deletions morph/Scale.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,10 @@ namespace morph {
*
* \tparam OAllocator Memory allocator for OContainer.
*/
template < template <typename, typename> typename Container,
typename TT=T,
typename Allocator=std::allocator<TT>,
template <typename, typename> typename OContainer=Container,
typename ST=S,
typename OAllocator=std::allocator<ST> >
void transform (const Container<TT, Allocator>& data, OContainer<ST, OAllocator>& output)
template <typename Container, typename OContainer=Container>
std::enable_if_t<morph::is_copyable_container<Container>::value
&& morph::is_copyable_container<OContainer>::value, void>
transform (const Container& data, OContainer& output)
{
size_t dsize = data.size();
if (output.size() != dsize) {
Expand All @@ -136,21 +133,18 @@ namespace morph {
} else if (this->do_autoscale == false && !this->ready()) {
throw std::runtime_error ("ScaleImplBase::transform(): Params are not set and do_autoscale is set false. Can't transform.");
}
typename Container<TT, Allocator>::const_iterator di = data.begin();
typename Container<ST, OAllocator>::iterator oi = output.begin();
typename Container::const_iterator di = data.begin();
typename OContainer::iterator oi = output.begin();
while (di != data.end()) { *oi++ = this->transform_one (*di++); }
}

/*!
* \brief Inverse transform a container of scalars or vectors.
*/
template < template <typename, typename> typename OContainer,
typename ST=S,
typename OAllocator=std::allocator<ST>,
template <typename, typename> typename Container=OContainer,
typename TT=T,
typename Allocator=std::allocator<TT> >
void inverse (const Container<ST, OAllocator>& data, OContainer<T, Allocator>& output)
template <typename OContainer, typename Container=OContainer>
std::enable_if_t<morph::is_copyable_container<Container>::value
&& morph::is_copyable_container<OContainer>::value, void>
inverse (const Container& data, OContainer& output)
{
size_t dsize = data.size();
if (output.size() != dsize) {
Expand All @@ -159,8 +153,8 @@ namespace morph {
if (!this->ready()) {
throw std::runtime_error ("ScaleImplBase::inverse(): Can't inverse transform; set params of this Scale, first");
}
typename Container<ST, OAllocator>::const_iterator di = data.begin();
typename Container<TT, Allocator>::iterator oi = output.begin();
typename Container::const_iterator di = data.begin();
typename OContainer::iterator oi = output.begin();
while (di != data.end()) { *oi++ = this->inverse_one (*di++); }
}

Expand Down Expand Up @@ -195,12 +189,11 @@ namespace morph {
* \param data The data from which to determine the scaling parameters. In practice, this
* will be something like \c std::vector<float> or \c std::list<morph::vec<double,2>>
*/
template < template <typename, typename> typename Container,
typename TT=T,
typename Allocator=std::allocator<TT> >
void autoscale_from (const Container<TT, Allocator>& data)
template <typename Container>
std::enable_if_t<morph::is_copyable_container<Container>::value, void>
autoscale_from (const Container& data)
{
morph::range<TT> mm = MathAlgo::maxmin (data);
morph::range<typename Container::value_type> mm = MathAlgo::maxmin (data);
this->compute_autoscale (mm.min, mm.max);
}

Expand Down
2 changes: 1 addition & 1 deletion morph/Winder.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#include <cmath>
#include <stdexcept>
#include <morph/mathconst.h>
#include <morph/expression_sfinae.h>
#include <morph/trait_tests.h>

namespace morph {

Expand Down
Loading

0 comments on commit 382d1d6

Please sign in to comment.