External/OxyPlot/OxyPlot/Series/AreaSeries.cs
author moel.mich
Sat, 08 Jun 2013 16:53:22 +0000
changeset 391 5be8f2773237
permissions -rw-r--r--
Added the source code of OxyPlot as of commit d190d7748a73 (6.5.2013).
moel@391
     1
// --------------------------------------------------------------------------------------------------------------------
moel@391
     2
// <copyright file="AreaSeries.cs" company="OxyPlot">
moel@391
     3
//   The MIT License (MIT)
moel@391
     4
//
moel@391
     5
//   Copyright (c) 2012 Oystein Bjorke
moel@391
     6
//
moel@391
     7
//   Permission is hereby granted, free of charge, to any person obtaining a
moel@391
     8
//   copy of this software and associated documentation files (the
moel@391
     9
//   "Software"), to deal in the Software without restriction, including
moel@391
    10
//   without limitation the rights to use, copy, modify, merge, publish,
moel@391
    11
//   distribute, sublicense, and/or sell copies of the Software, and to
moel@391
    12
//   permit persons to whom the Software is furnished to do so, subject to
moel@391
    13
//   the following conditions:
moel@391
    14
//
moel@391
    15
//   The above copyright notice and this permission notice shall be included
moel@391
    16
//   in all copies or substantial portions of the Software.
moel@391
    17
//
moel@391
    18
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
moel@391
    19
//   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
moel@391
    20
//   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
moel@391
    21
//   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
moel@391
    22
//   CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
moel@391
    23
//   TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
moel@391
    24
//   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
moel@391
    25
// </copyright>
moel@391
    26
// <summary>
moel@391
    27
//   Represents an area series that fills the polygon defined by one or two sets of points.
moel@391
    28
// </summary>
moel@391
    29
// --------------------------------------------------------------------------------------------------------------------
moel@391
    30
