coder543 / dataplotlib

Scientific plotting library for Rust
59 stars 6 forks source link

A development plan? #2

Open AtheMathmo opened 8 years ago

AtheMathmo commented 8 years ago

Hey!

I hope you don't mind me writing this issue up. I was just taking a look at the library to see if I could use it for an upcoming example in rusty-machine. There are a few things missing before I would be able to use. Before writing up issues for each I thought it was best to ask what your plans are for development? Are there any features you're looking at next?

Here are a few of the things I thought of while taking a look:

Of course plotting libraries are humongous, I just meant to get an idea of whether these things are on your radar or if you are focusing on other things.

And as I've taken up this space and your time I may as well chuck out a suggestion. I think it would make sense for the PlotBuilder2D to follow the Rust builder pattern. I think this feels natural in this application and makes the flow of the library a little more obvious.

Thanks!

coder543 commented 8 years ago

Hello! Sorry for the delayed response. Life has been very hectic for the past few weeks, and it promises to be just as hectic for the next month or so. I'm hoping to really dig into this library sometime in December, but I definitely welcome ideas and/or pull requests!

The heaviness of the current dependencies is definitely something I want to address, but as I mentioned on Reddit, I have also considered going all-in and using Conrod, which would give me nice controls that could be used inside the plotting window. If you have any suggestions on what kind of backend to use, I'm open to that. It might be a good idea to just make dataplotlib output an image, and then let the client send that image to a window or to the filesystem, and to ask dataplotlib for a new one if the user zooms, etc.

The Rust builder pattern does look interesting, and I think it could definitely apply here.

Scatter plots are on my todo list, and the drawing options would get there eventually, but I hadn't thought about ellipse plotting. We'll see what happens! Hopefully I can make dataplotlib into something useful.

fedtf commented 7 years ago

Hi!

I am learning Rust and hope that I will be able to contribute. Do I understand correctly that the purpose of spawning threads for new plots is to be able to do something like

let mut pb1 = PlotBuilder2D::new();
let mut pb2 = PlotBuilder2D::new();

pb1.add_simple_xy(xy_sin);
pb2.add_simple_xy(xy_lin);

let mut plt = Plotter::new();
plt.plot2d(pb1);
plt.plot2d(pb2);
plt.join();

to get 2 separate plots and wait for closing of each before terminating? This currently does not work since Plot::new2d each time tries to create new sdl context and crashes on the second call. Also, I would guess that update_frame in draw_plots should be called on each loop iteration: otherwise, e.g. moving the window outside the screen and then back or switching between desktops effectively erases the content.

I think it would be nice to have a development plan for example in the form of opened issues for features to implement or just a list of them.

coder543 commented 7 years ago

Yeah, right now the development plan is basically that I need to stop being distracted by other projects and invest some time into this one. Yes, that code you posted is the intent.

On the rare occasions that I've worked on this code lately, one of my primary goals has been to have a modular render backend system. This way it would be easy to render to an image file directly, if you want to minimize dependencies or do batch jobs. It would also be possible to use a GUI backend like I somewhat already developed if you wanted an interactive plot with minimal effort. I also need to figure out a way to make multiple GUI plots work without crashing, and I still need to implement all of the other basic plot types (like bar charts), but I've been holding off on this until the modular architecture was in place.

fedtf commented 7 years ago

I had some time to work on it. You can look at the result in sdl_gui branch in my fork. My previous code example does work as expected and adding new type of plots appears to be fairly straightforward, but the design is still not quite there and does not yet fit to your abstract Drawable framework. However, having read about SDL, I'm not sure that it would be possible to achieve that what you are for with DrawSDL as it is now.

First, there is clearly a limitation on the number of contexts that can be instantiated, only one. So, it is impossible to associate context with each plot. It should be initialized once and then create new windows. Also, there is only one event handler and it receives events for all the windows.

When it comes to multithreading, SDL video system is not designed for that, and all the rendering should be performed in one thread which created the context. Preferably, event handling should also be done there. With all this in mind, I ended up having one separate GUI thread responsible for creating windows, drawing there and processing events. It is spawned when Plotter is instantiated and runs its main loop, checking for events and tasks from Plotter. Plotter::plot2d sends new plotting task through the task queue, GUI receives it and creates new window with the plot.

Clearly, the implementation is too much coupled with SDL, and it should be somehow abstracted out, but the right abstraction is still something to think about.

coder543 commented 7 years ago

