trase-cpp / trase Goto Github PK
View Code? Open in Web Editor NEW๐ A lightweight plotting library
License: Other
๐ A lightweight plotting library
License: Other
From #46. Still a lot of implementation left in header files, affects:
I'm still getting to grips with the structure, so might have this wrong.
It looks as though there's a Drawable
with a vector of children, and a pointer to parent.
Ought this be a vector of unique_ptr
to children, with a raw pointer to parent?
Also, in Figure
, is there any need for m_axis
to be a shared_ptr
rather than unique_ptr
?
lots of errors in the following:
I would really like to be able to add latex equations for axis labels, but this seems like it would be very difficult to achieve in pure C++. There seems to be no C++ parsers or renderers for latex. Any ideas for how to do this?
Here is a java library for latex: https://github.com/opencollab/jlatexmath
Here is a "C++" library for latex that laughably just generates javascript (!) and use KaTex to render the equations (perhaps their parser is in C++, I didn't check): https://github.com/goldsborough/latexpp
Axis
Drawable
(#5)member functions:
plot(x, y)
: create new Plot1D
,plot2d(matrix)
, create new Plot2D
sym_plot(lambda f)
create new SymPlot1D
(uses current axis limits and N=100 points by default)sym_plot2d(lambda f)
create new SymPlot2D
(uses current axis limits and N=100 points by default)xlim(std::array<float> xlimits)
set x limits for axisylim(std::array<float> xlimits)
set y limits for axisxlabel(std::array<float> xlimits)
set x label for axisylabel(std::array<float> xlimits)
set y label for axislegend()
show Legend
other features:
The OpenGL backend uses paths to draw a circle or rect, this is nice cause you can combine the circle or rect with another path, and then set a common stroke/fill combo at the end. But this is costly for the svg backend, which would prefer to write a single circle
or rect
element. I propose to match the svg behaviour, where the circle
and rect
functions draw the shapes immediately (so you don't put a stroke or fill command after)
Proposal to split src
into the following three directories:
backend
frontend
utils
Currently this is still
Copyright (c) 2018, University of Oxford.
This file is part of the Oxford RSE C++ Template project.
Do you want this project to be completely separate from RSE @martinjrobins? (Might be nice to write a blog post about it anyway).
from #46. For Point
, still shows a line
The begin and end functions in DataWithAesthetics
use the aesthetic tag classes as arguments to choose an aesthetic, ie.
template <typename Aesthetic>
ColumnIterator DataWithAesthetic::end(const Aesthetic &a);
So they are called like
auto it = data.begin(Aesthetic::x());
I think it is cleaner to use a template argument, like so:
auto it = data.begin<Aesthetic::x>();
resulting in two less round brackets to type. I propose switching to the latter
do we want to turn off this warning @fcooper8472 , seems like pointer arithmetic is kinda essential for an iterator!
/home/travis/build/trase-cpp/trase/src/util/ColumnIterator.hpp:92:26: warning: do not use pointer arithmetic [cppcoreguidelines-pro-bounds-pointer-arithmetic]
void increment() { m_p += m_stride; }
Plot1D
Drawable
member functions:
set_values(std::vector<float>&& x, std::vector<float>&& y)
: takes r-values to the vectors holding x and y, sets the data points to plot, updates limits for self and parent axisset_color(RGBA color)
: set line color (this might want to be moved to Drawable
?)set_linewidth(float pixels)
: set line width in pixels (this might want to be moved to Drawable
? see #41 )set_linestyle(????)
: set line style. Need to come up with a way of representing a line style (this might want to be moved to Drawable
? see #41)related functions:
Axis::plot(const std::vector<T1>& x, const std::vector<T2>& y)
: creates a Plot1D
on the given Axis, adjust limits of axis to make the Plot1D
fit. T1
and T2
are convertible to float
Axis::plot(std::vector<float>&& x, std::vector<float>&& y)
: creates a Plot1D
on the given Axis using r-value vectors. The data is moved rather than copied.Axis::plot(Container x, Container2 y)
: creates a Plot1D
on the given Axis using any given containerAxis::plot(initialiser_list x, initialiser_list y)
: creates a Plot1D
using initialiser listsSymPlot1D
Axis
using the resolution (n points) providedmember functions:
set_n(const int N)
: set the number of points to plotrelated functions:
Axis::plot(const F& f)
: plot a symbolic function on the given Axis
. f
is the function object, x limits are set by the current axis, N=100
is the default number of points to use when plottingRather than multiple entries with "from"/"to" entries in the svg, it might be neater to use the "values" and "keyTimes" attributes:
values="50; 250; 120;250; 170; 250; 210; 250"
keyTimes="0; 0.15; 0.3; 0.45; 0.6; 0.75; 0.9; 1"
Here's a useful tool that can determine SVG standards compliance:
https://validator.w3.org/
Currently the test svg is missing encoding information, which should simply look something like
encoding="utf-8"
in the document header.
Matplotlib's first line in SVG docs is
<?xml version="1.0" encoding="utf-8" standalone="no"?>
trase currently has
<?xml version="1.0" standalone="no"?>
Need to think about if we should take any ideas from the grammar of graphics, this seems to be the latest bible of statisticians / visualisation people:
http://byrneslab.net/classes/biol607/readings/wickham_layered-grammar.pdf
from #46
One of the current features of the GL backend is to highlight points that are near to the mouse cursor and give their exact x,y values.
mouseover
events: https://www.w3.org/TR/SVG11/interact.html#SVGEventsThere are a number of targets for refactoring: opening this issue to commit against.
Need some sort of online documentation. Listing some options below:
At the moment new axes are created on-top of each other, this should be changed to a sensible default, e.g 2x1 subplots for two plots, 2x2 for 3 or 4 plots, etc., with the user being able to change how the axes are tiled/placed.
note that histogram geometry will be the first to have a default transform (x-> histogram to -> y)
Is the solution to this to add a noexcept to the Colormap
constructor?
/home/travis/build/trase-cpp/trase/src/util/Colors.cpp:126:27: warning: initialization of 'viridis' with static storage duration may throw an exception that cannot be caught [cert-err58-cpp]
const Colormap Colormaps::viridis(std::vector<Vector<float, 3>>{
^
/home/travis/build/trase-cpp/trase/src/util/Colors.cpp:107:11: note: possibly throwing constructor declared here
Colormap::Colormap(std::vector<Vector<float, 3>> list)
from #46, this will cover RawData
and ColumnIterator
as well
We need a consistent way of styling axes, plots etc.
I was thinking of a class Style
, that is contained in Drawable
, and which uses chaining to set its parameters:
class Style {
float m_linewidth;
LineStyle m_linestyle;
RGBA m_color;
std::string m_font;
....
Style& line_width(const float lw);
Style& line_style(const std::string& ls);
Style& color(const RGBA& lw);
};
That way you would write something like:
auto plt = ax->plot(x,y)
plt->style().line_width(3.f).line_style(".-").color(RGBA(0,0,0,255));
Or you could set a consistent style for multiple plots:
auto my_style = create_style().line_width(3.f).line_style(".-").color(RGBA(0,0,0,255)).font("Roboto");
auto plt = ax->plot(x,y)
plt->style() = my_style.color(blue);
auto plt = ax->plot(x,y)
plt->style() = my_style.color(green);
thoughts @fcooper8472?
from #46
name: Legend
created by Axis
Axis
, Plot1D
etcmember variables:
std::vector<Drawable *> m_children
m_area
: area as ratio of parentm_pixels
: current area as raw pixelsm_time_span
: maximum time for animationm_times
: frame times for animationm_time
: current time for animationmember functions:
void resize(const std::array<float, 4> &parent_pixels)
: resize itself and its childrentemplate <typename Backend> void draw(Backend &backend)
: draw itself and its childrenadd_frame(time)
: add a new animation framefloat get_frame_index()
: get frame index associated with current time (as a float, could be in-between frames)set_time(time)
: set current animation timename: Figure
member functions:
save(const char *filename)
: save to filename (requires #10)show(Backend& backend)
: This draws the figure using the given backend, control of main program is suspended until the figure is closed.axis()
: returns the figure axis as std::shared_ptr<Axis>
related functions:
figure()
: creates new figure, returns std::shared_ptr<Figure>
.SymPlot2D
Axis
SymPlot
, templated on function object, and other parameters of function are modifiable via slidersrelated functions:
Axis::plot2d(const F& f, int N, std::array<float, 4> limits)
: creates a SymPlot2D
on the current axis. N
is the number of points to use along each axis (so N*N points in total), limits
gives the x-y min and max limits that the data is plotted overAxis::plot2d(const F& f, int N)
: creates a SymPlot2D
on the current axis. N
is the number of points to use along each axis (so N*N points in total), uses the current limits of the axisAxis::plot2d(const F& f)
: creates a SymPlot2D
on the current axis. N=50
is the number of points to use, uses the current limits of the axisWould be good to optionally link-in web fonts (such as Google fonts) when using the SVG backend.
This will require:
<style type="text/css">
@import url('https://fonts.googleapis.com/css?family=Indie+Flower');
</style>
Axis::draw_common
. This might be best as a method on the axis class that fills in a new member variable on the axis class.Due to #32 there is now a std::vector<std::shared_ptr<Axis>> m_axes;
in Figure
.
As a result there are several calls to m_axes.back()
littered through the code. Some clearly ought to be replaced by a loop over all axes, but some are less clear:
m_axes.back()->serialise(backend);
See #21. Could use:
https://github.com/validator/validator
Image
Drawable
related functions:
Axis::image(DataWithAesthetics data, Transform transform = Transform(Identity))
: creates a Image
on the current axis.currently using std::array<float,2>
for 2d vectors, should do a proper 2d vector class that can do common vector operations in order to make the code more readable
BackendSVG
uses the text property alignment-baseline
for vertical alignment of text, but this does not work in either inkscape or the GNOME image viewer (it works correctly on google chrome). Is there a more widely supported vertical alignment property in SVG?
At the moment, the BackendGL uses a single font that must be in the a hard-coded directory. Be nice if the functionality was the same as the SVG backend. I.e. can use any of the locally install fonts, or perhaps even download a web font if requested.
The Axis
class uses std::array<float,4>
for its xy limits [xmin,ymin,xmax,ymax]. This should be a separate bounding box class, which allows for:
Need to fill out documentation here: https://codedocs.xyz/trase-cpp/trase/index.html
Many methods/classes in general need to be better commented, which should happen over time. This issue focuses on some core improvements:
if Plot1D
lines are outside the current Axis
limits, they are drawn outside the axis, only lines within the axis should be drawn
the BackendGL
class has a bunch of inlined member functions that are only used in BackendGL.cpp
, should move these to the cpp file, along with all the necessary includes
I would like to implement some helper to make this kind of thing look less nasty (easier to read, reason about, and maintain):
m_out << "x=\"" << min[0] << "\" y=\"" << min[1] << "\" width=\"" << delta[0] << "\" height=\"" << delta[1] << "\" ";
There are several possible options.
template<typename T>
std::string att(const std::string& name, const T val)
{
std::stringstream ss;
ss << std::setprecision(5) << name << "=\"" << val << "\" ";
return ss.str();
}
so that the above example becomes
m_out << att("x", min[0]) << att("y", min[1]) << att("width", delta[0]) << att("height", delta[1]);
This looks quite nice, and is certainly much easier to read. But, the overhead of generating lots of stringstream
objects and returning strings is high (~2x slower). Speed probably isn't a massive concern at this stage, but let's avoid massive slowdowns if there's a better option.
m_out
:I don't like this option at all, because the example becomes:
att("x", min[0]); att("y", min[1]); att("width", delta[0]); att("height", delta[1]);
It's super unclear at a glance what's happening and doesn't have any flexibility at all.
struct attribs {
std::stringstream ss;
attribs& add(const std::string& name, const float val) {
ss << std::setprecision(5) << name << "=\"" << val << "\" ";
return *this;
}
/* overload << */
};
The example becomes
attribs att;
att.add("x", min[0]).add("y", min[1]).add("width", delta[0]).add("height", delta[1]);
m_out << att;
This has a greatly reduced cost vs 1, is flexible, but perhaps not the most elegant.
@martinjrobins Any other ideas for how this might look?
Makes some function names more meaningful, and groups backends into Backend, and AnimatedBackend concepts.
We ought to unit test the svg backend methods. Simple things will be very straightforward, like
SECTION("finalise produces correct string") {
backend.finalise();
CHECK(out.str() == "</svg>\n");
}
For functions taking arguments, such as circle
, we can check that specific parameter values end up as expected in the resulting string.
need to write a cairo backend similar to #9 to, for example, write plots to svg files
I'm thinking of
auto data = create_data().x(x).y(y)
a class that deals with all the actual drawing, uses OpenGL/NanoVG + Dear ImGUI
Need output to file format
Was thinking that this could be done by serialization to SVG format https://www.w3.org/TR/2011/REC-SVG11-20110816/Overview.html
Objects with multiple time frames could use the animation functionality in SVG format
perhaps just add a std::string to_svg()
function to Drawable
#5
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.