plotters-rs / plotters

A rust drawing library for high quality data plotting for both WASM and native, statically and realtimely πŸ¦€ πŸ“ˆπŸš€
https://plotters-rs.github.io/home/
MIT License
3.88k stars 281 forks source link

[Feature Request] Allowing backend defined device coordinate type #128

Open EFanZh opened 4 years ago

EFanZh commented 4 years ago

It seems we should allow each backend define their own coordinate type. For example, the SVG standard actually allows float point numbers, but plotters currently uses BackendCoord for all the backends.

=== Original Conversation Currently, it seems that plotters only supports integer coordinates, so for SVG backend, some detail could be loss in the graph due to integer coordinates. Could real number coordinates be supported so the lines and curves could have more precision?

38 commented 4 years ago

Hi EFanZh, actually Plotters support real number already (See this example https://github.com/38/plotters/blob/master/examples/chart.rs#L17).

However, for histogram series, it needs the coordinate to be discrete, so that we have well defined buckets. Currently we don't have a way to do that, but the next major release will be shipped with better solution than use integer coord for discrete coord requirement.

I am not sure if this answers your question, if not, could you please share more use case, so that we can have a more concrete scenario?

Thanks you so much

EFanZh commented 4 years ago

I mean the coordinates that is used to draw on backends (drawing::backend::BackendCoord):

https://github.com/38/plotters/blob/31b8e5a1386a4841ad9e21b6f96b46432da299b0/src/drawing/backend.rs#L6

This image is generated using plotters. As you can see, all the coordinates in it are integers. I think real number coordinates could improve precision if the image is enlarged, especially for vector images.

38 commented 4 years ago

I see what you mean, but this might be tricky.

Since plotters has the backend independent drawing API, which means all the backend - but apparently having half pixel isn't supported in most of other backends. This breaks the plotters promise - which is the drawing result should be mostly unrelated to the backend (Because most of the backend need to truncate the backend coord anyway).

I think one of the possible precision mitigation would be creating a higher resolution image, this will gives you better precision, as long as it's vector graph, it's not really a difference (you could always use viewBox attribute for that purpose). Unfortunately, current published version doesn't have a way to set the viewBox, but this should be shipped with the next minor release.

If this is the case, I would like to change this feature request to track the customized viewPort API. Also please let me know if you think there's anything can't addressed by the viewPort.

Thanks!

EFanZh commented 4 years ago

Generating a high-resolution image then scale it down can already be done by the web page displaying it. I think supporting view port has little use here. And it is hard to determine how much should I scale the image.

Also, I found that actually most backends supports half pixel coordinates:

So maybe f64 is a better choice for backend coordinates?

38 commented 4 years ago

I think supporting view port has little use here

Actually you can use the combination of viewPort and height + width attribute to make the svg in correct original size, at the same time you can improve the plotting precision. Actually viewBox seems to be a good solution on this: https://jsfiddle.net/c6v9n10g/ (This means you don't need to zoom it yourself)

But accepting f64 doesn't necessarily means it can really support it, for those pixel based devices, integer is the physical limit. In addition, I am worry about when f64 is used as the coordinate type, it would introduce a huge performance regression in the bitmap backend, which have been used in embedded device display already. Please note real-time rendering on low power device is one of the use case of Plotters and there are actual users.

So I still hold the idea that we would keep current definition as long as it's

Changing backend coordinate might also be a breaking change and seems doesn't give a huge benefit. For SVG case, I believe viewBox is the solution to all the problems.

EFanZh commented 4 years ago

I see your point, and I still have a few arguments here:

  1. Even for pixel based devices, floating point coordinates could still be meaningful for anti-aliasing rasterizers to produce high quality output images. I know this can also be done by generating an larger image then scaling it down, but the cost to scale the image might be high, and the result might be suboptimal.
  2. If performance on coordinate calculations is the problem, how about making coordinate type backend dependent, so all backends could have their desired performance and output image quality. But like what you have said, this is a breaking change and could be difficult to implement.

Thank you for being patient with me. I totally understand if you don’t want to changing the current implementation. I am currently OK with integer coordinates and the quality of the output image. Plotters is very good as it is, I just think the quality of the generated image could be better.

38 commented 4 years ago

I am glad to have this conversation, I understand your point but I believe for anti-aliasing we can use alpha channel instead which is already supported by the API. I personally would rather be conservative to all API changes. And I think backend defined coordinate type would be one of the reasonable option, I am really appreciated about this idea - But just let me put this on my backlog since I don't think I will prioritize this work for now. But again, thanks for your advice and if you don't mind I would like to change the title of issue and keep it open as a backlog item.

Thanks again!

siefkenj commented 3 years ago

Could this be done with generics? That way we could maintain full compatibility with existing code and integer-only speed for applications that need it.

TonalidadeHidrica commented 3 years ago

I'm new to this plotter crate. I think this crate is wonderful. Really thanks to your efforts!

By the way, I really wish that it supports non-integral coordinates. It also causes not-really-good-quality plots. Here is a SVG-backend version of the parabola example illustrated in the quick start. As you can see, the curve is jaggy and not really beautiful.

image

Pascal-So commented 2 years ago

Hi, I would like to call attention to another way in which this would be relevant for raster backends. I believe issue #283 might be caused by this.

When plotting a function, the usual process is to choose a sampling grid in the x axis, then evaluating the function at these x positions. Setting an appropriate sampling resolution is sometimes difficult, especially when the code doesn't yet know the final pixel resolution at which the plot will be rendered.

If we set the sampling resolution too low, we end up with visible corners in the plotted line because the piecewise linear approximation doesn't sufficiently approximate the underlying function.

plot_lores

However when setting the sampling resolution too high, we might end up with a situation where the sampling resolution is greater than the pixel resolution. The quantized line segments generated in this setting will often have a length of just one pixel and end up exactly horizontal or vertical. Depending on the path rendering method used, this can effectively disable anti-aliasing.

plot_hires

The effect is most visible in regions with small slopes, for example at the top of the arch here. Note that both of these images have been scaled up (without resampling) by a factor of 3 to make it easier to see.

I believe for anti-aliasing we can use alpha channel instead which is already supported by the API

I think if we want to solve this by using the alpha channel in the API, we effectively have to move the rasterization to happen inside plotters, so that the backend can receive different alpha values for pixels along the border of the plotted line. That's of course a possible option but I feel like it would kinda go against the purpose of having different rendering backends in the first place, because that way the backend effectively receives an image and is just responsible for displaying that somewhere. Or maybe I'm completely misunderstanding what you propose here, in this case please do elaborate!

As far as I can tell, calculations within plotters are currently generic in the coordinate type, so I also think that letting the backend specify a preferred coordinate type would be a good solution, because it would not break embedded applications that don't want to or can't deal with floating point calculations. You mention that this would be a breaking change, but it feels like this would only be breaking for backend providers and not for end-users, or am I wrong on this?

Either way thank you for your valuable work on this library, it's overall a very enjoyable experience!