namespace OxyPlot.Series
moel@391
    31
{
moel@391
    32
    using System;
moel@391
    33
    using System.Collections.Generic;
moel@391
    34
moel@391
    35
    /// <summary>
moel@391
    36
    /// Represents an area series that fills the polygon defined by two sets of points or one set of points and a constant.
moel@391
    37
    /// </summary>
moel@391
    38
    public class AreaSeries : LineSeries
moel@391
    39
    {
moel@391
    40
        /// <summary>
moel@391
    41
        /// The second list of points.
moel@391
    42
        /// </summary>
moel@391
    43
        private readonly List<IDataPoint> points2 = new List<IDataPoint>();
moel@391
    44
moel@391
    45
        /// <summary>
moel@391
    46
        /// Initializes a new instance of the <see cref = "AreaSeries" /> class.
moel@391
    47
        /// </summary>
moel@391
    48
        public AreaSeries()
moel@391
    49
        {
moel@391
    50
            this.Reverse2 = true;
moel@391
    51
        }
moel@391
    52
moel@391
    53
        /// <summary>
moel@391
    54
        /// Gets or sets a constant value for the area definition.
moel@391
    55
        /// This is used if DataFieldBase and BaselineValues are null.
moel@391
    56
        /// </summary>
moel@391
    57
        /// <value>The baseline.</value>
moel@391
    58
        public double ConstantY2 { get; set; }
moel@391
    59
moel@391
    60
        /// <summary>
moel@391
    61
        /// Gets or sets the second X data field.
moel@391
    62
        /// </summary>
moel@391
    63
        public string DataFieldX2 { get; set; }
moel@391
    64
moel@391
    65
        /// <summary>
moel@391
    66
        /// Gets or sets the second Y data field.
moel@391
    67
        /// </summary>
moel@391
    68
        public string DataFieldY2 { get; set; }
moel@391
    69
moel@391
    70
        /// <summary>
moel@391
    71
        /// Gets or sets the area fill color.
moel@391
    72
        /// </summary>
moel@391
    73
        /// <value>The fill.</value>
moel@391
    74
        public OxyColor Fill { get; set; }
moel@391
    75
moel@391
    76
        /// <summary>
moel@391
    77
        /// Gets the second list of points.
moel@391
    78
        /// </summary>
moel@391
    79
        /// <value>The second list of points.</value>
moel@391
    80
        public List<IDataPoint> Points2
moel@391
    81
        {
moel@391
    82
            get
moel@391
    83
            {
moel@391
    84
                return this.points2;
moel@391
    85
            }
moel@391
    86
        }
moel@391
    87
moel@391
    88
        /// <summary>
moel@391
    89
        /// Gets or sets a value indicating whether the second
moel@391
    90
        /// data collection should be reversed.
moel@391
    91
        /// The first dataset is not reversed, and normally
moel@391
    92
        /// the second dataset should be reversed to get a
moel@391
    93
        /// closed polygon.
moel@391
    94
        /// </summary>
moel@391
    95
        public bool Reverse2 { get; set; }
moel@391
    96
moel@391
    97
        /// <summary>
moel@391
    98
        /// Gets the nearest point.
moel@391
    99
        /// </summary>
moel@391
   100
        /// <param name="point">The point.</param>
moel@391
   101
        /// <param name="interpolate">interpolate if set to <c>true</c> .</param>
moel@391
   102
        /// <returns>A TrackerHitResult for the current hit.</returns>
moel@391
   103
        public override TrackerHitResult GetNearestPoint(ScreenPoint point, bool interpolate)
moel@391
   104
        {
moel@391
   105
            if (interpolate)
moel@391
   106
            {
moel@391
   107
                var r1 = this.GetNearestInterpolatedPointInternal(this.Points, point);
moel@391
   108
                if (r1 != null)
moel@391
   109
                {
moel@391
   110
                    return r1;
moel@391
   111
                }
moel@391
   112
moel@391
   113
                var r2 = this.GetNearestInterpolatedPointInternal(this.points2, point);
moel@391
   114
                if (r2 != null)
moel@391
   115
                {
moel@391
   116
                    return r2;
moel@391
   117
                }
moel@391
   118
            }
moel@391
   119
            else
moel@391
   120
            {
moel@391
   121
                var result1 = this.GetNearestPointInternal(this.Points, point);
moel@391
   122
                var result2 = this.GetNearestPointInternal(this.points2, point);
moel@391
   123
moel@391
   124
                if (result1 != null && result2 != null)
moel@391
   125
                {
moel@391
   126
                    double dist1 = result1.Position.DistanceTo(point);
moel@391
   127
                    double dist2 = result2.Position.DistanceTo(point);
moel@391
   128
                    return dist1 < dist2 ? result1 : result2;
moel@391
   129
                }
moel@391
   130
moel@391
   131
                if (result1 != null)
moel@391
   132
                {
moel@391
   133
                    return result1;
moel@391
   134
                }
moel@391
   135
moel@391
   136
                if (result2 != null)
moel@391
   137
                {
moel@391
   138
                    return result2;
moel@391
   139
                }
moel@391
   140
            }
moel@391
   141
moel@391
   142
            return null;
moel@391
   143
        }
moel@391
   144
moel@391
   145
        /// <summary>
moel@391
   146
        /// Renders the series on the specified rendering context.
moel@391
   147
        /// </summary>
moel@391
   148
        /// <param name="rc">The rendering context.</param>
moel@391
   149
        /// <param name="model">The owner plot model.</param>
moel@391
   150
        public override void Render(IRenderContext rc, PlotModel model)
moel@391
   151
        {
moel@391
   152
            if (this.Points.Count == 0)
moel@391
   153
            {
moel@391
   154
                return;
moel@391
   155
            }
moel@391
   156
moel@391
   157
            base.VerifyAxes();
moel@391
   158
moel@391
   159
            double minDistSquared = this.MinimumSegmentLength * this.MinimumSegmentLength;
moel@391
   160
moel@391
   161
            var clippingRect = this.GetClippingRect();
moel@391
   162
moel@391
   163
            // Transform all points to screen coordinates
moel@391
   164
            var points = this.Points;
moel@391
   165
            int n0 = points.Count;
moel@391
   166
            IList<ScreenPoint> pts0 = new ScreenPoint[n0];
moel@391
   167
            for (int i = 0; i < n0; i++)
moel@391
   168
            {
moel@391
   169
                pts0[i] = this.XAxis.Transform(points[i].X, points[i].Y, this.YAxis);
moel@391
   170
            }
moel@391
   171
moel@391
   172
            int n1 = this.points2.Count;
moel@391
   173
            IList<ScreenPoint> pts1 = new ScreenPoint[n1];
moel@391
   174
            for (int i = 0; i < n1; i++)
moel@391
   175
            {
moel@391
   176
                int j = this.Reverse2 ? n1 - 1 - i : i;
moel@391
   177
                pts1[j] = this.XAxis.Transform(this.points2[i].X, this.points2[i].Y, this.YAxis);
moel@391
   178
            }
moel@391
   179
moel@391
   180
            if (this.Smooth)
moel@391
   181
            {
moel@391
   182
                var rpts0 = ScreenPointHelper.ResamplePoints(pts0, this.MinimumSegmentLength);
moel@391
   183
                var rpts1 = ScreenPointHelper.ResamplePoints(pts1, this.MinimumSegmentLength);
moel@391
   184
moel@391
   185
                pts0 = CanonicalSplineHelper.CreateSpline(rpts0, 0.5, null, false, 0.25);
moel@391
   186
                pts1 = CanonicalSplineHelper.CreateSpline(rpts1, 0.5, null, false, 0.25);
moel@391
   187
            }
moel@391
   188
moel@391
   189
            // draw the clipped lines
moel@391
   190
            rc.DrawClippedLine(
moel@391
   191
                pts0,
moel@391
   192
                clippingRect,
moel@391
   193
                minDistSquared,
moel@391
   194
                this.GetSelectableColor(this.ActualColor),
moel@391
   195
                this.StrokeThickness,
moel@391
   196
                this.ActualLineStyle,
moel@391
   197
                this.LineJoin,
moel@391
   198
                false);
moel@391
   199
            rc.DrawClippedLine(
moel@391
   200
                pts1,
moel@391
   201
                clippingRect,
moel@391
   202
                minDistSquared,
moel@391
   203
                this.GetSelectableColor(this.ActualColor),
moel@391
   204
                this.StrokeThickness,
moel@391
   205
                this.ActualLineStyle,
moel@391
   206
                this.LineJoin,
moel@391
   207
                false);
moel@391
   208
moel@391
   209
            // combine the two lines and draw the clipped area
moel@391
   210
            var pts = new List<ScreenPoint>();
moel@391
   211
            pts.AddRange(pts1);
moel@391
   212
            pts.AddRange(pts0);
moel@391
   213
moel@391
   214
            // pts = SutherlandHodgmanClipping.ClipPolygon(clippingRect, pts);
moel@391
   215
            rc.DrawClippedPolygon(pts, clippingRect, minDistSquared, this.GetSelectableFillColor(this.Fill), null);
moel@391
   216
moel@391
   217
            // draw the markers on top
moel@391
   218
            rc.DrawMarkers(
moel@391
   219
                pts0,
moel@391
   220
                clippingRect,
moel@391
   221
                this.MarkerType,
moel@391
   222
                null,
moel@391
   223
                new[] { this.MarkerSize },
moel@391
   224
                this.MarkerFill,
moel@391
   225
                this.MarkerStroke,
moel@391
   226
                this.MarkerStrokeThickness,
moel@391
   227
                1);
moel@391
   228
            rc.DrawMarkers(
moel@391
   229
                pts1,
moel@391
   230
                clippingRect,
moel@391
   231
                this.MarkerType,
moel@391
   232
                null,
moel@391
   233
                new[] { this.MarkerSize },
moel@391
   234
                this.MarkerFill,
moel@391
   235
                this.MarkerStroke,
moel@391
   236
                this.MarkerStrokeThickness,
moel@391
   237
                1);
moel@391
   238
        }
moel@391
   239
moel@391
   240
        /// <summary>
moel@391
   241
        /// Renders the legend symbol for the line series on the
moel@391
   242
        /// specified rendering context.
moel@391
   243
        /// </summary>
moel@391
   244
        /// <param name="rc">
moel@391
   245
        /// The rendering context.
moel@391
   246
        /// </param>
moel@391
   247
        /// <param name="legendBox">
moel@391
   248
        /// The bounding rectangle of the legend box.
moel@391
   249
        /// </param>
moel@391
   250
        public override void RenderLegend(IRenderContext rc, OxyRect legendBox)
moel@391
   251
        {
moel@391
   252
            double y0 = (legendBox.Top * 0.2) + (legendBox.Bottom * 0.8);
moel@391
   253
            double y1 = (legendBox.Top * 0.4) + (legendBox.Bottom * 0.6);
moel@391
   254
            double y2 = (legendBox.Top * 0.8) + (legendBox.Bottom * 0.2);
moel@391
   255
moel@391
   256
            var pts0 = new[] { new ScreenPoint(legendBox.Left, y0), new ScreenPoint(legendBox.Right, y0) };
moel@391
   257
            var pts1 = new[] { new ScreenPoint(legendBox.Right, y2), new ScreenPoint(legendBox.Left, y1) };
moel@391
   258
            var pts = new List<ScreenPoint>();
moel@391
   259
            pts.AddRange(pts0);
moel@391
   260
            pts.AddRange(pts1);
moel@391
   261
            var color = this.GetSelectableColor(this.ActualColor);
moel@391
   262
            rc.DrawLine(pts0, color, this.StrokeThickness, LineStyleHelper.GetDashArray(this.ActualLineStyle));
moel@391
   263
            rc.DrawLine(pts1, color, this.StrokeThickness, LineStyleHelper.GetDashArray(this.ActualLineStyle));
moel@391
   264
            rc.DrawPolygon(pts, this.GetSelectableFillColor(this.Fill), null);
moel@391
   265
        }
moel@391
   266
moel@391
   267
        /// <summary>
moel@391
   268
        /// The update data.
moel@391
   269
        /// </summary>
moel@391
   270
        protected internal override void UpdateData()
moel@391
   271
        {
moel@391
   272
            base.UpdateData();
moel@391
   273
moel@391
   274
            if (this.ItemsSource == null)
moel@391
   275
            {
moel@391
   276
                return;
moel@391
   277
            }
moel@391
   278
moel@391
   279
            this.points2.Clear();
moel@391
   280
moel@391
   281
            // Using reflection on DataFieldX2 and DataFieldY2
moel@391
   282
            this.AddDataPoints(this.points2, this.ItemsSource, this.DataFieldX2, this.DataFieldY2);
moel@391
   283
        }
moel@391
   284
moel@391
   285
        /// <summary>
moel@391
   286
        /// The update max min.
moel@391
   287
        /// </summary>
moel@391
   288
        protected internal override void UpdateMaxMin()
moel@391
   289
        {
moel@391
   290
            base.UpdateMaxMin();
moel@391
   291
            this.InternalUpdateMaxMin(this.points2);
moel@391
   292
        }
moel@391
   293
moel@391
   294
    }
moel@391
   295
}