1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/External/OxyPlot/OxyPlot/Series/LineSeries.cs Sat Jun 08 16:53:22 2013 +0000
1.3 @@ -0,0 +1,644 @@
1.4 +// --------------------------------------------------------------------------------------------------------------------
1.5 +// <copyright file="LineSeries.cs" company="OxyPlot">
1.6 +// The MIT License (MIT)
1.7 +//
1.8 +// Copyright (c) 2012 Oystein Bjorke
1.9 +//
1.10 +// Permission is hereby granted, free of charge, to any person obtaining a
1.11 +// copy of this software and associated documentation files (the
1.12 +// "Software"), to deal in the Software without restriction, including
1.13 +// without limitation the rights to use, copy, modify, merge, publish,
1.14 +// distribute, sublicense, and/or sell copies of the Software, and to
1.15 +// permit persons to whom the Software is furnished to do so, subject to
1.16 +// the following conditions:
1.17 +//
1.18 +// The above copyright notice and this permission notice shall be included
1.19 +// in all copies or substantial portions of the Software.
1.20 +//
1.21 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1.22 +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1.23 +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1.24 +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
1.25 +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
1.26 +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1.27 +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1.28 +// </copyright>
1.29 +// <summary>
1.30 +// Represents a line series.
1.31 +// </summary>
1.32 +// --------------------------------------------------------------------------------------------------------------------
1.33 +
1.34 +namespace OxyPlot.Series
1.35 +{
1.36 + using System;
1.37 + using System.Collections.Generic;
1.38 +
1.39 + /// <summary>
1.40 + /// Represents a line series.
1.41 + /// </summary>
1.42 + public class LineSeries : DataPointSeries
1.43 + {
1.44 + /// <summary>
1.45 + /// The divisor value used to calculate tolerance for line smoothing.
1.46 + /// </summary>
1.47 + private const double ToleranceDivisor = 200;
1.48 +
1.49 + /// <summary>
1.50 + /// The default color.
1.51 + /// </summary>
1.52 + private OxyColor defaultColor;
1.53 +
1.54 + /// <summary>
1.55 + /// The smoothed points.
1.56 + /// </summary>
1.57 + private IList<IDataPoint> smoothedPoints;
1.58 +
1.59 + /// <summary>
1.60 + /// Initializes a new instance of the <see cref = "LineSeries" /> class.
1.61 + /// </summary>
1.62 + public LineSeries()
1.63 + {
1.64 + this.MinimumSegmentLength = 2;
1.65 + this.StrokeThickness = 2;
1.66 + this.LineJoin = OxyPenLineJoin.Bevel;
1.67 + this.LineStyle = LineStyle.Undefined;
1.68 + this.MarkerSize = 3;
1.69 + this.MarkerStrokeThickness = 1;
1.70 + this.CanTrackerInterpolatePoints = true;
1.71 + this.LabelMargin = 6;
1.72 + }
1.73 +
1.74 + /// <summary>
1.75 + /// Initializes a new instance of the <see cref="LineSeries"/> class.
1.76 + /// </summary>
1.77 + /// <param name="title">
1.78 + /// The title.
1.79 + /// </param>
1.80 + public LineSeries(string title)
1.81 + : this()
1.82 + {
1.83 + this.Title = title;
1.84 + }
1.85 +
1.86 + /// <summary>
1.87 + /// Initializes a new instance of the <see cref="LineSeries"/> class.
1.88 + /// </summary>
1.89 + /// <param name="color">
1.90 + /// The color of the line stroke.
1.91 + /// </param>
1.92 + /// <param name="strokeThickness">
1.93 + /// The stroke thickness (optional).
1.94 + /// </param>
1.95 + /// <param name="title">
1.96 + /// The title (optional).
1.97 + /// </param>
1.98 + public LineSeries(OxyColor color, double strokeThickness = 1, string title = null)
1.99 + : this()
1.100 + {
1.101 + this.Color = color;
1.102 + this.StrokeThickness = strokeThickness;
1.103 + this.BrokenLineThickness = 0;
1.104 + this.Title = title;
1.105 + }
1.106 +
1.107 + /// <summary>
1.108 + /// Gets or sets the color of the curve.
1.109 + /// </summary>
1.110 + /// <value>The color.</value>
1.111 + public OxyColor Color { get; set; }
1.112 +
1.113 + /// <summary>
1.114 + /// Gets or sets the color of the broken line segments.
1.115 + /// </summary>
1.116 + /// <remarks>
1.117 + /// Add <c>DataPoint.Undefined</c> in the Points collection to create breaks in the line.
1.118 + /// </remarks>
1.119 + public OxyColor BrokenLineColor { get; set; }
1.120 +
1.121 + /// <summary>
1.122 + /// Gets or sets the broken line style.
1.123 + /// </summary>
1.124 + public LineStyle BrokenLineStyle { get; set; }
1.125 +
1.126 + /// <summary>
1.127 + /// Gets or sets the broken line thickness.
1.128 + /// </summary>
1.129 + public double BrokenLineThickness { get; set; }
1.130 +
1.131 + /// <summary>
1.132 + /// Gets or sets the dashes array.
1.133 + /// If this is not null it overrides the LineStyle property.
1.134 + /// </summary>
1.135 + /// <value>The dashes.</value>
1.136 + public double[] Dashes { get; set; }
1.137 +
1.138 + /// <summary>
1.139 + /// Gets or sets the label format string.
1.140 + /// </summary>
1.141 + /// <value> The label format string. </value>
1.142 + public string LabelFormatString { get; set; }
1.143 +
1.144 + /// <summary>
1.145 + /// Gets or sets the label margins.
1.146 + /// </summary>
1.147 + public double LabelMargin { get; set; }
1.148 +
1.149 + /// <summary>
1.150 + /// Gets or sets the line join.
1.151 + /// </summary>
1.152 + /// <value>The line join.</value>
1.153 + public OxyPenLineJoin LineJoin { get; set; }
1.154 +
1.155 + /// <summary>
1.156 + /// Gets or sets the line style.
1.157 + /// </summary>
1.158 + /// <value>The line style.</value>
1.159 + public LineStyle LineStyle { get; set; }
1.160 +
1.161 + /// <summary>
1.162 + /// Gets or sets a value specifying the position of a legend rendered on the line.
1.163 + /// </summary>
1.164 + /// <value>A value specifying the position of the legend.</value>
1.165 + public LineLegendPosition LineLegendPosition { get; set; }
1.166 +
1.167 + /// <summary>
1.168 + /// Gets or sets the marker fill color.
1.169 + /// </summary>
1.170 + /// <value>The marker fill.</value>
1.171 + public OxyColor MarkerFill { get; set; }
1.172 +
1.173 + /// <summary>
1.174 + /// Gets or sets the marker outline polygon.
1.175 + /// If this property is set, the MarkerType will not be used.
1.176 + /// </summary>
1.177 + /// <value>The marker outline.</value>
1.178 + public ScreenPoint[] MarkerOutline { get; set; }
1.179 +
1.180 + /// <summary>
1.181 + /// Gets or sets the size of the marker.
1.182 + /// </summary>
1.183 + /// <value>The size of the marker.</value>
1.184 + public double MarkerSize { get; set; }
1.185 +
1.186 + /// <summary>
1.187 + /// Gets or sets the marker stroke.
1.188 + /// </summary>
1.189 + /// <value>The marker stroke.</value>
1.190 + public OxyColor MarkerStroke { get; set; }
1.191 +
1.192 + /// <summary>
1.193 + /// Gets or sets the marker stroke thickness.
1.194 + /// </summary>
1.195 + /// <value>The marker stroke thickness.</value>
1.196 + public double MarkerStrokeThickness { get; set; }
1.197 +
1.198 + /// <summary>
1.199 + /// Gets or sets the type of the marker.
1.200 + /// </summary>
1.201 + /// <value>The type of the marker.</value>
1.202 + /// <remarks>
1.203 + /// If MarkerType.Custom is used, the MarkerOutline property must be specified.
1.204 + /// </remarks>
1.205 + public MarkerType MarkerType { get; set; }
1.206 +
1.207 + /// <summary>
1.208 + /// Gets or sets the minimum length of the segment.
1.209 + /// Increasing this number will increase performance,
1.210 + /// but make the curve less accurate.
1.211 + /// </summary>
1.212 + /// <value>The minimum length of the segment.</value>
1.213 + public double MinimumSegmentLength { get; set; }
1.214 +
1.215 + /// <summary>
1.216 + /// Gets or sets the thickness of the curve.
1.217 + /// </summary>
1.218 + /// <value>The stroke thickness.</value>
1.219 + public double StrokeThickness { get; set; }
1.220 +
1.221 + /// <summary>
1.222 + /// Gets the actual color.
1.223 + /// </summary>
1.224 + /// <value>The actual color.</value>
1.225 + protected OxyColor ActualColor
1.226 + {
1.227 + get
1.228 + {
1.229 + return this.Color ?? this.defaultColor;
1.230 + }
1.231 + }
1.232 +
1.233 + /// <summary>
1.234 + /// Gets the actual line style.
1.235 + /// </summary>
1.236 + /// <value>
1.237 + /// The actual line style.
1.238 + /// </value>
1.239 + protected LineStyle ActualLineStyle
1.240 + {
1.241 + get
1.242 + {
1.243 + return this.LineStyle != LineStyle.Undefined ? this.LineStyle : LineStyle.Solid;
1.244 + }
1.245 + }
1.246 +
1.247 + /// <summary>
1.248 + /// Gets the smoothed points.
1.249 + /// </summary>
1.250 + /// <value>The smoothed points.</value>
1.251 + protected IList<IDataPoint> SmoothedPoints
1.252 + {
1.253 + get
1.254 + {
1.255 + return this.smoothedPoints;
1.256 + }
1.257 + }
1.258 +
1.259 + /// <summary>
1.260 + /// Gets the point on the series that is nearest the specified point.
1.261 + /// </summary>
1.262 + /// <param name="point">The point.</param>
1.263 + /// <param name="interpolate">Interpolate the series if this flag is set to <c>true</c>.</param>
1.264 + /// <returns>
1.265 + /// A TrackerHitResult for the current hit.
1.266 + /// </returns>
1.267 + public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate)
1.268 + {
1.269 + if (interpolate)
1.270 + {
1.271 + // Cannot interpolate if there is no line
1.272 + if (this.ActualColor == null || this.StrokeThickness.IsZero())
1.273 + {
1.274 + return null;
1.275 + }
1.276 +
1.277 + if (!this.CanTrackerInterpolatePoints)
1.278 + {
1.279 + return null;
1.280 + }
1.281 + }
1.282 +
1.283 + if (interpolate && this.Smooth && this.SmoothedPoints != null)
1.284 + {
1.285 + return this.GetNearestInterpolatedPointInternal(this.SmoothedPoints, point);
1.286 + }
1.287 +
1.288 + return base.GetNearestPoint(point, interpolate);
1.289 + }
1.290 +
1.291 + /// <summary>
1.292 + /// Renders the series on the specified rendering context.
1.293 + /// </summary>
1.294 + /// <param name="rc">
1.295 + /// The rendering context.
1.296 + /// </param>
1.297 + /// <param name="model">
1.298 + /// The owner plot model.
1.299 + /// </param>
1.300 + public override void Render(IRenderContext rc, PlotModel model)
1.301 + {
1.302 + if (this.Points.Count == 0)
1.303 + {
1.304 + return;
1.305 + }
1.306 +
1.307 + this.VerifyAxes();
1.308 +
1.309 + var clippingRect = this.GetClippingRect();
1.310 + var transformedPoints = new List<ScreenPoint>();
1.311 + var lineBreakSegments = new List<ScreenPoint>();
1.312 +
1.313 + ScreenPoint lastValidPoint = default(ScreenPoint);
1.314 + bool isBroken = false;
1.315 + var renderBrokenLineSegments = this.BrokenLineThickness > 0 && this.BrokenLineStyle != LineStyle.None;
1.316 +
1.317 + // Transform all points to screen coordinates
1.318 + // Render the line when invalid points occur
1.319 + foreach (var point in this.Points)
1.320 + {
1.321 + if (!this.IsValidPoint(point, this.XAxis, this.YAxis))
1.322 + {
1.323 + this.RenderPoints(rc, clippingRect, transformedPoints);
1.324 + transformedPoints.Clear();
1.325 + isBroken = true;
1.326 + continue;
1.327 + }
1.328 +
1.329 + var pt = this.XAxis.Transform(point.X, point.Y, this.YAxis);
1.330 + transformedPoints.Add(pt);
1.331 +
1.332 + if (renderBrokenLineSegments)
1.333 + {
1.334 + if (isBroken)
1.335 + {
1.336 + lineBreakSegments.Add(lastValidPoint);
1.337 + lineBreakSegments.Add(pt);
1.338 + isBroken = false;
1.339 + }
1.340 +
1.341 + lastValidPoint = pt;
1.342 + }
1.343 + }
1.344 +
1.345 + // Render the remaining points
1.346 + this.RenderPoints(rc, clippingRect, transformedPoints);
1.347 +
1.348 + if (renderBrokenLineSegments)
1.349 + {
1.350 + // Render line breaks
1.351 + rc.DrawClippedLineSegments(
1.352 + lineBreakSegments,
1.353 + clippingRect,
1.354 + this.BrokenLineColor,
1.355 + this.BrokenLineThickness,
1.356 + this.BrokenLineStyle,
1.357 + this.LineJoin,
1.358 + false);
1.359 + }
1.360 +
1.361 + if (this.LabelFormatString != null)
1.362 + {
1.363 + // render point labels (not optimized for performance)
1.364 + this.RenderPointLabels(rc, clippingRect);
1.365 + }
1.366 +
1.367 + if (this.LineLegendPosition != LineLegendPosition.None && this.Points.Count > 0
1.368 + && !string.IsNullOrEmpty(this.Title))
1.369 + {
1.370 + // renders a legend on the line
1.371 + this.RenderLegendOnLine(rc, clippingRect);
1.372 + }
1.373 + }
1.374 +
1.375 + /// <summary>
1.376 + /// Renders the legend symbol for the line series on the
1.377 + /// specified rendering context.
1.378 + /// </summary>
1.379 + /// <param name="rc">
1.380 + /// The rendering context.
1.381 + /// </param>
1.382 + /// <param name="legendBox">
1.383 + /// The bounding rectangle of the legend box.
1.384 + /// </param>
1.385 + public override void RenderLegend(IRenderContext rc, OxyRect legendBox)
1.386 + {
1.387 + double xmid = (legendBox.Left + legendBox.Right) / 2;
1.388 + double ymid = (legendBox.Top + legendBox.Bottom) / 2;
1.389 + var pts = new[] { new ScreenPoint(legendBox.Left, ymid), new ScreenPoint(legendBox.Right, ymid) };
1.390 + rc.DrawLine(
1.391 + pts,
1.392 + this.GetSelectableColor(this.ActualColor),
1.393 + this.StrokeThickness,
1.394 + this.ActualLineStyle.GetDashArray());
1.395 + var midpt = new ScreenPoint(xmid, ymid);
1.396 + rc.DrawMarker(
1.397 + midpt,
1.398 + legendBox,
1.399 + this.MarkerType,
1.400 + this.MarkerOutline,
1.401 + this.MarkerSize,
1.402 + this.MarkerFill,
1.403 + this.MarkerStroke,
1.404 + this.MarkerStrokeThickness);
1.405 + }
1.406 +
1.407 + /// <summary>
1.408 + /// The set default values.
1.409 + /// </summary>
1.410 + /// <param name="model">
1.411 + /// The model.
1.412 + /// </param>
1.413 + protected internal override void SetDefaultValues(PlotModel model)
1.414 + {
1.415 + // todo: should use ActualLineStyle
1.416 + if (this.Color == null)
1.417 + {
1.418 + if (this.LineStyle == LineStyle.Undefined)
1.419 + {
1.420 + this.LineStyle = model.GetDefaultLineStyle();
1.421 + }
1.422 +
1.423 + this.defaultColor = model.GetDefaultColor();
1.424 +
1.425 + // And MarkerFill will be overridden if not set to null
1.426 + if (this.MarkerFill == null)
1.427 + {
1.428 + this.MarkerFill = this.defaultColor;
1.429 + }
1.430 + }
1.431 + }
1.432 +
1.433 + /// <summary>
1.434 + /// Updates the axes to include the max and min of this series.
1.435 + /// </summary>
1.436 + protected internal override void UpdateMaxMin()
1.437 + {
1.438 + if (this.Smooth)
1.439 + {
1.440 + // Update the max/min from the control points
1.441 + base.UpdateMaxMin();
1.442 +
1.443 + // Make sure the smooth points are re-evaluated.
1.444 + this.ResetSmoothedPoints();
1.445 +
1.446 + // Update the max/min from the smoothed points
1.447 + foreach (var pt in this.SmoothedPoints)
1.448 + {
1.449 + this.MinX = Math.Min(this.MinX, pt.X);
1.450 + this.MinY = Math.Min(this.MinY, pt.Y);
1.451 + this.MaxX = Math.Max(this.MaxX, pt.X);
1.452 + this.MaxY = Math.Max(this.MaxY, pt.Y);
1.453 + }
1.454 + }
1.455 + else
1.456 + {
1.457 + base.UpdateMaxMin();
1.458 + }
1.459 + }
1.460 +
1.461 + /// <summary>
1.462 + /// Renders the point labels.
1.463 + /// </summary>
1.464 + /// <param name="rc">The render context.</param>
1.465 + /// <param name="clippingRect">The clipping rectangle.</param>
1.466 + protected void RenderPointLabels(IRenderContext rc, OxyRect clippingRect)
1.467 + {
1.468 + int index = -1;
1.469 + foreach (var point in this.Points)
1.470 + {
1.471 + index++;
1.472 +
1.473 + if (!this.IsValidPoint(point, this.XAxis, this.YAxis))
1.474 + {
1.475 + continue;
1.476 + }
1.477 +
1.478 + var pt = this.XAxis.Transform(point.X, point.Y, this.YAxis);
1.479 + pt.Y -= this.LabelMargin;
1.480 +
1.481 + if (!clippingRect.Contains(pt))
1.482 + {
1.483 + continue;
1.484 + }
1.485 +
1.486 + var s = StringHelper.Format(
1.487 + this.ActualCulture, this.LabelFormatString, this.GetItem(index), point.X, point.Y);
1.488 +
1.489 +#if SUPPORTLABELPLACEMENT
1.490 + switch (this.LabelPlacement)
1.491 + {
1.492 + case LabelPlacement.Inside:
1.493 + pt = new ScreenPoint(rect.Right - this.LabelMargin, (rect.Top + rect.Bottom) / 2);
1.494 + ha = HorizontalAlignment.Right;
1.495 + break;
1.496 + case LabelPlacement.Middle:
1.497 + pt = new ScreenPoint((rect.Left + rect.Right) / 2, (rect.Top + rect.Bottom) / 2);
1.498 + ha = HorizontalAlignment.Center;
1.499 + break;
1.500 + case LabelPlacement.Base:
1.501 + pt = new ScreenPoint(rect.Left + this.LabelMargin, (rect.Top + rect.Bottom) / 2);
1.502 + ha = HorizontalAlignment.Left;
1.503 + break;
1.504 + default: // Outside
1.505 + pt = new ScreenPoint(rect.Right + this.LabelMargin, (rect.Top + rect.Bottom) / 2);
1.506 + ha = HorizontalAlignment.Left;
1.507 + break;
1.508 + }
1.509 +#endif
1.510 +
1.511 + rc.DrawClippedText(
1.512 + clippingRect,
1.513 + pt,
1.514 + s,
1.515 + this.ActualTextColor,
1.516 + this.ActualFont,
1.517 + this.ActualFontSize,
1.518 + this.ActualFontWeight,
1.519 + 0,
1.520 + HorizontalAlignment.Center,
1.521 + VerticalAlignment.Bottom);
1.522 + }
1.523 + }
1.524 +
1.525 + /// <summary>
1.526 + /// Renders a legend on the line.
1.527 + /// </summary>
1.528 + /// <param name="rc">The render context.</param>
1.529 + /// <param name="clippingRect">The clipping rectangle.</param>
1.530 + protected void RenderLegendOnLine(IRenderContext rc, OxyRect clippingRect)
1.531 + {
1.532 + // Find the position
1.533 + IDataPoint point;
1.534 + var ha = HorizontalAlignment.Left;
1.535 + double dx;
1.536 + switch (this.LineLegendPosition)
1.537 + {
1.538 + case LineLegendPosition.Start:
1.539 +
1.540 + // start position
1.541 + point = this.Points[0];
1.542 + ha = HorizontalAlignment.Right;
1.543 + dx = -4;
1.544 + break;
1.545 + default:
1.546 +
1.547 + // end position
1.548 + point = this.Points[this.Points.Count - 1];
1.549 + dx = 4;
1.550 + break;
1.551 + }
1.552 +
1.553 + var pt = this.XAxis.Transform(point.X, point.Y, this.YAxis);
1.554 + pt.X += dx;
1.555 +
1.556 + // Render the legend
1.557 + rc.DrawClippedText(
1.558 + clippingRect,
1.559 + pt,
1.560 + this.Title,
1.561 + this.ActualTextColor,
1.562 + this.ActualFont,
1.563 + this.ActualFontSize,
1.564 + this.ActualFontWeight,
1.565 + 0,
1.566 + ha,
1.567 + VerticalAlignment.Middle);
1.568 + }
1.569 +
1.570 + /// <summary>
1.571 + /// Renders the transformed points.
1.572 + /// </summary>
1.573 + /// <param name="rc">
1.574 + /// The render context.
1.575 + /// </param>
1.576 + /// <param name="clippingRect">
1.577 + /// The clipping rectangle.
1.578 + /// </param>
1.579 + /// <param name="pointsToRender">
1.580 + /// The points to render.
1.581 + /// </param>
1.582 + protected void RenderPoints(IRenderContext rc, OxyRect clippingRect, IList<ScreenPoint> pointsToRender)
1.583 + {
1.584 + var screenPoints = pointsToRender;
1.585 + if (this.Smooth)
1.586 + {
1.587 + // spline smoothing (should only be used on small datasets)
1.588 + var resampledPoints = ScreenPointHelper.ResamplePoints(pointsToRender, this.MinimumSegmentLength);
1.589 + screenPoints = CanonicalSplineHelper.CreateSpline(resampledPoints, 0.5, null, false, 0.25);
1.590 + }
1.591 +
1.592 + // clip the line segments with the clipping rectangle
1.593 + if (this.StrokeThickness > 0 && this.ActualLineStyle != LineStyle.None)
1.594 + {
1.595 + this.RenderSmoothedLine(rc, clippingRect, screenPoints);
1.596 + }
1.597 +
1.598 + if (this.MarkerType != MarkerType.None)
1.599 + {
1.600 + rc.DrawMarkers(
1.601 + pointsToRender,
1.602 + clippingRect,
1.603 + this.MarkerType,
1.604 + this.MarkerOutline,
1.605 + new[] { this.MarkerSize },
1.606 + this.MarkerFill,
1.607 + this.MarkerStroke,
1.608 + this.MarkerStrokeThickness);
1.609 + }
1.610 + }
1.611 +
1.612 + /// <summary>
1.613 + /// Renders the (smoothed) line.
1.614 + /// </summary>
1.615 + /// <param name="rc">
1.616 + /// The render context.
1.617 + /// </param>
1.618 + /// <param name="clippingRect">
1.619 + /// The clipping rectangle.
1.620 + /// </param>
1.621 + /// <param name="pointsToRender">
1.622 + /// The points to render.
1.623 + /// </param>
1.624 + protected virtual void RenderSmoothedLine(
1.625 + IRenderContext rc, OxyRect clippingRect, IList<ScreenPoint> pointsToRender)
1.626 + {
1.627 + rc.DrawClippedLine(
1.628 + pointsToRender,
1.629 + clippingRect,
1.630 + this.MinimumSegmentLength * this.MinimumSegmentLength,
1.631 + this.GetSelectableColor(this.ActualColor),
1.632 + this.StrokeThickness,
1.633 + this.ActualLineStyle,
1.634 + this.LineJoin,
1.635 + false);
1.636 + }
1.637 +
1.638 + /// <summary>
1.639 + /// Force the smoothed points to be re-evaluated.
1.640 + /// </summary>
1.641 + protected void ResetSmoothedPoints()
1.642 + {
1.643 + double tolerance = Math.Abs(Math.Max(this.MaxX - this.MinX, this.MaxY - this.MinY) / ToleranceDivisor);
1.644 + this.smoothedPoints = CanonicalSplineHelper.CreateSpline(this.Points, 0.5, null, false, tolerance);
1.645 + }
1.646 + }
1.647 +}
1.648 \ No newline at end of file