beto-rodriguez / LiveCharts2

Simple, flexible, interactive & powerful charts, maps and gauges for .Net, LiveCharts2 can now practically run everywhere Maui, Uno Platform, Blazor-wasm, WPF, WinForms, Xamarin, Avalonia, WinUI, UWP.
https://livecharts.dev
MIT License
4.25k stars 552 forks source link

Chart/Series with fixed sized geometries across zoom levels and HeatSeries like coloration. #1093

Open erichiller opened 1 year ago

erichiller commented 1 year ago

I'm working to create a chart that has:

Similar to a HeatSeries, but:

I created a demo, but it requires adding it directly to LiveCharts as many of the necessary items are internal-only (See number 2 below).

Example: Screenshot from 2023-07-01 07-17-06

Options:

  1. Add directly to LiveCharts, I'm happy to submit a PR and have a working demo. Essentially, the required changes are:
    • To enable uniform geometry screen size across zoom levels, change the GeometrySize to use scale.MeasureInPixels(axis.UnitWidth) as HeatSeries does, then width and height could use GeometrySize as a float and calculate size like UnitWidth * GeometrySize.
    • Coloration would use the exact same logic as HeatSeries.
  2. Make the following bits public and then library consumers will more easily be able to extend / create new series.
    • ChartPoint.Context.Visual setter in order to initialize the point's visual.
    • ChartPoint.Context.HoverArea setter
    • ChartPoint.Context.Label setter
    • ChartPointCleanupContext class
    • Bounds class constructor
  3. Use the PointMeasured event to change all the point / geometry properties after they have been calculated by ScatterSeries. This has the downsides:
    • Unable to make use of Tertiary because then ScatterSeries will do its own math to change point size.
    • I would guess this would cause performance to decrease.

If I am missing a way to achieve this that already exists, please let me know.

beto-rodriguez commented 1 year ago

Hi I was able to use the HeatSeries to build this, the PointPadding property removes the default blank space between each point:

heat

var values = new List<WeightedPoint>();
var r = new Random();

for (var i = 0; i < 50; i++)
{
   for (var j = 0; j < 20; j++)
   {
      if (r.NextDouble() < 0.40) continue; // 40% chance that the value is empty

      var w = r.Next(0, 100);
      values.Add(new WeightedPoint(i, j, w));
    }
}

Series = new ISeries[]
{
  new HeatSeries<WeightedPoint>
  {
    PointPadding = new LiveChartsCore.Drawing.Padding(0),
    Values = values,
  }
};

the XY coordinates are not within a fixed range

You can also use the VisualElements property to add custom visuals to the plot, would that be enough?

https://github.com/beto-rodriguez/LiveCharts2/blob/master/samples/ViewModelsSamples/General/VisualElements/ViewModel.cs

visuals

erichiller commented 1 year ago

@beto-rodriguez Thank you for the information on PointPadding, that might be useful in the future.

For this purpose though, it still wouldn't allow use outside a fixed range. The picture I provided is just using some dummy data, the real values have a much larger range, so prepopulating a Heatmap with a bunch of empty values wouldn't be feasible.

With VisualElements there wouldn't be any of the series logic grouping the values together and I would then have to go through and re-write the entire heat map logic for coloring anyways. So that doesn't seem to be a viable option.

beto-rodriguez commented 1 year ago

Thanks for the reply, I am afraid that I do not fully understand your needs, do you have an article about the name of this kind of plots? is it a custom plot for your case?

erichiller commented 1 year ago

I'm not sure there's a separate name for it, it's an XY/ Scatter plot that has discrete positions (there is never any smaller difference in values than the data's resolution. eg. If the resolution is 0.25, then positions like 10.00, 10.25, 10.75 would be valid) but there is no min or max range. ( eg. -1,000.25 to +15,000.75), in this respect its a normal XY chart.

The difference is how the points are displayed. The points are always the minimum resolution wide x high.

To continue my above example, X axis unit is date time, and the minimum time resolution is 100ms. So the points should always be represented as being 0.25 high × 100ms wide, no matter the zoom level. If the screen is displaying times from 500ms to 1000ms and X values 40 to 42 and I have a point at 40.50, 700ms then the point should take up ⅕ of the display wide by ⅛ of the display tall. If the screen is displaying times from 0ms to 5000ms and X values 40 to 60 (10x before) and I have a point at 40.50, 700ms then the point should take up 1/50 of the display wide by 1/80 of the display tall.

The second component is gradient coloring use the min and max of the weight (same as Heatmap does).

Image examples

I would think others would benefit from being able to do this, but if not, I believe it would be worthwhile for users to be able to extend ScatterSeries or any series for that matter.