Skip to content

Commit 588c7de

Browse files
authored
Add control over how far from the series the tracker fires (#1736)
Add control over how far from the series the tracker fires (#1731)
1 parent 61ffa99 commit 588c7de

9 files changed

Lines changed: 168 additions & 98 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,3 +242,6 @@ _Pvt_Extensions
242242
# OxyPlot specific
243243
Output/
244244
launchSettings.json
245+
246+
# Rider
247+
.idea/

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ All notable changes to this project will be documented in this file.
66
### Added
77
- Example for Issue #1716 showing poor tick spacing on DateTimeAxis with interval types of Weeks or Years
88
- Example for label placement on BarSeries with non-zero BaseValue (#1726)
9+
- Add control over how far from the series the tracker fires (#1736)
10+
- Add option to check distance for result between data points (#1736)
911

1012
### Changed
1113

CONTRIBUTORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,4 @@ Duncan Robertson <duncanjacobrobertson@gmail.com>
133133
LauXjpn <laucomm@gmail.com>
134134
R. Usamentiaga
135135
Dmytro Shaurin
136+
Rustam Sayfutdinov

Source/Examples/ExampleLibrary/Examples/TrackerExamples.cs

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ public static class TrackerExamples
1818
[Example("No interpolation")]
1919
public static PlotModel NoInterpolation()
2020
{
21-
var model = new PlotModel { Title = "No tracker interpolation", Subtitle = "Used for discrete values or scatter plots." };
21+
var model = new PlotModel
22+
{
23+
Title = "No tracker interpolation",
24+
Subtitle = "Used for discrete values or scatter plots.",
25+
};
2226
var l = new Legend
2327
{
2428
LegendSymbolLength = 30
@@ -27,16 +31,16 @@ public static PlotModel NoInterpolation()
2731
model.Legends.Add(l);
2832

2933
var s1 = new LineSeries
30-
{
31-
Title = "Series 1",
32-
CanTrackerInterpolatePoints = false,
33-
Color = OxyColors.SkyBlue,
34-
MarkerType = MarkerType.Circle,
35-
MarkerSize = 6,
36-
MarkerStroke = OxyColors.White,
37-
MarkerFill = OxyColors.SkyBlue,
38-
MarkerStrokeThickness = 1.5
39-
};
34+
{
35+
Title = "Series 1",
36+
CanTrackerInterpolatePoints = false,
37+
Color = OxyColors.SkyBlue,
38+
MarkerType = MarkerType.Circle,
39+
MarkerSize = 6,
40+
MarkerStroke = OxyColors.White,
41+
MarkerFill = OxyColors.SkyBlue,
42+
MarkerStrokeThickness = 1.5
43+
};
4044
for (int i = 0; i < 63; i++)
4145
{
4246
s1.Points.Add(new DataPoint((int)(Math.Sqrt(i) * Math.Cos(i * 0.1)), (int)(Math.Sqrt(i) * Math.Sin(i * 0.1))));
@@ -50,7 +54,11 @@ public static PlotModel NoInterpolation()
5054
[Example("TrackerChangedEvent")]
5155
public static PlotModel TrackerChangedEvent()
5256
{
53-
var model = new PlotModel { Title = "Handling the TrackerChanged event", Subtitle = "Press the left mouse button to test the tracker." };
57+
var model = new PlotModel
58+
{
59+
Title = "Handling the TrackerChanged event",
60+
Subtitle = "Press the left mouse button to test the tracker.",
61+
};
5462
model.Series.Add(new FunctionSeries(Math.Sin, 0, 10, 100));
5563
model.TrackerChanged += (s, e) =>
5664
{
@@ -59,5 +67,51 @@ public static PlotModel TrackerChangedEvent()
5967
};
6068
return model;
6169
}
70+
71+
[Example("Specified distance of the tracker fires")]
72+
public static Example TrackerFiresDistance()
73+
{
74+
var model = new PlotModel
75+
{
76+
Title = "Specified distance of the tracker fires",
77+
Subtitle = "Press the left mouse button to test the tracker.",
78+
};
79+
model.Series.Add(new FunctionSeries(Math.Sin, 0, 10, 100));
80+
81+
// create a new plot controller with default bindings
82+
var plotController = new PlotController();
83+
84+
// remove a tracker command to the mouse-left/touch down event by default
85+
plotController.Unbind(PlotCommands.SnapTrack);
86+
plotController.Unbind(PlotCommands.SnapTrackTouch);
87+
88+
// add a tracker command to the mouse-left/touch down event with specified distance
89+
plotController.BindMouseDown(
90+
OxyMouseButton.Left,
91+
new DelegatePlotCommand<OxyMouseDownEventArgs>((view, controller, args) =>
92+
controller.AddMouseManipulator(
93+
view,
94+
new TrackerManipulator(view)
95+
{
96+
Snap = true,
97+
PointsOnly = false,
98+
FiresDistance = 2.0,
99+
CheckDistanceBetweenPoints = true,
100+
},
101+
args)));
102+
plotController.BindTouchDown(
103+
new DelegatePlotCommand<OxyTouchEventArgs>((view, controller, args) =>
104+
controller.AddTouchManipulator(
105+
view,
106+
new TouchTrackerManipulator(view)
107+
{
108+
Snap = true,
109+
PointsOnly = false,
110+
FiresDistance = 2.0,
111+
},
112+
args)));
113+
114+
return new Example(model, plotController);
115+
}
62116
}
63117
}

Source/OxyPlot/PlotController/Manipulators/TouchTrackerManipulator.cs

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public TouchTrackerManipulator(IPlotView plotView)
2929
this.Snap = true;
3030
this.PointsOnly = false;
3131
this.LockToInitialSeries = true;
32+
this.FiresDistance = 20.0;
33+
this.CheckDistanceBetweenPoints = false;
3234

3335
// Note: the tracker manipulator should not handle pan or zoom
3436
this.SetHandledForPanOrZoom = false;
@@ -50,6 +52,17 @@ public TouchTrackerManipulator(IPlotView plotView)
5052
/// <value><c>true</c> if the tracker should be locked; otherwise, <c>false</c>.</value>
5153
public bool LockToInitialSeries { get; set; }
5254

55+
/// <summary>
56+
/// Gets or sets the distance from the series at which the tracker fires.
57+
/// </summary>
58+
public double FiresDistance { get; set; }
59+
60+
/// <summary>
61+
/// Gets or sets a value indicating whether to check distance when showing tracker between data points.
62+
/// </summary>
63+
/// <remarks>This parameter is ignored if <see cref="PointsOnly"/> is equal to <c>False</c>.</remarks>
64+
public bool CheckDistanceBetweenPoints { get; set; }
65+
5366
/// <summary>
5467
/// Occurs when a manipulation is complete.
5568
/// </summary>
@@ -85,7 +98,7 @@ public override void Delta(OxyTouchEventArgs e)
8598
public override void Started(OxyTouchEventArgs e)
8699
{
87100
base.Started(e);
88-
this.currentSeries = this.PlotView.ActualModel != null ? this.PlotView.ActualModel.GetSeriesFromPoint(e.Position) : null;
101+
this.currentSeries = this.PlotView.ActualModel?.GetSeriesFromPoint(e.Position, this.FiresDistance);
89102

90103
UpdateTracker(e.Position);
91104
}
@@ -99,7 +112,7 @@ private void UpdateTracker(ScreenPoint position)
99112
if (this.currentSeries == null || !this.LockToInitialSeries)
100113
{
101114
// get the nearest
102-
this.currentSeries = this.PlotView.ActualModel != null ? this.PlotView.ActualModel.GetSeriesFromPoint(position, 20) : null;
115+
this.currentSeries = this.PlotView.ActualModel?.GetSeriesFromPoint(position, this.FiresDistance);
103116
}
104117

105118
if (this.currentSeries == null)
@@ -123,51 +136,14 @@ private void UpdateTracker(ScreenPoint position)
123136
return;
124137
}
125138

126-
var result = GetNearestHit(this.currentSeries, position, this.Snap, this.PointsOnly);
139+
var result = Utilities.TrackerHelper.GetNearestHit(
140+
this.currentSeries, position, this.Snap, this.PointsOnly, this.FiresDistance, this.CheckDistanceBetweenPoints);
127141
if (result != null)
128142
{
129143
result.PlotModel = this.PlotView.ActualModel;
130144
this.PlotView.ShowTracker(result);
131145
this.PlotView.ActualModel.RaiseTrackerChanged(result);
132146
}
133147
}
134-
135-
/// <summary>
136-
/// Gets the nearest tracker hit.
137-
/// </summary>
138-
/// <param name="series">The series.</param>
139-
/// <param name="point">The point.</param>
140-
/// <param name="snap">Snap to points.</param>
141-
/// <param name="pointsOnly">Check points only (no interpolation).</param>
142-
/// <returns>A tracker hit result.</returns>
143-
private static TrackerHitResult GetNearestHit(Series.Series series, ScreenPoint point, bool snap, bool pointsOnly)
144-
{
145-
if (series == null)
146-
{
147-
return null;
148-
}
149-
150-
// Check data points only
151-
if (snap || pointsOnly)
152-
{
153-
var result = series.GetNearestPoint(point, false);
154-
if (result != null)
155-
{
156-
if (result.Position.DistanceTo(point) < 20)
157-
{
158-
return result;
159-
}
160-
}
161-
}
162-
163-
// Check between data points (if possible)
164-
if (!pointsOnly)
165-
{
166-
var result = series.GetNearestPoint(point, true);
167-
return result;
168-
}
169-
170-
return null;
171-
}
172148
}
173-
}
149+
}

Source/OxyPlot/PlotController/Manipulators/TrackerManipulator.cs

Lines changed: 18 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ public TrackerManipulator(IPlotView plotView)
2929
this.Snap = true;
3030
this.PointsOnly = false;
3131
this.LockToInitialSeries = true;
32+
this.FiresDistance = 20.0;
33+
this.CheckDistanceBetweenPoints = false;
3234
}
3335

