Skip to content

Commit fb7d1ef

Browse files
authored
Merge branch 'main' into fix-datetimeautomatic-inverted-axes
2 parents c4a3501 + b0d8166 commit fb7d1ef

24 files changed

Lines changed: 567 additions & 123 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
## ScottPlot 5.0.56
22
_Not yet on NuGet..._
3+
* Statistics: Added support for kernel density estimation with Epanechnikov, Gaussian, Uniform, and Triangular strategies. Scott's rule (no relation) was implemented for bandwidth estimation (#4869) @bclehmann
4+
* Radar: Added a `IsAxisAboveData` property to allow the axes to be rendered on top of the data series (#4874) @Christoph-Wagner
5+
* Scatter: Add support for vertically oriented gradient fills (#4881) @manaruto
6+
* Bar Plot: Add `LabelsOnTop` option to allow bar labels to always be displayed and never get overlapped by other bars or plottables (#4886, #4855) @manaruto @bclehmann
7+
* PolarAxis: Add support for custom background color (#4897) @CoderPM2011
8+
* PolarAxis: Added support for custom spoke lengths (#4897) @CoderPM2011
39

410
## ScottPlot 5.0.55
511
_Published on [NuGet](https://www.nuget.org/profiles/ScottPlot) on 2025-03-22_
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
using ScottPlot.Statistics;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace ScottPlotCookbook.Recipes.Miscellaneous;
9+
10+
public class KernelDensityEstimation : ICategory
11+
{
12+
public Chapter Chapter => Chapter.General;
13+
public string CategoryName => "Kernel Density Estimation";
14+
public string CategoryDescription => "Kernel Density Estimation (KDE) can be used to estimate a PDF for a histogram, allowing the creation of density plots";
15+
public class KdeQuickstart : RecipeBase
16+
{
17+
public override string Name => "Density Plot";
18+
public override string Description => "Density Plots use KDE to estimate a PDF.";
19+
20+
[Test]
21+
public override void Execute()
22+
{
23+
var ys = SampleData.Faithful;
24+
25+
var hist = Histogram.WithBinCount(80, ys);
26+
27+
var histPlot = myPlot.Add.Histogram(hist);
28+
histPlot.BarWidthFraction = 0.8;
29+
30+
var densityEstimate = hist.Bins.Select((x, i) => KernelDensity.Estimate(x, ys)).ToArray();
31+
double scale = ys.Length;
32+
33+
var rescaledDensityEstimate = densityEstimate.Select(x => x * scale).ToArray();
34+
35+
var scat = myPlot.Add.Scatter(hist.Bins, rescaledDensityEstimate, Colors.Red);
36+
scat.MarkerSize = 0;
37+
}
38+
}
39+
40+
public class KdeKernelOptions : RecipeBase
41+
{
42+
public override string Name => "Density Plot Kernels";
43+
public override string Description => "Several choices of kernels are available.";
44+
45+
[Test]
46+
public override void Execute()
47+
{
48+
var ys = SampleData.Faithful;
49+
50+
var hist = Histogram.WithBinCount(80, ys);
51+
52+
var histPlot = myPlot.Add.Histogram(hist);
53+
histPlot.BarWidthFraction = 0.8;
54+
foreach (var bar in histPlot.Bars)
55+
{
56+
bar.FillColor = Colors.LightBlue;
57+
}
58+
59+
foreach (var kernel in Enum.GetValues<KdeKernel>())
60+
{
61+
var densityEstimate = hist.Bins.Select((x, i) => KernelDensity.Estimate(x, ys, kernel)).ToArray();
62+
double scale = ys.Length;
63+
64+
var rescaledDensityEstimate = densityEstimate.Select(x => x * scale).ToArray();
65+
66+
var scat = myPlot.Add.Scatter(hist.Bins, rescaledDensityEstimate);
67+
scat.MarkerSize = 0;
68+
scat.LegendText = kernel.ToString();
69+
}
70+
}
71+
}
72+
}

src/ScottPlot5/ScottPlot5 Cookbook/Recipes/PlotTypes/Bar.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,49 @@ public override void Execute()
101101
}
102102
}
103103

104+
public class BarLabelsLast : RecipeBase
105+
{
106+
public override string Name => "Labels on Top";
107+
public override string Description => "Bars with labels are rendered one at a time by default, " +
108+
"but this makes it possible for bar labels to get overlapped by other bars. " +
109+
"Bar chars may be configured to render labels last, even above other plottables.";
110+
111+
[Test]
112+
public override void Execute()
113+
{
114+
double[] values = Generate.Consecutive(5, first: 1);
115+
116+
// create two bar plots
117+
var bars1 = myPlot.Add.Bars(values);
118+
var bars2 = myPlot.Add.Bars(values);
119+
120+
// enable the LabelsOnTop feature on one of the bars
121+
bars2.LabelsOnTop = true;
122+
123+
// give each bar a label and style it to demonstrate the effect
124+
static void StyleBar(ScottPlot.Plottables.BarPlot barPlot, double xOffset)
125+
{
126+
barPlot.ValueLabelStyle.FontSize = 32;
127+
for (int i = 0; i < barPlot.Bars.Count; i++)
128+
{
129+
var bar = barPlot.Bars[i];
130+
bar.Label = i.ToString();
131+
bar.CenterLabel = true;
132+
bar.Position = i * .5 + xOffset;
133+
bar.FillColor = bar.FillColor.WithAlpha(.9);
134+
}
135+
}
136+
137+
StyleBar(bars1, 0);
138+
StyleBar(bars2, 4);
139+
140+
myPlot.Add.Text("Default", 0, 6);
141+
myPlot.Add.Text("LabelsOnTop", 4, 6);
142+
143+
myPlot.HideGrid();
144+
}
145+
}
146+
104147
public class BarPosition : RecipeBase
105148
{
106149
public override string Name => "Bar Positioning";

src/ScottPlot5/ScottPlot5 Cookbook/Recipes/PlotTypes/PolarAxis.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,32 @@ public override void Execute()
124124
}
125125
}
126126

127+
public class PolarAxisBackground : RecipeBase
128+
{
129+
public override string Name => "Polar Axis Background";
130+
public override string Description => "The background style of polar axes may be customized";
131+
132+
[Test]
133+
public override void Execute()
134+
{
135+
var polarAxis = myPlot.Add.PolarAxis();
136+
polarAxis.FillColor = Colors.Blue.WithAlpha(.2);
137+
}
138+
}
139+
140+
public class PolarSpokeLength : RecipeBase
141+
{
142+
public override string Name => "Polar Axis Spoke Length";
143+
public override string Description => "The length of spokes may be customized. " +
144+
"Spoke length is expressed as a fraction of the polar axis radius.";
145+
146+
[Test]
147+
public override void Execute()
148+
{
149+
myPlot.Add.PolarAxis(spokeLength: 1.3);
150+
}
151+
}
152+
127153
public class PolarSpokeLabels : RecipeBase
128154
{
129155
public override string Name => "Polar Axis Spoke Labels";

src/ScottPlot5/ScottPlot5 Cookbook/Recipes/PlotTypes/Radar.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,23 @@ public override void Execute()
139139
radar.PolarAxis.StraightLines = true;
140140
}
141141
}
142+
143+
public class AxisOnTop : RecipeBase
144+
{
145+
public override string Name => "Axis on Top of Data";
146+
public override string Description => "Radar charts can be customized so the axis is rendered on top of the data";
147+
148+
[Test]
149+
public override void Execute()
150+
{
151+
double[] values = { 78, 83, 100, 76, 43 };
152+
var radar = myPlot.Add.Radar(values);
153+
154+
// make the shape opaque
155+
radar.Series[0].FillColor = Colors.RebeccaPurple;
156+
157+
// render the axis above the data
158+
radar.IsAxisAboveData = true;
159+
}
160+
}
142161
}

src/ScottPlot5/ScottPlot5 Cookbook/Recipes/PlotTypes/Scatter.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,10 @@ public override void Execute()
438438
double[] ys = Generate.Sin(51);
439439

440440
var poly = myPlot.Add.ScatterLine(xs, ys);
441-
442441
poly.FillY = true;
442+
443+
// colors are placed at specific positions on the X axis
444+
poly.AxisGradientDirection = AxisGradientDirection.Horizontal;
443445
poly.ColorPositions.Add(new(Colors.Red, 0));
444446
poly.ColorPositions.Add(new(Colors.Orange, 10));
445447
poly.ColorPositions.Add(new(Colors.Yellow, 20));
@@ -449,6 +451,29 @@ public override void Execute()
449451
}
450452
}
451453

