@@ -0,0 +1,323 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="CustomSeriesExamples.cs" company="OxyPlot">
|
||||
// Copyright (c) 2014 OxyPlot contributors
|
||||
// </copyright>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace ExampleLibrary
|
||||
{
|
||||
using System;
|
||||
|
||||
using OxyPlot;
|
||||
using OxyPlot.Axes;
|
||||
using OxyPlot.Series;
|
||||
using OxyPlot.Legends;
|
||||
|
||||
[Examples("Custom series"), Tags("Series")]
|
||||
public static class CustomSeriesExamples
|
||||
{
|
||||
[Example("ErrorSeries")]
|
||||
public static PlotModel ErrorSeries()
|
||||
{
|
||||
int n = 20;
|
||||
|
||||
var model = new PlotModel { Title = "ErrorSeries" };
|
||||
var l = new Legend
|
||||
{
|
||||
LegendPosition = LegendPosition.BottomRight
|
||||
};
|
||||
|
||||
model.Legends.Add(l);
|
||||
|
||||
var s1 = new ErrorSeries { Title = "Measurements" };
|
||||
var random = new Random(31);
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
x += 2 + (random.NextDouble() * 10);
|
||||
y += 1 + random.NextDouble();
|
||||
double xe = 1 + (random.NextDouble() * 2);
|
||||
double ye = 1 + (random.NextDouble() * 2);
|
||||
s1.Points.Add(new ErrorItem(x, y, xe, ye));
|
||||
}
|
||||
|
||||
model.Series.Add(s1);
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("LineSegmentSeries")]
|
||||
public static PlotModel LineSegmentSeries()
|
||||
{
|
||||
var model = new PlotModel { Title = "LineSegmentSeries" };
|
||||
|
||||
var lss1 = new LineSegmentSeries { Title = "The first series" };
|
||||
|
||||
// First segment
|
||||
lss1.Points.Add(new DataPoint(0, 3));
|
||||
lss1.Points.Add(new DataPoint(2, 3.2));
|
||||
|
||||
// Second segment
|
||||
lss1.Points.Add(new DataPoint(2, 2.7));
|
||||
lss1.Points.Add(new DataPoint(7, 2.9));
|
||||
|
||||
model.Series.Add(lss1);
|
||||
|
||||
var lss2 = new LineSegmentSeries { Title = "The second series" };
|
||||
|
||||
// First segment
|
||||
lss2.Points.Add(new DataPoint(1, -3));
|
||||
lss2.Points.Add(new DataPoint(2, 10));
|
||||
|
||||
// Second segment
|
||||
lss2.Points.Add(new DataPoint(0, 4.8));
|
||||
lss2.Points.Add(new DataPoint(7, 2.3));
|
||||
|
||||
// A very short segment
|
||||
lss2.Points.Add(new DataPoint(6, 4));
|
||||
lss2.Points.Add(new DataPoint(6, 4 + 1e-8));
|
||||
|
||||
model.Series.Add(lss2);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("FlagSeries")]
|
||||
public static PlotModel FlagSeries()
|
||||
{
|
||||
var model = new PlotModel { Title = "FlagSeries" };
|
||||
|
||||
var s1 = new FlagSeries { Title = "Incidents", Color = OxyColors.Red };
|
||||
s1.Values.Add(2);
|
||||
s1.Values.Add(3);
|
||||
s1.Values.Add(5);
|
||||
s1.Values.Add(7);
|
||||
s1.Values.Add(11);
|
||||
s1.Values.Add(13);
|
||||
s1.Values.Add(17);
|
||||
s1.Values.Add(19);
|
||||
|
||||
model.Series.Add(s1);
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("MatrixSeries - diagonal matrix")]
|
||||
public static PlotModel DiagonalMatrix()
|
||||
{
|
||||
var model = new PlotModel();
|
||||
|
||||
var matrix = new double[3, 3];
|
||||
matrix[0, 0] = 1;
|
||||
matrix[1, 1] = 2;
|
||||
matrix[2, 2] = 3;
|
||||
|
||||
// Reverse the vertical axis
|
||||
model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, StartPosition = 1, EndPosition = 0 });
|
||||
model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom });
|
||||
model.Series.Add(new MatrixSeries { Matrix = matrix, ShowDiagonal = true });
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("PolarHeatMap")]
|
||||
public static PlotModel PolarHeatMap()
|
||||
{
|
||||
var model = new PlotModel { Title = "Polar heat map", PlotMargins = new OxyThickness(40, 80, 40, 40), PlotType = PlotType.Polar, PlotAreaBorderThickness = new OxyThickness(0) };
|
||||
|
||||
var matrix = new double[2, 2];
|
||||
matrix[0, 0] = 0;
|
||||
matrix[0, 1] = 2;
|
||||
matrix[1, 0] = 1.5;
|
||||
matrix[1, 1] = 0.2;
|
||||
|
||||
model.Axes.Add(new AngleAxis { StartAngle = 0, EndAngle = 360, Minimum = 0, Maximum = 360, MajorStep = 30, MinorStep = 15 });
|
||||
model.Axes.Add(new MagnitudeAxis { Minimum = 0, Maximum = 100, MajorStep = 25, MinorStep = 5 });
|
||||
model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Rainbow(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black });
|
||||
model.Series.Add(new PolarHeatMapSeries { Data = matrix, Angle0 = 30, Angle1 = 150, Magnitude0 = 0, Magnitude1 = 80, Interpolate = false });
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("PolarHeatMap Reversed Angle Axis")]
|
||||
public static PlotModel PolarHeatMapReversedAngleAxis()
|
||||
{
|
||||
var model = new PlotModel { Title = "Polar heat map", PlotMargins = new OxyThickness(40, 80, 40, 40), PlotType = PlotType.Polar, PlotAreaBorderThickness = new OxyThickness(0) };
|
||||
|
||||
var matrix = new double[2, 2];
|
||||
matrix[0, 0] = 0;
|
||||
matrix[0, 1] = 2;
|
||||
matrix[1, 0] = 1.5;
|
||||
matrix[1, 1] = 0.2;
|
||||
|
||||
model.Axes.Add(new AngleAxis { StartAngle = 360, EndAngle = 0, Minimum = 0, Maximum = 360, MajorStep = 30, MinorStep = 15 });
|
||||
model.Axes.Add(new MagnitudeAxis { Minimum = 0, Maximum = 100, MajorStep = 25, MinorStep = 5 });
|
||||
model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Rainbow(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black });
|
||||
model.Series.Add(new PolarHeatMapSeries { Data = matrix, Angle0 = 30, Angle1 = 150, Magnitude0 = 0, Magnitude1 = 80, Interpolate = false });
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("PolarHeatMap Rotated CounterClockwise 90")]
|
||||
public static PlotModel PolarHeatMapRotatedCounterClockwise90()
|
||||
{
|
||||
var model = new PlotModel { Title = "Polar heat map", PlotMargins = new OxyThickness(40, 80, 40, 40), PlotType = PlotType.Polar, PlotAreaBorderThickness = new OxyThickness(0) };
|
||||
|
||||
var matrix = new double[2, 2];
|
||||
matrix[0, 0] = 0;
|
||||
matrix[0, 1] = 2;
|
||||
matrix[1, 0] = 1.5;
|
||||
matrix[1, 1] = 0.2;
|
||||
|
||||
model.Axes.Add(new AngleAxis { StartAngle = 90, EndAngle = 90+360, Minimum = 0, Maximum = 360, MajorStep = 30, MinorStep = 15 });
|
||||
model.Axes.Add(new MagnitudeAxis { Minimum = 0, Maximum = 100, MajorStep = 25, MinorStep = 5 });
|
||||
model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Rainbow(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black });
|
||||
model.Series.Add(new PolarHeatMapSeries { Data = matrix, Angle0 = 30, Angle1 = 150, Magnitude0 = 0, Magnitude1 = 80, Interpolate = false });
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("PolarHeatMap Rotated CounterClockwise on PI degrees")]
|
||||
public static PlotModel PolarHeatMapRotatedCounterClockwisePi()
|
||||
{
|
||||
var model = new PlotModel { Title = "Polar heat map", PlotMargins = new OxyThickness(40, 80, 40, 40), PlotType = PlotType.Polar, PlotAreaBorderThickness = new OxyThickness(0) };
|
||||
|
||||
var matrix = new double[2, 2];
|
||||
matrix[0, 0] = 0;
|
||||
matrix[0, 1] = 2;
|
||||
matrix[1, 0] = 1.5;
|
||||
matrix[1, 1] = 0.2;
|
||||
|
||||
model.Axes.Add(new AngleAxis { StartAngle = Math.PI, EndAngle = Math.PI + 360, Minimum = 0, Maximum = 360, MajorStep = 30, MinorStep = 15 });
|
||||
model.Axes.Add(new MagnitudeAxis { Minimum = 0, Maximum = 100, MajorStep = 25, MinorStep = 5 });
|
||||
model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Rainbow(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black });
|
||||
model.Series.Add(new PolarHeatMapSeries { Data = matrix, Angle0 = 30, Angle1 = 150, Magnitude0 = 0, Magnitude1 = 80, Interpolate = false });
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("PolarHeatMap (interpolated)")]
|
||||
public static PlotModel PolarHeatMapInterpolated()
|
||||
{
|
||||
var model = PolarHeatMap();
|
||||
model.Title = "Polar heat map (interpolated)";
|
||||
((PolarHeatMapSeries)model.Series[0]).Interpolate = true;
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("PolarHeatMap fixed size image")]
|
||||
public static PlotModel PolarHeatMapFixed()
|
||||
{
|
||||
var model = PolarHeatMap();
|
||||
model.Title = "Polar heat map with fixed size image";
|
||||
((PolarHeatMapSeries)model.Series[0]).ImageSize = 800;
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("PolarHeatMap on linear axes")]
|
||||
public static PlotModel PolarHeatMapLinearAxes()
|
||||
{
|
||||
var model = new PlotModel { Title = "Polar heat map on linear axes" };
|
||||
|
||||
var matrix = new double[2, 2];
|
||||
matrix[0, 0] = 0;
|
||||
matrix[0, 1] = 2;
|
||||
matrix[1, 0] = 1.5;
|
||||
matrix[1, 1] = 0.2;
|
||||
|
||||
model.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Minimum = -100, Maximum = 100 });
|
||||
model.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Minimum = 0, Maximum = 100 });
|
||||
model.Axes.Add(new LinearColorAxis { Position = AxisPosition.Right, Palette = OxyPalettes.Rainbow(500), HighColor = OxyColors.Gray, LowColor = OxyColors.Black });
|
||||
model.Series.Add(new PolarHeatMapSeries { Data = matrix, Angle0 = 30, Angle1 = 150, Magnitude0 = 0, Magnitude1 = 80, Interpolate = true });
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("PolarHeatMap linear axes, fixed size image (256x256)")]
|
||||
public static PlotModel PolarHeatMapLinearAxesFixed256()
|
||||
{
|
||||
var model = PolarHeatMapLinearAxes();
|
||||
model.Title = "Polar heat map on linear axes & fixed size image (256x256)";
|
||||
((PolarHeatMapSeries)model.Series[0]).ImageSize = 256;
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("PolarHeatMap linear axes, fixed size image (1000x1000)")]
|
||||
public static PlotModel PolarHeatMapLinearAxesFixed1000()
|
||||
{
|
||||
var model = PolarHeatMapLinearAxes();
|
||||
model.Title = "Polar heat map on linear axes & fixed size image (1000x1000)";
|
||||
((PolarHeatMapSeries)model.Series[0]).ImageSize = 1000;
|
||||
return model;
|
||||
}
|
||||
|
||||
[Example("Design structure matrix (DSM)")]
|
||||
public static PlotModel DesignStructureMatrix()
|
||||
{
|
||||
// See also http://en.wikipedia.org/wiki/Design_structure_matrix
|
||||
var data = new double[7, 7];
|
||||
|
||||
// indexing: data[column,row]
|
||||
data[1, 0] = 1;
|
||||
data[5, 0] = 1;
|
||||
data[3, 1] = 1;
|
||||
data[0, 2] = 1;
|
||||
data[6, 2] = 1;
|
||||
data[4, 3] = 1;
|
||||
data[1, 4] = 1;
|
||||
data[5, 4] = 1;
|
||||
data[2, 5] = 1;
|
||||
data[0, 6] = 1;
|
||||
data[4, 6] = 1;
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
data[i, i] = -1;
|
||||
}
|
||||
|
||||
var model = new PlotModel { Title = "Design structure matrix (DSM)" };
|
||||
model.Axes.Add(new LinearColorAxis { Position = AxisPosition.None, Palette = new OxyPalette(OxyColors.White, OxyColors.LightGreen), LowColor = OxyColors.Black, Minimum = 0, IsAxisVisible = false });
|
||||
var topAxis = new CategoryAxis
|
||||
{
|
||||
Position = AxisPosition.Top
|
||||
};
|
||||
topAxis.Labels.AddRange(new[] { "A", "B", "C", "D", "E", "F", "G" });
|
||||
model.Axes.Add(topAxis);
|
||||
var leftAxis = new CategoryAxis
|
||||
{
|
||||
Position = AxisPosition.Left,
|
||||
StartPosition = 1,
|
||||
EndPosition = 0
|
||||
};
|
||||
leftAxis.Labels.AddRange(new[] { "Element A", "Element B", "Element C", "Element D", "Element E", "Element F", "Element G" });
|
||||
model.Axes.Add(leftAxis);
|
||||
|
||||
var hms = new DesignStructureMatrixSeries
|
||||
{
|
||||
Data = data,
|
||||
Interpolate = false,
|
||||
LabelFormatString = "#",
|
||||
LabelFontSize = 0.25,
|
||||
X0 = 0,
|
||||
X1 = data.GetLength(0) - 1,
|
||||
Y0 = 0,
|
||||
Y1 = data.GetLength(1) - 1,
|
||||
};
|
||||
|
||||
model.Series.Add(hms);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
public class DesignStructureMatrixSeries : HeatMapSeries
|
||||
{
|
||||
protected override string GetLabel(double v, int i, int j)
|
||||
{
|
||||
if (i == j)
|
||||
{
|
||||
return ((CategoryAxis)this.XAxis).Labels[i];
|
||||
}
|
||||
|
||||
return base.GetLabel(v, i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
70
Source/Examples/ExampleLibrary/CustomSeries/ErrorItem.cs
Normal file
70
Source/Examples/ExampleLibrary/CustomSeries/ErrorItem.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="ErrorItem.cs" company="OxyPlot">
|
||||
// Copyright (c) 2014 OxyPlot contributors
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Represents an error item.
|
||||
// </summary>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace ExampleLibrary
|
||||
{
|
||||
using OxyPlot;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an error item.
|
||||
/// </summary>
|
||||
public class ErrorItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ErrorItem" /> class.
|
||||
/// </summary>
|
||||
public ErrorItem()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ErrorItem" /> class.
|
||||
/// </summary>
|
||||
/// <param name="x">The x.</param>
|
||||
/// <param name="y">The y.</param>
|
||||
/// <param name="xerror">The xerror.</param>
|
||||
/// <param name="yerror">The yerror.</param>
|
||||
public ErrorItem(double x, double y, double xerror, double yerror)
|
||||
{
|
||||
this.X = x;
|
||||
this.Y = y;
|
||||
this.XError = xerror;
|
||||
this.YError = yerror;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the X.
|
||||
/// </summary>
|
||||
public double X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Y.
|
||||
/// </summary>
|
||||
public double Y { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the X error.
|
||||
/// </summary>
|
||||
public double XError { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Y error.
|
||||
/// </summary>
|
||||
public double YError { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns c# code that generates this instance.
|
||||
/// </summary>
|
||||
/// <returns>C# code.</returns>
|
||||
public string ToCode()
|
||||
{
|
||||
return CodeGenerator.FormatConstructor(this.GetType(), "{0},{1},{2},{3}", this.X, this.Y, this.XError, this.YError);
|
||||
}
|
||||
}
|
||||
}
|
||||
163
Source/Examples/ExampleLibrary/CustomSeries/ErrorSeries.cs
Normal file
163
Source/Examples/ExampleLibrary/CustomSeries/ErrorSeries.cs
Normal file
@@ -0,0 +1,163 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="ErrorSeries.cs" company="OxyPlot">
|
||||
// Copyright (c) 2014 OxyPlot contributors
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Represents an error series.
|
||||
// </summary>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace ExampleLibrary
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using OxyPlot;
|
||||
using OxyPlot.Series;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an error series.
|
||||
/// </summary>
|
||||
public class ErrorSeries : XYAxisSeries
|
||||
{
|
||||
/// <summary>
|
||||
/// The list of error items.
|
||||
/// </summary>
|
||||
private readonly List<ErrorItem> points = new List<ErrorItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ErrorSeries" /> class.
|
||||
/// </summary>
|
||||
public ErrorSeries()
|
||||
{
|
||||
this.Color = OxyColors.Black;
|
||||
this.StrokeThickness = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color.
|
||||
/// </summary>
|
||||
/// <value>The color.</value>
|
||||
public OxyColor Color { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of points.
|
||||
/// </summary>
|
||||
/// <value>A list of <see cref="ErrorItem" />.</value>
|
||||
public List<ErrorItem> Points
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.points;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stroke thickness.
|
||||
/// </summary>
|
||||
/// <value>The stroke thickness.</value>
|
||||
public double StrokeThickness { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Renders the series on the specified render context.
|
||||
/// </summary>
|
||||
/// <param name="rc">The rendering context.</param>
|
||||
public override void Render(IRenderContext rc)
|
||||
{
|
||||
var points = this.Points;
|
||||
if (points.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.VerifyAxes();
|
||||
|
||||
int n = points.Count;
|
||||
|
||||
// Transform all points to screen coordinates
|
||||
var segments = new List<ScreenPoint>(n * 6);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
var sp = XAxis.Transform(points[i].X, points[i].Y, YAxis);
|
||||
var ei = points[i];
|
||||
double errorx = ei != null ? ei.XError * XAxis.Scale : 0;
|
||||
double errory = ei != null ? ei.YError * Math.Abs(YAxis.Scale) : 0;
|
||||
double d = 4;
|
||||
|
||||
if (errorx > 0)
|
||||
{
|
||||
var p0 = new ScreenPoint(sp.X - (errorx * 0.5), sp.Y);
|
||||
var p1 = new ScreenPoint(sp.X + (errorx * 0.5), sp.Y);
|
||||
segments.Add(p0);
|
||||
segments.Add(p1);
|
||||
segments.Add(new ScreenPoint(p0.X, p0.Y - d));
|
||||
segments.Add(new ScreenPoint(p0.X, p0.Y + d));
|
||||
segments.Add(new ScreenPoint(p1.X, p1.Y - d));
|
||||
segments.Add(new ScreenPoint(p1.X, p1.Y + d));
|
||||
}
|
||||
|
||||
if (errory > 0)
|
||||
{
|
||||
var p0 = new ScreenPoint(sp.X, sp.Y - (errory * 0.5));
|
||||
var p1 = new ScreenPoint(sp.X, sp.Y + (errory * 0.5));
|
||||
segments.Add(p0);
|
||||
segments.Add(p1);
|
||||
segments.Add(new ScreenPoint(p0.X - d, p0.Y));
|
||||
segments.Add(new ScreenPoint(p0.X + d, p0.Y));
|
||||
segments.Add(new ScreenPoint(p1.X - d, p1.Y));
|
||||
segments.Add(new ScreenPoint(p1.X + d, p1.Y));
|
||||
}
|
||||
}
|
||||
|
||||
// clip the line segments with the clipping rectangle
|
||||
for (int i = 0; i + 1 < segments.Count; i += 2)
|
||||
{
|
||||
rc.DrawReducedLine(
|
||||
new[] { segments[i], segments[i + 1] },
|
||||
0,
|
||||
this.GetSelectableColor(this.Color),
|
||||
this.StrokeThickness,
|
||||
this.EdgeRenderingMode,
|
||||
null,
|
||||
LineJoin.Bevel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the legend symbol on the specified rendering context.
|
||||
/// </summary>
|
||||
/// <param name="rc">The rendering context.</param>
|
||||
/// <param name="legendBox">The legend rectangle.</param>
|
||||
public override void RenderLegend(IRenderContext rc, OxyRect legendBox)
|
||||
{
|
||||
double xmid = (legendBox.Left + legendBox.Right) * 0.5;
|
||||
double ymid = (legendBox.Top + legendBox.Bottom) * 0.5;
|
||||
var pts = new[]
|
||||
{
|
||||
new ScreenPoint(legendBox.Left, ymid),
|
||||
new ScreenPoint(legendBox.Right, ymid),
|
||||
new ScreenPoint(legendBox.Left, ymid - 2),
|
||||
new ScreenPoint(legendBox.Left, ymid + 3),
|
||||
new ScreenPoint(legendBox.Right, ymid - 2),
|
||||
new ScreenPoint(legendBox.Right, ymid + 3),
|
||||
|
||||
new ScreenPoint(xmid, legendBox.Top),
|
||||
new ScreenPoint(xmid, legendBox.Bottom),
|
||||
new ScreenPoint(xmid - 2, legendBox.Top),
|
||||
new ScreenPoint(xmid + 3, legendBox.Top),
|
||||
new ScreenPoint(xmid - 2, legendBox.Bottom),
|
||||
new ScreenPoint(xmid + 3, legendBox.Bottom)
|
||||
};
|
||||
rc.DrawLineSegments(pts, this.GetSelectableColor(this.Color), this.StrokeThickness, this.EdgeRenderingMode, null, LineJoin.Miter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the maximum and minimum values of the series.
|
||||
/// </summary>
|
||||
protected override void UpdateMaxMin()
|
||||
{
|
||||
base.UpdateMaxMin();
|
||||
this.InternalUpdateMaxMin(this.points, p => p.X - p.XError, p => p.X + p.XError, p => p.Y - p.YError, p => p.Y + p.YError);
|
||||
}
|
||||
}
|
||||
}
|
||||
236
Source/Examples/ExampleLibrary/CustomSeries/FlagSeries.cs
Normal file
236
Source/Examples/ExampleLibrary/CustomSeries/FlagSeries.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="FlagSeries.cs" company="OxyPlot">
|
||||
// Copyright (c) 2014 OxyPlot contributors
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Renders a 'flag' above the x-axis at the specified positions (in the Values list).
|
||||
// </summary>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace ExampleLibrary
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using OxyPlot;
|
||||
using OxyPlot.Axes;
|
||||
using OxyPlot.Series;
|
||||
|
||||
/// <summary>
|
||||
/// Renders a 'flag' above the x-axis at the specified positions (in the Values list).
|
||||
/// </summary>
|
||||
public class FlagSeries : ItemsSeries
|
||||
{
|
||||
/// <summary>
|
||||
/// The symbol position (y coordinate).
|
||||
/// </summary>
|
||||
private double symbolPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The symbol text size.
|
||||
/// </summary>
|
||||
private OxySize symbolSize;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FlagSeries" /> class.
|
||||
/// </summary>
|
||||
public FlagSeries()
|
||||
{
|
||||
this.Values = new List<double>();
|
||||
this.Color = OxyColors.Black;
|
||||
this.FontSize = 10;
|
||||
this.Symbol = ((char)0xEA).ToString();
|
||||
this.Font = "Wingdings 2";
|
||||
this.TrackerFormatString = "{0}: {1}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the symbols.
|
||||
/// </summary>
|
||||
/// <value>The color.</value>
|
||||
public OxyColor Color { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum value.
|
||||
/// </summary>
|
||||
/// <value>The maximum value.</value>
|
||||
public double MaximumX { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum value.
|
||||
/// </summary>
|
||||
/// <value>The minimum value.</value>
|
||||
public double MinimumX { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the symbol to draw at each value.
|
||||
/// </summary>
|
||||
/// <value>The symbol.</value>
|
||||
public string Symbol { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the values.
|
||||
/// </summary>
|
||||
/// <value>The values.</value>
|
||||
public List<double> Values { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the x-axis.
|
||||
/// </summary>
|
||||
/// <value>The x-axis.</value>
|
||||
public Axis XAxis { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the x-axis key.
|
||||
/// </summary>
|
||||
/// <value>The x-axis key.</value>
|
||||
public string XAxisKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point on the series that is nearest the specified point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <param name="interpolate">Interpolate the series if this flag is set to <c>true</c>.</param>
|
||||
/// <returns>A TrackerHitResult for the current hit.</returns>
|
||||
public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate)
|
||||
{
|
||||
foreach (var v in this.Values)
|
||||
{
|
||||
if (double.IsNaN(v) || v < this.XAxis.ActualMinimum || v > this.XAxis.ActualMaximum)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double x = this.XAxis.Transform(v);
|
||||
var r = new OxyRect(x - (this.symbolSize.Width / 2), this.symbolPosition - this.symbolSize.Height, this.symbolSize.Width, this.symbolSize.Height);
|
||||
if (r.Contains(point))
|
||||
{
|
||||
return new TrackerHitResult
|
||||
{
|
||||
Series = this,
|
||||
DataPoint = new DataPoint(v, double.NaN),
|
||||
Position = new ScreenPoint(x, this.symbolPosition - this.symbolSize.Height),
|
||||
Text = StringHelper.Format(this.ActualCulture, this.TrackerFormatString, null, this.Title, v)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the series on the specified render context.
|
||||
/// </summary>
|
||||
/// <param name="rc">The rendering context.</param>
|
||||
public override void Render(IRenderContext rc)
|
||||
{
|
||||
if (this.XAxis == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.symbolPosition = this.PlotModel.PlotArea.Bottom;
|
||||
this.symbolSize = rc.MeasureText(this.Symbol, this.ActualFont, this.ActualFontSize);
|
||||
foreach (var v in this.Values)
|
||||
{
|
||||
if (double.IsNaN(v) || v < this.XAxis.ClipMinimum || v > this.XAxis.ClipMaximum)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double x = this.XAxis.Transform(v);
|
||||
rc.DrawText(
|
||||
new ScreenPoint(x, this.symbolPosition),
|
||||
this.Symbol,
|
||||
this.Color,
|
||||
this.ActualFont,
|
||||
this.ActualFontSize,
|
||||
this.ActualFontWeight,
|
||||
0,
|
||||
HorizontalAlignment.Center,
|
||||
VerticalAlignment.Bottom);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the legend symbol on the specified render context.
|
||||
/// </summary>
|
||||
/// <param name="rc">The rendering context.</param>
|
||||
/// <param name="legendBox">The legend rectangle.</param>
|
||||
public override void RenderLegend(IRenderContext rc, OxyRect legendBox)
|
||||
{
|
||||
rc.DrawText(
|
||||
legendBox.Center,
|
||||
this.Symbol,
|
||||
this.Color,
|
||||
this.ActualFont,
|
||||
this.ActualFontSize,
|
||||
this.ActualFontWeight,
|
||||
0,
|
||||
HorizontalAlignment.Center,
|
||||
VerticalAlignment.Middle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if this data series requires X/Y axes. (e.g. Pie series do not require axes)
|
||||
/// </summary>
|
||||
/// <returns>True if no axes are required.</returns>
|
||||
protected override bool AreAxesRequired()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the axes of the series is defined.
|
||||
/// </summary>
|
||||
protected override void EnsureAxes()
|
||||
{
|
||||
this.XAxis = this.XAxisKey != null ?
|
||||
this.PlotModel.GetAxis(this.XAxisKey) :
|
||||
this.PlotModel.DefaultXAxis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the data series is using the specified axis.
|
||||
/// </summary>
|
||||
/// <param name="axis">An axis which should be checked if used</param>
|
||||
/// <returns>True if the axis is in use.</returns>
|
||||
protected override bool IsUsing(Axis axis)
|
||||
{
|
||||
return axis == this.XAxis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets default values (colors, line style etc) from the plot model.
|
||||
/// </summary>
|
||||
protected override void SetDefaultValues()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the axis maximum and minimum values.
|
||||
/// </summary>
|
||||
protected override void UpdateAxisMaxMin()
|
||||
{
|
||||
this.XAxis.Include(this.MinimumX);
|
||||
this.XAxis.Include(this.MaximumX);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the data from the ItemsSource.
|
||||
/// </summary>
|
||||
protected override void UpdateData()
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the maximum and minimum values of the series.
|
||||
/// </summary>
|
||||
protected override void UpdateMaxMin()
|
||||
{
|
||||
this.MinimumX = this.Values.Min();
|
||||
this.MaximumX = this.Values.Max();
|
||||
}
|
||||
}
|
||||
}
|
||||
165
Source/Examples/ExampleLibrary/CustomSeries/LineSegmentSeries.cs
Normal file
165
Source/Examples/ExampleLibrary/CustomSeries/LineSegmentSeries.cs
Normal file
@@ -0,0 +1,165 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="LineSegmentSeries.cs" company="OxyPlot">
|
||||
// Copyright (c) 2014 OxyPlot contributors
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Represents a line series where the points collection define line segments.
|
||||
// </summary>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace ExampleLibrary
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OxyPlot;
|
||||
using OxyPlot.Series;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line series where the points collection define line segments.
|
||||
/// </summary>
|
||||
public class LineSegmentSeries : LineSeries
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LineSegmentSeries" /> class.
|
||||
/// </summary>
|
||||
public LineSegmentSeries()
|
||||
{
|
||||
this.ShowVerticals = true;
|
||||
this.Epsilon = 1e-8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show vertical lines where there is no gap in x-coordinate.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if verticals should be shown; otherwise, <c>false</c>.</value>
|
||||
public bool ShowVerticals { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the x-coordinate gap tolerance.
|
||||
/// </summary>
|
||||
/// <value>The epsilon value.</value>
|
||||
public double Epsilon { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Renders the series on the specified rendering context.
|
||||
/// </summary>
|
||||
/// <param name="rc">The rendering context.</param>
|
||||
public override void Render(IRenderContext rc)
|
||||
{
|
||||
if (Points.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Points.Count % 2 != 0)
|
||||
{
|
||||
throw new InvalidOperationException("The number of points should be even.");
|
||||
}
|
||||
|
||||
if (this.XAxis == null || this.YAxis == null)
|
||||
{
|
||||
throw new InvalidOperationException("Axis has not been defined.");
|
||||
}
|
||||
|
||||
var screenPoints = Points.Select(this.Transform).ToList();
|
||||
var verticalLines = new List<ScreenPoint>();
|
||||
|
||||
for (int i = 0; i < screenPoints.Count; i += 2)
|
||||
{
|
||||
if (screenPoints[i].DistanceToSquared(screenPoints[i + 1]) < this.StrokeThickness)
|
||||
{
|
||||
screenPoints[i] = new ScreenPoint(screenPoints[i].X - (this.StrokeThickness * 0.5), screenPoints[i].Y);
|
||||
screenPoints[i + 1] = new ScreenPoint(screenPoints[i].X + (this.StrokeThickness * 0.5), screenPoints[i].Y);
|
||||
}
|
||||
|
||||
if (this.ShowVerticals && i > 0 && Math.Abs(screenPoints[i - 1].X - screenPoints[i].X) < this.Epsilon)
|
||||
{
|
||||
verticalLines.Add(screenPoints[i - 1]);
|
||||
verticalLines.Add(screenPoints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.StrokeThickness > 0)
|
||||
{
|
||||
if (this.LineStyle != LineStyle.None)
|
||||
{
|
||||
rc.DrawLineSegments(screenPoints, this.ActualColor, this.StrokeThickness, this.EdgeRenderingMode, this.LineStyle.GetDashArray(), this.LineJoin);
|
||||
}
|
||||
|
||||
rc.DrawLineSegments(verticalLines, this.ActualColor, this.StrokeThickness / 3, this.EdgeRenderingMode, LineStyle.Dash.GetDashArray(), this.LineJoin);
|
||||
}
|
||||
|
||||
rc.DrawMarkers(screenPoints, this.MarkerType, null, this.MarkerSize, this.MarkerFill, this.MarkerStroke, this.MarkerStrokeThickness, this.EdgeRenderingMode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point on the series that is nearest the specified point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <param name="interpolate">Interpolate the series if this flag is set to <c>true</c>.</param>
|
||||
/// <returns>A TrackerHitResult for the current hit.</returns>
|
||||
public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate)
|
||||
{
|
||||
var points = this.Points;
|
||||
|
||||
if (points == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var spn = default(ScreenPoint);
|
||||
var dpn = default(DataPoint);
|
||||
double index = -1;
|
||||
|
||||
double minimumDistance = double.MaxValue;
|
||||
|
||||
for (int i = 0; i + 1 < points.Count; i += 2)
|
||||
{
|
||||
var p1 = points[i];
|
||||
var p2 = points[i + 1];
|
||||
if (!this.IsValidPoint(p1) || !this.IsValidPoint(p2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var sp1 = this.Transform(p1);
|
||||
var sp2 = this.Transform(p2);
|
||||
|
||||
// Find the nearest point on the line segment.
|
||||
var spl = ScreenPointHelper.FindPointOnLine(point, sp1, sp2);
|
||||
|
||||
if (ScreenPoint.IsUndefined(spl))
|
||||
{
|
||||
// P1 && P2 coincident
|
||||
continue;
|
||||
}
|
||||
|
||||
double l2 = (point - spl).LengthSquared;
|
||||
|
||||
if (l2 < minimumDistance)
|
||||
{
|
||||
double u = (spl - sp1).Length / (sp2 - sp1).Length;
|
||||
dpn = new DataPoint(p1.X + (u * (p2.X - p1.X)), p1.Y + (u * (p2.Y - p1.Y)));
|
||||
spn = spl;
|
||||
minimumDistance = l2;
|
||||
index = i + u;
|
||||
}
|
||||
}
|
||||
|
||||
if (minimumDistance < double.MaxValue)
|
||||
{
|
||||
return new TrackerHitResult
|
||||
{
|
||||
Series = this,
|
||||
DataPoint = dpn,
|
||||
Position = spn,
|
||||
Item = this.GetItem((int)index),
|
||||
Index = index
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
236
Source/Examples/ExampleLibrary/CustomSeries/MatrixSeries.cs
Normal file
236
Source/Examples/ExampleLibrary/CustomSeries/MatrixSeries.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="MatrixSeries.cs" company="OxyPlot">
|
||||
// Copyright (c) 2014 OxyPlot contributors
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Provides a series that visualizes the structure of a matrix.
|
||||
// </summary>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace ExampleLibrary
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using OxyPlot;
|
||||
using OxyPlot.Series;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a series that visualizes the structure of a matrix.
|
||||
/// </summary>
|
||||
public class MatrixSeries : XYAxisSeries
|
||||
{
|
||||
/// <summary>
|
||||
/// The image
|
||||
/// </summary>
|
||||
private OxyImage image;
|
||||
|
||||
/// <summary>
|
||||
/// The matrix
|
||||
/// </summary>
|
||||
private double[,] matrix;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MatrixSeries" /> class.
|
||||
/// </summary>
|
||||
public MatrixSeries()
|
||||
{
|
||||
this.GridInterval = 1;
|
||||
this.ShowDiagonal = false;
|
||||
this.MinimumGridLineDistance = 4;
|
||||
this.GridColor = OxyColors.LightGray;
|
||||
this.BorderColor = OxyColors.Gray;
|
||||
this.NotZeroColor = OxyColors.Black;
|
||||
this.ZeroTolerance = 0;
|
||||
this.TrackerFormatString = "{0}\r\n[{1},{2}] = {3}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the matrix.
|
||||
/// </summary>
|
||||
public double[,] Matrix
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.matrix;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this.image = null;
|
||||
this.matrix = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the interval between the grid lines (the grid is hidden if value is 0).
|
||||
/// </summary>
|
||||
public int GridInterval { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to show the diagonal.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if the diagonal should be shown; otherwise, <c>false</c>.</value>
|
||||
public bool ShowDiagonal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum grid line distance.
|
||||
/// </summary>
|
||||
public double MinimumGridLineDistance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the grid.
|
||||
/// </summary>
|
||||
/// <value>The color of the grid.</value>
|
||||
public OxyColor GridColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the border around the matrix.
|
||||
/// </summary>
|
||||
/// <value>The color of the border.</value>
|
||||
public OxyColor BorderColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color of the not zero elements of the matrix.
|
||||
/// </summary>
|
||||
public OxyColor NotZeroColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the zero tolerance (inclusive).
|
||||
/// </summary>
|
||||
/// <value>The zero tolerance.</value>
|
||||
public double ZeroTolerance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point on the series that is nearest the specified point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <param name="interpolate">Interpolate the series if this flag is set to <c>true</c>.</param>
|
||||
/// <returns>A TrackerHitResult for the current hit.</returns>
|
||||
public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate)
|
||||
{
|
||||
var dp = this.InverseTransform(point);
|
||||
int i = (int)dp.Y;
|
||||
int j = (int)dp.X;
|
||||
|
||||
if (i >= 0 && i < this.matrix.GetLength(0) && j >= 0 && j < this.matrix.GetLength(1))
|
||||
{
|
||||
var value = this.matrix[i, j];
|
||||
return new TrackerHitResult
|
||||
{
|
||||
Series = this,
|
||||
DataPoint = dp,
|
||||
Position = point,
|
||||
Item = null,
|
||||
Index = -1,
|
||||
Text = StringHelper.Format(this.ActualCulture, this.TrackerFormatString, null, this.Title, i, j, value)
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the series on the specified render context.
|
||||
/// </summary>
|
||||
/// <param name="rc">The rendering context.</param>
|
||||
public override void Render(IRenderContext rc)
|
||||
{
|
||||
if (this.Matrix == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int m = this.Matrix.GetLength(0);
|
||||
int n = this.Matrix.GetLength(1);
|
||||
var p0 = this.Transform(0, 0);
|
||||
var p1 = this.Transform(n, m);
|
||||
|
||||
// note matrix index [i,j] maps to image index [j,i]
|
||||
if (this.image == null)
|
||||
{
|
||||
var pixels = new OxyColor[n, m];
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
pixels[j, i] = Math.Abs(this.Matrix[i, j]) <= this.ZeroTolerance ? OxyColors.Transparent : this.NotZeroColor;
|
||||
}
|
||||
}
|
||||
|
||||
this.image = OxyImage.Create(pixels, ImageFormat.Png);
|
||||
}
|
||||
|
||||
var x0 = Math.Min(p0.X, p1.X);
|
||||
var y0 = Math.Min(p0.Y, p1.Y);
|
||||
var w = Math.Abs(p0.X - p1.X);
|
||||
var h = Math.Abs(p0.Y - p1.Y);
|
||||
rc.DrawImage(this.image, x0, y0, w, h, 1, false);
|
||||
|
||||
var points = new List<ScreenPoint>();
|
||||
if (this.GridInterval > 0)
|
||||
{
|
||||
var p2 = this.Transform(this.GridInterval, this.GridInterval);
|
||||
if (Math.Abs(p2.Y - p0.Y) > this.MinimumGridLineDistance)
|
||||
{
|
||||
for (int i = 1; i < n; i += this.GridInterval)
|
||||
{
|
||||
points.Add(this.Transform(0, i));
|
||||
points.Add(this.Transform(n, i));
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.Abs(p2.X - p0.X) > this.MinimumGridLineDistance)
|
||||
{
|
||||
for (int j = 1; j < m; j += this.GridInterval)
|
||||
{
|
||||
points.Add(this.Transform(j, 0));
|
||||
points.Add(this.Transform(j, m));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.ShowDiagonal)
|
||||
{
|
||||
points.Add(this.Transform(0, 0));
|
||||
points.Add(this.Transform(n, m));
|
||||
}
|
||||
|
||||
rc.DrawLineSegments(points, this.GridColor, 1, this.EdgeRenderingMode, null, LineJoin.Miter);
|
||||
|
||||
if (this.BorderColor.IsVisible())
|
||||
{
|
||||
var borderPoints = new[]
|
||||
{
|
||||
this.Transform(0, 0),
|
||||
this.Transform(m, 0),
|
||||
this.Transform(0, n),
|
||||
this.Transform(m, n),
|
||||
this.Transform(0, 0),
|
||||
this.Transform(0, n),
|
||||
this.Transform(m, 0),
|
||||
this.Transform(m, n)
|
||||
};
|
||||
|
||||
rc.DrawLineSegments(borderPoints, this.BorderColor, 1, this.EdgeRenderingMode, null, LineJoin.Miter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the maximum and minimum values of the series.
|
||||
/// </summary>
|
||||
protected override void UpdateMaxMin()
|
||||
{
|
||||
base.UpdateMaxMin();
|
||||
if (this.Matrix == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.MinX = 0;
|
||||
this.MaxX = this.Matrix.GetLength(1);
|
||||
this.MinY = 0;
|
||||
this.MaxY = this.Matrix.GetLength(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,397 @@
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
// <copyright file="PolarHeatMapSeries.cs" company="OxyPlot">
|
||||
// Copyright (c) 2014 OxyPlot contributors
|
||||
// </copyright>
|
||||
// <summary>
|
||||
// Implements a polar heat map series.
|
||||
// </summary>
|
||||
// --------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace OxyPlot.Series
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using OxyPlot.Axes;
|
||||
|
||||
/// <summary>
|
||||
/// Implements a polar heat map series.
|
||||
/// </summary>
|
||||
public class PolarHeatMapSeries : XYAxisSeries
|
||||
{
|
||||
/// <summary>
|
||||
/// The image
|
||||
/// </summary>
|
||||
private OxyImage image;
|
||||
|
||||
/// <summary>
|
||||
/// The pixels
|
||||
/// </summary>
|
||||
private OxyColor[,] pixels;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PolarHeatMapSeries" /> class.
|
||||
/// </summary>
|
||||
public PolarHeatMapSeries()
|
||||
{
|
||||
this.Interpolate = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the size of the image - if set to 0, the image will be generated at every update.
|
||||
/// </summary>
|
||||
/// <value>The size of the image.</value>
|
||||
public int ImageSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the x-coordinate of the left column mid point.
|
||||
/// </summary>
|
||||
public double Angle0 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the x-coordinate of the right column mid point.
|
||||
/// </summary>
|
||||
public double Angle1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the y-coordinate of the top row mid point.
|
||||
/// </summary>
|
||||
public double Magnitude0 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the y-coordinate of the bottom row mid point.
|
||||
/// </summary>
|
||||
public double Magnitude1 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the data array.
|
||||
/// </summary>
|
||||
/// <remarks>Note that the indices of the data array refer to [x,y].</remarks>
|
||||
public double[,] Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to interpolate when rendering.
|
||||
/// </summary>
|
||||
/// <remarks>This property is not supported on all platforms.</remarks>
|
||||
public bool Interpolate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum value of the dataset.
|
||||
/// </summary>
|
||||
public double MinValue { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum value of the dataset.
|
||||
/// </summary>
|
||||
public double MaxValue { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color axis.
|
||||
/// </summary>
|
||||
/// <value>The color axis.</value>
|
||||
public IColorAxis ColorAxis { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the color axis key.
|
||||
/// </summary>
|
||||
/// <value>The color axis key.</value>
|
||||
public string ColorAxisKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Renders the series on the specified render context.
|
||||
/// </summary>
|
||||
/// <param name="rc">The rendering context.</param>
|
||||
public override void Render(IRenderContext rc)
|
||||
{
|
||||
if (this.Data == null)
|
||||
{
|
||||
this.image = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.ImageSize > 0)
|
||||
{
|
||||
this.RenderFixed(rc, this.PlotModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.RenderDynamic(rc, this.PlotModel);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders by an image sized from the available plot area.
|
||||
/// </summary>
|
||||
/// <param name="rc">The rc.</param>
|
||||
/// <param name="model">The model.</param>
|
||||
public void RenderDynamic(IRenderContext rc, PlotModel model)
|
||||
{
|
||||
int m = this.Data.GetLength(0);
|
||||
int n = this.Data.GetLength(1);
|
||||
|
||||
// get the available plot area
|
||||
var dest = model.PlotArea;
|
||||
int width = (int)dest.Width;
|
||||
int height = (int)dest.Height;
|
||||
if (width == 0 || height == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.pixels == null || this.pixels.GetLength(0) != height || this.pixels.GetLength(1) != width)
|
||||
{
|
||||
this.pixels = new OxyColor[width, height];
|
||||
}
|
||||
|
||||
var p = this.pixels;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
// transform from screen to magnitude/angle
|
||||
var sp = new ScreenPoint(dest.Left + x, dest.Top + y);
|
||||
var xy = this.InverseTransform(sp);
|
||||
double angle;
|
||||
double magnitude;
|
||||
if (this.PlotModel.PlotType != PlotType.Polar)
|
||||
{
|
||||
angle = Math.Atan2(xy.Y, xy.X) / Math.PI * 180;
|
||||
magnitude = Math.Sqrt((xy.X * xy.X) + (xy.Y * xy.Y));
|
||||
}
|
||||
else
|
||||
{
|
||||
angle = xy.Y / Math.PI * 180;
|
||||
magnitude = xy.X;
|
||||
while (angle < 0)
|
||||
{
|
||||
angle += 360;
|
||||
}
|
||||
while (angle > 360)
|
||||
{
|
||||
angle -= 360;
|
||||
}
|
||||
}
|
||||
|
||||
// transform to indices in the Data array
|
||||
var ii = (angle - this.Angle0) / (this.Angle1 - this.Angle0) * m;
|
||||
var jj = (magnitude - this.Magnitude0) / (this.Magnitude1 - this.Magnitude0) * n;
|
||||
if (ii >= 0 && ii < m && jj >= 0 && jj < n)
|
||||
{
|
||||
// get the (interpolated) value
|
||||
var value = this.GetValue(ii, jj);
|
||||
|
||||
// use the color axis to get the color
|
||||
p[x, y] = OxyColor.FromAColor(160, this.ColorAxis.GetColor(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
// outside the range of the Data array
|
||||
p[x, y] = OxyColors.Transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the PNG image
|
||||
this.image = OxyImage.Create(p, ImageFormat.Png);
|
||||
|
||||
// Render the image
|
||||
rc.DrawImage(this.image, dest.Left, dest.Top, dest.Width, dest.Height, 1, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the image next time the series is rendered.
|
||||
/// </summary>
|
||||
public void Refresh()
|
||||
{
|
||||
this.image = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders by scaling a fixed image.
|
||||
/// </summary>
|
||||
/// <param name="rc">The render context.</param>
|
||||
/// <param name="model">The model.</param>
|
||||
public void RenderFixed(IRenderContext rc, PlotModel model)
|
||||
{
|
||||
if (image == null)
|
||||
{
|
||||
int m = this.Data.GetLength(0);
|
||||
int n = this.Data.GetLength(1);
|
||||
|
||||
int width = this.ImageSize;
|
||||
int height = this.ImageSize;
|
||||
if (this.pixels == null || this.pixels.GetLength(0) != height || this.pixels.GetLength(1) != width)
|
||||
{
|
||||
this.pixels = new OxyColor[width, height];
|
||||
}
|
||||
|
||||
var p = this.pixels;
|
||||
for (int yi = 0; yi < height; yi++)
|
||||
{
|
||||
for (int xi = 0; xi < width; xi++)
|
||||
{
|
||||
double x = (xi - width * 0.5) / (width * 0.5) * this.Magnitude1;
|
||||
double y = -(yi - height * 0.5) / (height * 0.5) * this.Magnitude1;
|
||||
|
||||
double angle = Math.Atan2(y, x) / Math.PI * 180;
|
||||
double magnitude = Math.Sqrt(x * x + y * y);
|
||||
|
||||
while (angle < 0)
|
||||
{
|
||||
angle += 360;
|
||||
}
|
||||
while (angle > 360)
|
||||
{
|
||||
angle -= 360;
|
||||
}
|
||||
|
||||
// transform to indices in the Data array
|
||||
var ii = (angle - this.Angle0) / (this.Angle1 - this.Angle0) * m;
|
||||
var jj = (magnitude - this.Magnitude0) / (this.Magnitude1 - this.Magnitude0) * n;
|
||||
if (ii >= 0 && ii < m && jj >= 0 && jj < n)
|
||||
{
|
||||
// get the (interpolated) value
|
||||
var value = this.GetValue(ii, jj);
|
||||
|
||||
// use the color axis to get the color
|
||||
p[xi, yi] = OxyColor.FromAColor(160, this.ColorAxis.GetColor(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
// outside the range of the Data array
|
||||
p[xi, yi] = OxyColors.Transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the PNG image
|
||||
this.image = OxyImage.Create(p, ImageFormat.Png);
|
||||
}
|
||||
|
||||
OxyRect dest;
|
||||
if (this.PlotModel.PlotType != PlotType.Polar)
|
||||
{
|
||||
var topleft = this.Transform(-this.Magnitude1, this.Magnitude1);
|
||||
var bottomright = this.Transform(this.Magnitude1, -this.Magnitude1);
|
||||
dest = new OxyRect(topleft.X, topleft.Y, bottomright.X - topleft.X, bottomright.Y - topleft.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
var top = this.Transform(this.Magnitude1, 90);
|
||||
var bottom = this.Transform(this.Magnitude1, 270);
|
||||
var left = this.Transform(this.Magnitude1, 180);
|
||||
var right = this.Transform(this.Magnitude1, 0);
|
||||
dest = new OxyRect(left.X, top.Y, right.X - left.X, bottom.Y - top.Y);
|
||||
}
|
||||
|
||||
// Render the image
|
||||
rc.DrawImage(this.image, dest.Left, dest.Top, dest.Width, dest.Height, 1, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value at the specified data indices.
|
||||
/// </summary>
|
||||
/// <param name="ii">The first index in the Data array.</param>
|
||||
/// <param name="jj">The second index in the Data array.</param>
|
||||
/// <returns>The value.</returns>
|
||||
protected virtual double GetValue(double ii, double jj)
|
||||
{
|
||||
if (!this.Interpolate)
|
||||
{
|
||||
var i = (int)Math.Floor(ii);
|
||||
var j = (int)Math.Floor(jj);
|
||||
return this.Data[i, j];
|
||||
}
|
||||
|
||||
ii -= 0.5;
|
||||
jj -= 0.5;
|
||||
|
||||
// bi-linear interpolation http://en.wikipedia.org/wiki/Bilinear_interpolation
|
||||
var r = (int)Math.Floor(ii);
|
||||
var c = (int)Math.Floor(jj);
|
||||
|
||||
int r0 = r > 0 ? r : 0;
|
||||
int r1 = r + 1 < this.Data.GetLength(0) ? r + 1 : r;
|
||||
int c0 = c > 0 ? c : 0;
|
||||
int c1 = c + 1 < this.Data.GetLength(1) ? c + 1 : c;
|
||||
|
||||
double v00 = this.Data[r0, c0];
|
||||
double v01 = this.Data[r0, c1];
|
||||
double v10 = this.Data[r1, c0];
|
||||
double v11 = this.Data[r1, c1];
|
||||
|
||||
double di = ii - r;
|
||||
double dj = jj - c;
|
||||
|
||||
double v0 = (v00 * (1 - dj)) + (v01 * dj);
|
||||
double v1 = (v10 * (1 - dj)) + (v11 * dj);
|
||||
|
||||
return (v0 * (1 - di)) + (v1 * di);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the point on the series that is nearest the specified point.
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <param name="interpolate">Interpolate the series if this flag is set to <c>true</c>.</param>
|
||||
/// <returns>A TrackerHitResult for the current hit.</returns>
|
||||
public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the axes of the series is defined.
|
||||
/// </summary>
|
||||
protected override void EnsureAxes()
|
||||
{
|
||||
base.EnsureAxes();
|
||||
|
||||
this.ColorAxis = this.ColorAxisKey != null ?
|
||||
this.PlotModel.GetAxis(this.ColorAxisKey) as IColorAxis :
|
||||
this.PlotModel.DefaultColorAxis as IColorAxis;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the maximum and minimum values of the series.
|
||||
/// </summary>
|
||||
protected override void UpdateMaxMin()
|
||||
{
|
||||
base.UpdateMaxMin();
|
||||
|
||||
this.MinValue = this.GetData().Min();
|
||||
this.MaxValue = this.GetData().Max();
|
||||
|
||||
//this.XAxis.Include(this.MinX);
|
||||
//this.XAxis.Include(this.MaxX);
|
||||
|
||||
//this.YAxis.Include(this.MinY);
|
||||
//this.YAxis.Include(this.MaxY);
|
||||
|
||||
var colorAxis = this.ColorAxis as Axis;
|
||||
if (colorAxis != null)
|
||||
{
|
||||
colorAxis.Include(this.MinValue);
|
||||
colorAxis.Include(this.MaxValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data as a sequence (LINQ-friendly).
|
||||
/// </summary>
|
||||
/// <returns>The sequence of data.</returns>
|
||||
protected IEnumerable<double> GetData()
|
||||
{
|
||||
int m = this.Data.GetLength(0);
|
||||
int n = this.Data.GetLength(1);
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
for (int j = 0; j < n; j++)
|
||||
{
|
||||
yield return this.Data[i, j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user