3436
/// <summary>
@@ -47,6 +49,17 @@ public TrackerManipulator(IPlotView plotView)
4749
/// <value><c>true</c> if the tracker should be locked; otherwise, <c>false</c>.</value>
4850
public bool LockToInitialSeries { get; set; }
4951

52+
/// <summary>
53+
/// Gets or sets the distance from the series at which the tracker fires.
54+
/// </summary>
55+
public double FiresDistance { get; set; }
56+
57+
/// <summary>
58+
/// Gets or sets a value indicating whether to check distance when showing tracker between data points.
59+
/// </summary>
60+
/// <remarks>This parameter is ignored if <see cref="PointsOnly"/> is equal to <c>False</c>.</remarks>
61+
public bool CheckDistanceBetweenPoints { get; set; }
62+
5063
/// <summary>
5164
/// Occurs when a manipulation is complete.
5265
/// </summary>
@@ -76,7 +89,7 @@ public override void Delta(OxyMouseEventArgs e)
7689
if (this.currentSeries == null || !this.LockToInitialSeries)
7790
{
7891
// get the nearest
79-
this.currentSeries = this.PlotView.ActualModel != null ? this.PlotView.ActualModel.GetSeriesFromPoint(e.Position, 20) : null;
92+
this.currentSeries = this.PlotView.ActualModel?.GetSeriesFromPoint(e.Position, this.FiresDistance);
8093
}
8194