454+
public class ScatterFillGradientVertical : RecipeBase
455+
{
456+
public override string Name => "Scatter Plot with Vertical Gradient";
457+
public override string Description => "Scatter plots may be filled with vertical gradients.";
458+
459+
[Test]
460+
public override void Execute()
461+
{
462+
double[] xs = Generate.Consecutive(51);
463+
double[] ys = Generate.Sin(51);
464+
465+
var poly = myPlot.Add.ScatterLine(xs, ys);
466+
poly.FillY = true;
467+
468+
// colors are placed at specific positions on the Y axis
469+
poly.AxisGradientDirection = AxisGradientDirection.Vertical;
470+
poly.ColorPositions.Add(new(Colors.Red, -1));
471+
poly.ColorPositions.Add(new(Colors.Blue, 0));
472+
poly.ColorPositions.Add(new(Colors.Orange, .5));
473+
poly.ColorPositions.Add(new(Colors.Magenta, 1));
474+
}
475+
}
476+
452477
public class ScatterScaleAndOffset : RecipeBase
453478
{
454479
public override string Name => "Scatter Scale and Offset";

src/ScottPlot5/ScottPlot5 Demos/Avalonia Demo/Demos/BackgroundImagesWindow.axaml.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
namespace Avalonia_Demo.Demos;
1414

15-
public class BackgroundImagesDemo: IDemo
15+
public class BackgroundImagesDemo : IDemo
1616
{
1717
public string Title => "Background Images";
1818
public string Description => "Use a bitmap image for the background of the figure or data area";
@@ -39,7 +39,8 @@ public BackgroundImagesWindow()
3939

4040
private void HandleDataContextChanged(object? sender, PropertyChangedEventArgs e)
4141
{
42-
if (e.PropertyName == nameof(TypedDataContext.ShowFigureBackground) || e.PropertyName == nameof(TypedDataContext.ShowDataBackground) || e.PropertyName == nameof(TypedDataContext.SelectedImagePosition)) {
42+
if (e.PropertyName == nameof(TypedDataContext.ShowFigureBackground) || e.PropertyName == nameof(TypedDataContext.ShowDataBackground) || e.PropertyName == nameof(TypedDataContext.SelectedImagePosition))
43+
{
4344
ResetPlot();
4445
}
4546
}
@@ -58,8 +59,8 @@ private void ResetPlot()
5859
AvaPlot.Plot.Title("Plot with Image Background");
5960

6061
// assign the bitmap image
61-
AvaPlot.Plot.FigureBackground.Image =TypedDataContext.ShowFigureBackground ? SampleImages.ScottPlotLogo() : null;
62-
AvaPlot.Plot.DataBackground.Image = TypedDataContext.ShowDataBackground? SampleImages.MonaLisa() : null;
62+
AvaPlot.Plot.FigureBackground.Image = TypedDataContext.ShowFigureBackground ? SampleImages.ScottPlotLogo() : null;
63+
AvaPlot.Plot.DataBackground.Image = TypedDataContext.ShowDataBackground ? SampleImages.MonaLisa() : null;
6364

6465
// set the scaling mode
6566
AvaPlot.Plot.FigureBackground.ImagePosition = TypedDataContext.SelectedImagePosition;

src/ScottPlot5/ScottPlot5 Demos/Avalonia Demo/Demos/ScrollViewerWindow.axaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public partial class ScrollViewerWindow : Window
3232
public ScrollViewerWindow()
3333
{
3434
InitializeComponent();
35-
35+
3636
DataContext = new ScrollViewerViewModel();
3737
TypedDataContext.PropertyChanged += HandleDataContextChanged;
3838

src/ScottPlot5/ScottPlot5 Demos/Avalonia Demo/Demos/SharedAxesWindow.axaml.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ public SharedAxesWindow()
4444

4545
private void HandleDataContextChanged(object? sender, PropertyChangedEventArgs e)
4646
{
47-
if (e.PropertyName == nameof(TypedDataContext.ShareX) || e.PropertyName == nameof(TypedDataContext.ShareY)) {
47+
if (e.PropertyName == nameof(TypedDataContext.ShareX) || e.PropertyName == nameof(TypedDataContext.ShareY))
48+
{
4849
UpdateLinkedPlots();
4950
}
5051
}

src/ScottPlot5/ScottPlot5 Demos/Avalonia Demo/ViewModels/Demos/BackgroundImagesViewModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public partial class BackgroundImagesViewModel : ViewModelBase
1313
{
1414
[ObservableProperty]
1515
private bool _showFigureBackground = true;
16-
16+
1717
[ObservableProperty]
1818
private bool _showDataBackground = true;
1919

0 commit comments

Comments
 (0)