So, I have good news. This evening I spent probably 5 hours working on dataplotlib and sdl2_mt! I've got things moving in the right direction, hopefully. What is sdl_mt? sdl2_mt is a multithreaded SDL2 wrapper that I'm creating in a separate crate that will hopefully be useful to other people and projects in addition to dataplotlib! Basically, it's a message-passing wrapper around SDL2 that keeps all SDL2 operations on a single UI thread. Unlimited threads can hold a clone of an Sdl2Mt object, which is just a wrapper around an mpsc::Sender object with some nice helper functions. The event loop itself receives messages and manages state. Most of the messages are in the form of a lambda function to be executed on the UI thread itself, with access to the most relevant SDL2 objects.

I'm still working on how to handle SDL2 events with this architecture, but I think I've got that to where it should be mostly working, there's just a concern that unhandled events could build up and slow things down over time, so I'll need to implement "garbage collection" of events that no event handler wants to handle.

I really appreciate @fedtf stepping up and creating an experimental fork. It means a lot to me to see other people so interested in advancing this project. I've had an idea of how I wanted to implement this for a long time now, so I'm at least going to play with that idea for now and see if it works well in practice.

I'm sorry that I've been so busy lately with other things that I haven't been able to work on this until today. Hopefully I'll have more time in the near future to make progress on it. I went ahead and committed the code changes I had made on dataplotlib to a new_arch branch, but that branch does not currently build. Once I realized what I was doing would be better off in a separate crate, I kinda just stopped where I was. When I get sdl2_mt complete enough to handle dataplotlib's needs (which looks to be soon), then I'll finish moving the new_arch branch over to using sdl2_mt. I also copied draw_sdl.rs into draw_image.rs and changed like 5 lines in preparation for creating a direct-to-image-file backend for dataplotlib when I shifted all of my focus over to sdl2_mt. Ideally, dataplotlib will support at least these two backends, configurable through Cargo "features". Other backends would be fun to have, but as long as dataplotlib supports a cross-platform GUI option and an image file option, I think that'll be a solid foundation.

coder543 commented 7 years ago

I spent more time working on sdl2_mt today, and I think it's actually at a basic, usable point. I added some simple examples and integration tests, although they aren't particularly comprehensive at the moment,

Basic example of sdl2_mt.

Resizing the window and using the arrow keys each have effects. Closing the window or hitting escape will exit the demo.

coder543 commented 7 years ago

The new_arch branch now compiles using sdl2_mt and things mostly work. The drawing is all done in a small corner of the upper-left corner of the window because I need to scale the plot to fit the window, and then I need to make window resizing work in the new model. The sdl2_mt version should be at feature-parity with the current master soon, but theoretically without the issues the previous implementation had with running multiple plot windows simultaneously.

coder543 commented 7 years ago

I have now merged the new_arch branch into master! https://github.com/coder543/dataplotlib/pull/3

Things work pretty well now! And there's zooming with the scroll wheel, but I still need to implement click-dragging.

At this point, now that the new architecture is in place the big needs are as follows:

shakram02 commented 7 years ago

Hello, I'm so new here so please excuse me if I say something that's not logical.

note: I'm a real noob regarding sdl2, openGL and graphics stuff in general, any advice is greatly appreciated

coder543 commented 7 years ago

SDL2 is a heavy dependency. On Windows, it's very annoying to set up a development environment where you can depend on SDL2, so any Windows users who just want to save generated plots to image files directly would be faced with a non-trivial setup task. Even if they have SDL2 properly configured in their dev environment, initial compile times should be faster with the image backend than with the SDL2 backend, which could make for a more pleasant experience.

I haven't read the matplotlib link yet, but I specifically designed the Draw trait (interface) to do what you're describing. link here. It provides a common set of primitive drawing functions that any backend can implement, and then that backend will work with dataplotlib. The code to plot line charts, bar charts, etc. will depend solely on the Draw trait, making them backend agnostic. Since I haven't read the link yet, I'm not sure what "layers" are exactly.

I'm perfectly fine with an OpenGL backend being added, or a Piston backend. All that is required is for someone to implement the Draw trait using OpenGL or Piston.

The Draw trait still needs a few more functions. At a minimum, it needs a function to draw text with a particular font and font size, and it would be good to have a function which returns the size of a given string of text if it were drawn without actually drawing it.

mmstick commented 7 years ago

So what areas are left to implement in this project?

coder543 commented 7 years ago

I try to keep this list up to date, and it seems to still be accurate now. If you see anything you want to tackle, I'm happy to help as much as I can.

coder543 commented 6 years ago

I have now created individual issues to track progress on this project.