8295
if (this.currentSeries == null)
@@ -100,7 +113,8 @@ public override void Delta(OxyMouseEventArgs e)
100113
return;
101114
}
102115

103-
var result = GetNearestHit(this.currentSeries, e.Position, this.Snap, this.PointsOnly);
116+
var result = Utilities.TrackerHelper.GetNearestHit(
117+
this.currentSeries, e.Position, this.Snap, this.PointsOnly, this.FiresDistance, this.CheckDistanceBetweenPoints);
104118
if (result != null)
105119
{
106120
result.PlotModel = this.PlotView.ActualModel;
@@ -116,46 +130,8 @@ public override void Delta(OxyMouseEventArgs e)
116130
public override void Started(OxyMouseEventArgs e)
117131
{
118132
base.Started(e);
119-
this.currentSeries = this.PlotView.ActualModel != null ? this.PlotView.ActualModel.GetSeriesFromPoint(e.Position) : null;
133+
this.currentSeries = this.PlotView.ActualModel?.GetSeriesFromPoint(e.Position, FiresDistance);
120134
this.Delta(e);
121135
}
122-
123-
/// <summary>
124-
/// Gets the nearest tracker hit.
125-
/// </summary>
126-
/// <param name="series">The series.</param>
127-
/// <param name="point">The point.</param>
128-
/// <param name="snap">Snap to points.</param>
129-
/// <param name="pointsOnly">Check points only (no interpolation).</param>
130-
/// <returns>A tracker hit result.</returns>
131-
private static TrackerHitResult GetNearestHit(Series.Series series, ScreenPoint point, bool snap, bool pointsOnly)
132-
{
133-
if (series == null)
134-
{
135-
return null;
136-
}
137-
138-
// Check data points only
139-
if (snap || pointsOnly)
140-
{
141-
var result = series.GetNearestPoint(point, false);
142-
if (result != null)
143-
{
144-
if (result.Position.DistanceTo(point) < 20)
145-
{
146-
return result;
147-
}
148-
}
149-
}
150-
151-
// Check between data points (if possible)
152-
if (!pointsOnly)
153-
{
154-
var result = series.GetNearestPoint(point, true);
155-
return result;
156-
}
157-
158-
return null;
159-
}
160136
}
161-
}
137+
}

