-
-
Save VisualMelon/3d35da1a7fc8ee97a4b4c5cd8862e282 to your computer and use it in GitHub Desktop.
OxyPlot Cursors
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using OxyPlot; | |
using OxyPlot.Annotations; | |
using OxyPlot.Axes; | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
namespace Utilities | |
{ | |
public class CursorPair | |
{ | |
public CursorPair(CursorAnnotation low, CursorAnnotation high) | |
{ | |
Low = low ?? throw new ArgumentNullException(nameof(low)); | |
High = high ?? throw new ArgumentNullException(nameof(high)); | |
} | |
public CursorAnnotation Low { get; } | |
public CursorAnnotation High { get; } | |
public bool AnyOn => Low.IsOn || High.IsOn; | |
public void GetInfo(out double low, out double high, out double delta) | |
{ | |
low = Low.Position; | |
high = High.Position; | |
delta = high - low; | |
} | |
} | |
public class CursorInfoAnnotation : Annotation | |
{ | |
private List<CursorPair> Pairs { get; } = new List<CursorPair>(); | |
public ScreenPoint Position { get; set; } | |
public HorizontalAlignment HorizontalAlignment { get; set; } = HorizontalAlignment.Left; | |
public VerticalAlignment VerticalAlignment { get; set; } = VerticalAlignment.Top; | |
public double Width { get; } = 200; | |
public void AddPair(CursorAnnotation low, CursorAnnotation high) | |
{ | |
Pairs.Add(new CursorPair(low, high)); | |
} | |
private int OnCount => Pairs.Count(p => p.AnyOn); | |
private OxyRect Bounds { get; set; } | |
public CursorInfoAnnotation() | |
{ | |
this.MouseDown += CursorInfoAnnotation_MouseDown; | |
this.MouseUp += CursorInfoAnnotation_MouseUp; | |
this.MouseMove += CursorInfoAnnotation_MouseMove; | |
} | |
public override void Render(IRenderContext rc) | |
{ | |
int on = OnCount; | |
if (on == 0) | |
return; | |
var subHeight = rc.MeasureText("0123456789").Height + 2; | |
var subWidth = Width / 3; | |
var height = subHeight * (on + 1); | |
var x = Position.X + (Sign(HorizontalAlignment) + 1) * Width / 2; | |
var y = Position.Y + (Sign(VerticalAlignment) + 1) * height / 2; | |
var topLeft = new ScreenPoint(x, y); | |
Bounds = new OxyRect(topLeft, new OxySize(Width, height)); | |
rc.DrawRectangle(Bounds, OxyColor.FromAColor(128, OxyColors.LightSteelBlue), OxyColors.Black, 1); | |
rc.DrawText(new ScreenPoint(x + subWidth * 0.5, y + subHeight * 0.5), "Low", OxyColors.Black, horizontalAlignment: HorizontalAlignment.Center, verticalAlignment: VerticalAlignment.Middle); | |
rc.DrawText(new ScreenPoint(x + subWidth * 1.5, y + subHeight * 0.5), "High", OxyColors.Black, horizontalAlignment: HorizontalAlignment.Center, verticalAlignment: VerticalAlignment.Middle); | |
rc.DrawText(new ScreenPoint(x + subWidth * 2.5, y + subHeight * 0.5), "Delta", OxyColors.Black, horizontalAlignment: HorizontalAlignment.Center, verticalAlignment: VerticalAlignment.Middle); | |
foreach (var p in Pairs.Where(p => p.AnyOn)) | |
{ | |
y += subHeight; | |
p.GetInfo(out double low, out double high, out double delta); | |
if (!double.IsNaN(low)) | |
rc.DrawText(new ScreenPoint(x + subWidth * 0.5, y + subHeight * 0.5), low.ToString("G5"), p.Low.Color, horizontalAlignment: HorizontalAlignment.Center, verticalAlignment: VerticalAlignment.Middle); | |
if (!double.IsNaN(high)) | |
rc.DrawText(new ScreenPoint(x + subWidth * 1.5, y + subHeight * 0.5), high.ToString("G5"), p.High.Color, horizontalAlignment: HorizontalAlignment.Center, verticalAlignment: VerticalAlignment.Middle); | |
if (!double.IsNaN(delta)) | |
rc.DrawText(new ScreenPoint(x + subWidth * 2.5, y + subHeight * 0.5), delta.ToString("G5"), p.Low.Color, horizontalAlignment: HorizontalAlignment.Center, verticalAlignment: VerticalAlignment.Middle); | |
} | |
} | |
protected override HitTestResult HitTestOverride(HitTestArguments args) | |
{ | |
if (Bounds.Contains(args.Point)) | |
{ | |
return new HitTestResult(this, args.Point); | |
} | |
return null; | |
} | |
private bool moused = false; | |
private ScreenVector mouseOffset; | |
private void CursorInfoAnnotation_MouseMove(object sender, OxyMouseEventArgs e) | |
{ | |
if (moused && e.Position.X >= 0 && e.Position.Y >= 0 && e.Position.X < PlotModel.Width && e.Position.Y < PlotModel.Height) | |
{ | |
Position = e.Position - mouseOffset; | |
PlotModel.InvalidatePlot(false); | |
} | |
} | |
private void CursorInfoAnnotation_MouseUp(object sender, OxyMouseEventArgs e) | |
{ | |
moused = false; | |
} | |
private void CursorInfoAnnotation_MouseDown(object sender, OxyMouseDownEventArgs e) | |
{ | |
if (e.ChangedButton == OxyMouseButton.Left) | |
{ | |
moused = true; | |
mouseOffset = e.Position - Position; | |
e.Handled = true; | |
} | |
} | |
private int Sign(HorizontalAlignment horizontalAlignment) | |
{ | |
switch (horizontalAlignment) | |
{ | |
case HorizontalAlignment.Left: | |
return -1; | |
case HorizontalAlignment.Center: | |
return 0; | |
case HorizontalAlignment.Right: | |
return +1; | |
default: | |
throw new ArgumentException(nameof(horizontalAlignment)); | |
} | |
} | |
private int Sign(VerticalAlignment verticalAlignment) | |
{ | |
switch (verticalAlignment) | |
{ | |
case VerticalAlignment.Top: | |
return -1; | |
case VerticalAlignment.Middle: | |
return 0; | |
case VerticalAlignment.Bottom: | |
return +1; | |
default: | |
throw new ArgumentException(nameof(verticalAlignment)); | |
} | |
} | |
} | |
public class CursorAnnotation : Annotation | |
{ | |
public double Position { get; set; } = double.NaN; | |
public OxyColor Color { get; set; } = OxyColors.LightGray; | |
public bool ReverseX { get; set; } = false; | |
public bool ReverseY { get; set; } = false; | |
public double BoxSize { get; set; } = 8; | |
public double LineThickness { get; set; } = 1; | |
public bool IsEnabled { get; set; } = true; | |
public CursorAnnotation(string xAxisKey, string yAxisKey) | |
{ | |
XAxisKey = xAxisKey; | |
YAxisKey = yAxisKey; | |
Layer = AnnotationLayer.BelowAxes; | |
this.MouseDown += CursorAnnotation_MouseDown; | |
this.MouseUp += CursorAnnotation_MouseUp; | |
this.MouseMove += CursorAnnotation_MouseMove; | |
} | |
public bool IsOn => IsEnabled && XAxis.IsAxisVisible && !double.IsNaN(Position); | |
public void GetPoints(out ScreenPoint near, out ScreenPoint far, out ScreenPoint off) | |
{ | |
var p = Position; | |
if (double.IsNaN(p)) | |
p = ReverseX ? XAxis.ActualMaximum : XAxis.ActualMinimum; | |
if (this.YAxisKey == null) | |
{ | |
var xp = XAxis.Transform(p); | |
near = new ScreenPoint(xp, XAxis.IsHorizontal() ? XAxis.PlotModel.PlotArea.Bottom : XAxis.PlotModel.PlotArea.Left); | |
far = new ScreenPoint(xp, XAxis.IsHorizontal() ? XAxis.PlotModel.PlotArea.Top : XAxis.PlotModel.PlotArea.Right); | |
} | |
else | |
{ | |
near = XAxis.Transform(p, YAxis.ActualMinimum, YAxis); | |
far = XAxis.Transform(p, YAxis.ActualMaximum, YAxis); | |
} | |
if (ReverseY) | |
{ | |
var t = near; | |
near = far; | |
far = t; | |
} | |
if (XAxis.IsVertical()) | |
{ | |
near = new ScreenPoint(near.Y, near.X); | |
far = new ScreenPoint(far.Y, far.X); | |
off = new ScreenPoint(near.X + (ReverseY ? BoxSize / 2 : -BoxSize / 2), far.Y); | |
} | |
else | |
{ | |
off = new ScreenPoint(near.X, far.Y + (ReverseY ? BoxSize / 2 : -BoxSize / 2)); | |
} | |
} | |
public double TranslateScreenPoint(ScreenPoint sp) | |
{ | |
if (XAxis.IsVertical()) | |
sp = new ScreenPoint(sp.Y, sp.X); | |
var newPosition = XAxis.InverseTransform(sp.X, sp.Y, YAxis).X; | |
if (XAxis.ActualMinimum > newPosition || XAxis.ActualMaximum < newPosition) | |
newPosition = double.NaN; | |
return newPosition; | |
} | |
public override void Render(IRenderContext rc) | |
{ | |
if (!IsEnabled || !XAxis.IsAxisVisible || XAxis.ActualMinimum > Position || XAxis.ActualMaximum < Position) | |
return; | |
GetPoints(out ScreenPoint near, out ScreenPoint far, out ScreenPoint off); | |
rc.DrawLine(new[] { near, far }, Color, LineThickness); | |
var offRect = new OxyRect(off.X - BoxSize / 2, off.Y - BoxSize / 2, BoxSize, BoxSize); | |
rc.DrawRectangle(offRect, Color, OxyColors.Transparent, 0); | |
} | |
protected override HitTestResult HitTestOverride(HitTestArguments args) | |
{ | |
if (!IsEnabled || !XAxis.IsAxisVisible || XAxis.ActualMinimum > Position || XAxis.ActualMaximum < Position) | |
return null; | |
GetPoints(out ScreenPoint near, out ScreenPoint far, out ScreenPoint off); | |
var lineHitRect = new OxyRect(near, far).Inflate(new OxyThickness(LineThickness + 1)); | |
var offRect = new OxyRect(off.X - BoxSize / 2, off.Y - BoxSize / 2, BoxSize, BoxSize); | |
if (lineHitRect.Contains(args.Point) || offRect.Contains(args.Point)) | |
{ | |
return new HitTestResult(this, near); | |
} | |
return null; | |
} | |
private bool moused = false; | |
private void CursorAnnotation_MouseMove(object sender, OxyMouseEventArgs e) | |
{ | |
if (moused) | |
{ | |
Position = TranslateScreenPoint(e.Position); | |
PlotModel.InvalidatePlot(false); | |
} | |
} | |
private void CursorAnnotation_MouseUp(object sender, OxyMouseEventArgs e) | |
{ | |
moused = false; | |
} | |
private void CursorAnnotation_MouseDown(object sender, OxyMouseDownEventArgs e) | |
{ | |
if (e.ChangedButton == OxyMouseButton.Left) | |
{ | |
moused = true; | |
e.Handled = true; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var cusorInfo = new CursorInfoAnnotation(); | |
plot.Annotations.Add(cusorInfo); | |
AddCursorPair(cusorInfo, "mv", "t", false, true, OxyColors.Red); | |
AddCursorPair(cusorInfo, "t", null, true, true, OxyColors.Blue); | |
private void AddCursorPair(CursorInfoAnnotation cursorInfo, string xaxisKey, string yaxisKey, bool reverseX, bool reverseY, OxyColor color) | |
{ | |
var low = AddCursor(cursorInfo.PlotModel, xaxisKey, yaxisKey, reverseX, reverseY, color); | |
var high = AddCursor(cursorInfo.PlotModel, xaxisKey, yaxisKey, reverseX, reverseY, color); | |
cursorInfo.AddPair(low, high); | |
} | |
private CursorAnnotation AddCursor(PlotModel plot, string xaxisKey, string yaxisKey, bool reverseX, bool reverseY, OxyColor color) | |
{ | |
var res = new CursorAnnotation(xaxisKey, yaxisKey) | |
{ | |
Color = color, | |
ReverseX = reverseX, | |
ReverseY = reverseY, | |
}; | |
plot.Annotations.Add(res); | |
return res; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment