Skip to content

Commit ddab90e

Browse files
committed
Catmull-Rom splines: Changed default segments to 100 from 1000, made segment count settable by user. Fixed integer overflows causing infinite iterations.
1 parent f9cebf3 commit ddab90e

File tree

2 files changed

+30
-18
lines changed

2 files changed

+30
-18
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ All notable changes to this project will be documented in this file.
5454
- Chart is not updated when top and bottom are not visible (#1219)
5555
- Candle overlap each candle (#623)
5656
- CandleStick is overlapped when item.open == item.close in the CandleStickAndVolumeSeries (#1245)
57+
- Out of memory exception and performance issue with Catmull-Rom Spline (#1237)
5758

5859
## [1.0.0] - 2016-09-11
5960
### Added

Source/OxyPlot/Rendering/Utilities/CatmullRomSpline.cs

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public class CatmullRomSpline : IInterpolationAlgorithm
2424
/// The alpha.
2525
/// </summary>
2626
public double Alpha { get; }
27+
public int MaxSegments { get; set; }
2728

2829
/// <summary>
2930
/// Initializes a new instance of the <see cref = "CatmullRomSpline" /> class.
@@ -32,6 +33,7 @@ public class CatmullRomSpline : IInterpolationAlgorithm
3233
public CatmullRomSpline(double alpha)
3334
{
3435
this.Alpha = alpha;
36+
this.MaxSegments = 100;
3537
}
3638

3739
/// <summary>
@@ -41,8 +43,9 @@ public CatmullRomSpline(double alpha)
4143
/// <param name="isClosed">True if the spline is closed.</param>
4244
/// <param name="tolerance">The tolerance.</param>
4345
/// <returns>A list of data points.</returns>
44-
public List<DataPoint> CreateSpline(List<DataPoint> points, bool isClosed, double tolerance) {
45-
return CreateSpline(points, this.Alpha, isClosed, tolerance);
46+
public List<DataPoint> CreateSpline(List<DataPoint> points, bool isClosed, double tolerance)
47+
{
48+
return CreateSpline(points, this.Alpha, isClosed, tolerance, MaxSegments);
4649
}
4750

4851
/// <summary>
@@ -52,8 +55,9 @@ public List<DataPoint> CreateSpline(List<DataPoint> points, bool isClosed, doubl
5255
/// <param name="isClosed">True if the spline is closed.</param>
5356
/// <param name="tolerance">The tolerance.</param>
5457
/// <returns>A list of screen points.</returns>
55-
public List<ScreenPoint> CreateSpline(IList<ScreenPoint> points, bool isClosed, double tolerance) {
56-
return CreateSpline(points, this.Alpha, isClosed, tolerance);
58+
public List<ScreenPoint> CreateSpline(IList<ScreenPoint> points, bool isClosed, double tolerance)
59+
{
60+
return CreateSpline(points, this.Alpha, isClosed, tolerance, MaxSegments);
5761
}
5862

5963
/// <summary>
@@ -64,10 +68,10 @@ public List<ScreenPoint> CreateSpline(IList<ScreenPoint> points, bool isClosed,
6468
/// <param name="isClosed">True if the spline is closed.</param>
6569
/// <param name="tolerance">The tolerance.</param>
6670
/// <returns>A list of data points.</returns>
67-
internal static List<DataPoint> CreateSpline(List<DataPoint> points, double alpha, bool isClosed, double tolerance)
71+
internal static List<DataPoint> CreateSpline(List<DataPoint> points, double alpha, bool isClosed, double tolerance, int maxSegments)
6872
{
6973
var screenPoints = points.Select(p => new ScreenPoint(p.X, p.Y)).ToList();
70-
var interpolatedScreenPoints = CreateSpline(screenPoints, alpha, isClosed, tolerance);
74+
var interpolatedScreenPoints = CreateSpline(screenPoints, alpha, isClosed, tolerance, maxSegments);
7175
var interpolatedDataPoints = new List<DataPoint>(interpolatedScreenPoints.Count);
7276

7377
foreach (var s in interpolatedScreenPoints)
@@ -87,7 +91,7 @@ internal static List<DataPoint> CreateSpline(List<DataPoint> points, double alph
8791
/// <param name="tolerance">The tolerance.</param>
8892
/// <returns>A list of screen points.</returns>
8993
internal static List<ScreenPoint> CreateSpline(
90-
IList<ScreenPoint> points, double alpha, bool isClosed, double tolerance)
94+
IList<ScreenPoint> points, double alpha, bool isClosed, double tolerance, int maxSegments)
9195
{
9296
var result = new List<ScreenPoint>();
9397
if (points == null)
@@ -111,12 +115,12 @@ internal static List<ScreenPoint> CreateSpline(
111115
{
112116
if (!isClosed)
113117
{
114-
Segment(result, points[0], points[0], points[1], points[1], alpha, tolerance);
118+
Segment(result, points[0], points[0], points[1], points[1], alpha, tolerance, maxSegments);
115119
}
116120
else
117121
{
118-
Segment(result, points[1], points[0], points[1], points[0], alpha, tolerance);
119-
Segment(result, points[0], points[1], points[0], points[1], alpha, tolerance);
122+
Segment(result, points[1], points[0], points[1], points[0], alpha, tolerance, maxSegments);
123+
Segment(result, points[0], points[1], points[0], points[1], alpha, tolerance, maxSegments);
120124
}
121125
}
122126
else
@@ -132,7 +136,8 @@ internal static List<ScreenPoint> CreateSpline(
132136
points[1],
133137
points[2],
134138
alpha,
135-
tolerance);
139+
tolerance,
140+
maxSegments);
136141
}
137142
else if (i == n - 2)
138143
{
@@ -143,18 +148,19 @@ internal static List<ScreenPoint> CreateSpline(
143148
points[i + 1],
144149
isClosed ? points[0] : points[i + 1],
145150
alpha,
146-
tolerance);
151+
tolerance,
152+
maxSegments);
147153
}
148154
else if (i == n - 1)
149155
{
150156
if (isClosed)
151157
{
152-
Segment(result, points[i - 1], points[i], points[0], points[1], alpha, tolerance);
158+
Segment(result, points[i - 1], points[i], points[0], points[1], alpha, tolerance, maxSegments);
153159
}
154160
}
155161
else
156162
{
157-
Segment(result, points[i - 1], points[i], points[i + 1], points[i + 2], alpha, tolerance);
163+
Segment(result, points[i - 1], points[i], points[i + 1], points[i + 2], alpha, tolerance, maxSegments);
158164
}
159165
}
160166
}
@@ -172,7 +178,7 @@ internal static List<ScreenPoint> CreateSpline(
172178
/// <param name="pt3">The pt 3.</param>
173179
/// <param name="alpha">The alpha.</param>
174180
/// <param name="tolerance">The tolerance.</param>
175-
/// <param name="maxSegments">The maximum number of segments. Default is <c>1000</c>.</param>
181+
/// <param name="maxSegments">The maximum number of segments.</param>
176182
private static void Segment(
177183
IList<ScreenPoint> points,
178184
ScreenPoint pt0,
@@ -181,7 +187,7 @@ private static void Segment(
181187
ScreenPoint pt3,
182188
double alpha,
183189
double tolerance,
184-
int maxSegments = 1000)
190+
int maxSegments)
185191
{
186192
if (Equals(pt1, pt2))
187193
{
@@ -204,7 +210,12 @@ private static void Segment(
204210
double t2 = GetT(t1, pt1, pt2, alpha);
205211
double t3 = GetT(t2, pt2, pt3, alpha);
206212

207-
int iterations = Math.Min((int)((Math.Abs(pt1.X - pt2.X) + Math.Abs(pt1.Y - pt2.Y)) / tolerance), maxSegments);
213+
214+
int iterations = (int)((Math.Abs(pt1.X - pt2.X) + Math.Abs(pt1.Y - pt2.Y)) / tolerance);
215+
//Make sure it is positive (negative means an integer overflow)
216+
iterations = Math.Max(0, iterations);
217+
//Never more iterations than maxSegments
218+
iterations = Math.Min(maxSegments, iterations);
208219
for (double t = t1; t < t2; t += (t2 - t1) / iterations)
209220
{
210221
ScreenPoint a1 = Sum(Mult((t1 - t) / (t1 - t0), pt0), Mult((t - t0) / (t1 - t0), pt1));
@@ -247,4 +258,4 @@ private static ScreenPoint Sum(ScreenPoint a, ScreenPoint b)
247258
return new ScreenPoint(a.X + b.X, a.Y + b.Y);
248259
}
249260
}
250-
}
261+
}

0 commit comments

Comments
 (0)