Source/OxyPlot/PlotModel/PlotElement.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public PlotModel PlotModel
7878
/// <summary>
7979
/// Gets or sets the edge rendering mode that is used for rendering the plot element.
8080
/// </summary>
81-
/// <value>The edge rendering mode. The default is <see cref="EdgeRenderingMode.Automatic"/>.</value>
81+
/// <value>The edge rendering mode. The default is <see cref="OxyPlot.EdgeRenderingMode.Automatic"/>.</value>
8282
public EdgeRenderingMode EdgeRenderingMode { get; set; }
8383

8484
/// <summary>

Source/OxyPlot/PlotModel/PlotModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ public IPlotView PlotView
250250
/// <summary>
251251
/// Gets or sets the edge rendering mode that is used for rendering the plot bounds and backgrounds.
252252
/// </summary>
253-
/// <value>The edge rendering mode. The default is <see cref="EdgeRenderingMode.Automatic"/>.</value>
253+
/// <value>The edge rendering mode. The default is <see cref="OxyPlot.EdgeRenderingMode.Automatic"/>.</value>
254254
public EdgeRenderingMode EdgeRenderingMode { get; set; }
255255

256256
/// <summary>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
namespace OxyPlot.Utilities
2+
{
3+
internal static class TrackerHelper
4+
{
5+
/// <summary>
6+
/// Gets the nearest tracker hit.
7+
/// </summary>
8+
/// <param name="series">The series.</param>
9+
/// <param name="point">The point.</param>
10+
/// <param name="snap">Snap to points.</param>
11+
/// <param name="pointsOnly">Check points only (no interpolation).</param>
12+
/// <param name="firesDistance">The distance from the series at which the tracker fires</param>
13+
/// <param name="checkDistanceBetweenPoints">The value indicating whether to check distance
14+
/// when showing tracker between data points.</param>
15+
/// <remarks>
16+
/// <paramref name="checkDistanceBetweenPoints" /> is ignored if <paramref name="pointsOnly"/> is equal to <c>False</c>.
17+
/// </remarks>
18+
/// <returns>A tracker hit result.</returns>
19+
public static TrackerHitResult GetNearestHit(
20+
Series.Series series,
21+
ScreenPoint point,
22+
bool snap,
23+
bool pointsOnly,
24+
double firesDistance,
25+
bool checkDistanceBetweenPoints)
26+
{
27+
if (series == null)
28+
{
29+
return null;
30+
}
31+
32+
// Check data points only
33+
if (snap || pointsOnly)
34+
{
35+
var result = series.GetNearestPoint(point, false);
36+
if (ShouldTrackerOpen(result, point, firesDistance))
37+
{
38+
return result;
39+
}
40+
}
41+
42+
// Check between data points (if possible)
43+
if (!pointsOnly)
44+
{
45+
var result = series.GetNearestPoint(point, true);
46+
if (!checkDistanceBetweenPoints || ShouldTrackerOpen(result, point, firesDistance))
47+
{
48+
return result;
49+
}
50+
}
51+
52+
return null;
53+
}
54+
55+
private static bool ShouldTrackerOpen(TrackerHitResult result, ScreenPoint point, double firesDistance) =>
56+
result?.Position.DistanceTo(point) < firesDistance;
57+
}
58+
}

0 commit comments

Comments
 (0)