moel@391
|
1 |
// --------------------------------------------------------------------------------------------------------------------
|
moel@391
|
2 |
// <copyright file="PlotModel.Rendering.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 |
// Partial PlotModel class - this file contains rendering methods.
|
moel@391
|
28 |
// </summary>
|
moel@391
|
29 |
// --------------------------------------------------------------------------------------------------------------------
|
moel@391
|
30 |
namespace OxyPlot
|
moel@391
|
31 |
{
|
moel@391
|
32 |
using System;
|
moel@391
|
33 |
using System.Collections.Generic;
|
moel@391
|
34 |
using System.Linq;
|
moel@391
|
35 |
|
moel@391
|
36 |
using OxyPlot.Annotations;
|
moel@391
|
37 |
using OxyPlot.Axes;
|
moel@391
|
38 |
using OxyPlot.Series;
|
moel@391
|
39 |
|
moel@391
|
40 |
public partial class PlotModel
|
moel@391
|
41 |
{
|
moel@391
|
42 |
/// <summary>
|
moel@391
|
43 |
/// Renders the plot with the specified rendering context.
|
moel@391
|
44 |
/// </summary>
|
moel@391
|
45 |
/// <param name="rc">The rendering context.</param>
|
moel@391
|
46 |
/// <param name="width">The width.</param>
|
moel@391
|
47 |
/// <param name="height">The height.</param>
|
moel@391
|
48 |
public void Render(IRenderContext rc, double width, double height)
|
moel@391
|
49 |
{
|
moel@391
|
50 |
lock (this.syncRoot)
|
moel@391
|
51 |
{
|
moel@391
|
52 |
if (width <= 0 || height <= 0)
|
moel@391
|
53 |
{
|
moel@391
|
54 |
return;
|
moel@391
|
55 |
}
|
moel@391
|
56 |
|
moel@391
|
57 |
this.Width = width;
|
moel@391
|
58 |
this.Height = height;
|
moel@391
|
59 |
|
moel@391
|
60 |
this.ActualPlotMargins = this.PlotMargins;
|
moel@391
|
61 |
this.EnsureLegendProperties();
|
moel@391
|
62 |
|
moel@391
|
63 |
while (true)
|
moel@391
|
64 |
{
|
moel@391
|
65 |
this.UpdatePlotArea(rc);
|
moel@391
|
66 |
this.UpdateAxisTransforms();
|
moel@391
|
67 |
this.UpdateIntervals();
|
moel@391
|
68 |
if (!this.AutoAdjustPlotMargins)
|
moel@391
|
69 |
{
|
moel@391
|
70 |
break;
|
moel@391
|
71 |
}
|
moel@391
|
72 |
|
moel@391
|
73 |
if (!this.AdjustPlotMargins(rc))
|
moel@391
|
74 |
{
|
moel@391
|
75 |
break;
|
moel@391
|
76 |
}
|
moel@391
|
77 |
}
|
moel@391
|
78 |
|
moel@391
|
79 |
if (this.PlotType == PlotType.Cartesian)
|
moel@391
|
80 |
{
|
moel@391
|
81 |
this.EnforceCartesianTransforms();
|
moel@391
|
82 |
this.UpdateIntervals();
|
moel@391
|
83 |
}
|
moel@391
|
84 |
|
moel@391
|
85 |
this.RenderBackgrounds(rc);
|
moel@391
|
86 |
this.RenderAnnotations(rc, AnnotationLayer.BelowAxes);
|
moel@391
|
87 |
this.RenderAxes(rc, AxisLayer.BelowSeries);
|
moel@391
|
88 |
this.RenderAnnotations(rc, AnnotationLayer.BelowSeries);
|
moel@391
|
89 |
this.RenderSeries(rc);
|
moel@391
|
90 |
this.RenderAnnotations(rc, AnnotationLayer.AboveSeries);
|
moel@391
|
91 |
this.RenderTitle(rc);
|
moel@391
|
92 |
this.RenderBox(rc);
|
moel@391
|
93 |
this.RenderAxes(rc, AxisLayer.AboveSeries);
|
moel@391
|
94 |
|
moel@391
|
95 |
if (this.IsLegendVisible)
|
moel@391
|
96 |
{
|
moel@391
|
97 |
this.RenderLegends(rc, this.LegendArea);
|
moel@391
|
98 |
}
|
moel@391
|
99 |
}
|
moel@391
|
100 |
}
|
moel@391
|
101 |
|
moel@391
|
102 |
/// <summary>
|
moel@391
|
103 |
/// Calculates the maximum size of the specified axes.
|
moel@391
|
104 |
/// </summary>
|
moel@391
|
105 |
/// <param name="rc">
|
moel@391
|
106 |
/// The render context.
|
moel@391
|
107 |
/// </param>
|
moel@391
|
108 |
/// <param name="axesOfPositionTier">
|
moel@391
|
109 |
/// The axes of position tier.
|
moel@391
|
110 |
/// </param>
|
moel@391
|
111 |
/// <returns>
|
moel@391
|
112 |
/// The maximum size.
|
moel@391
|
113 |
/// </returns>
|
moel@391
|
114 |
private static double MaxSizeOfPositionTier(IRenderContext rc, IEnumerable<Axis> axesOfPositionTier)
|
moel@391
|
115 |
{
|
moel@391
|
116 |
double maxSizeOfPositionTier = 0;
|
moel@391
|
117 |
foreach (var axis in axesOfPositionTier)
|
moel@391
|
118 |
{
|
moel@391
|
119 |
OxySize size = axis.Measure(rc);
|
moel@391
|
120 |
if (axis.IsHorizontal())
|
moel@391
|
121 |
{
|
moel@391
|
122 |
if (size.Height > maxSizeOfPositionTier)
|
moel@391
|
123 |
{
|
moel@391
|
124 |
maxSizeOfPositionTier = size.Height;
|
moel@391
|
125 |
}
|
moel@391
|
126 |
}
|
moel@391
|
127 |
else
|
moel@391
|
128 |
{
|
moel@391
|
129 |
if (size.Width > maxSizeOfPositionTier)
|
moel@391
|
130 |
{
|
moel@391
|
131 |
maxSizeOfPositionTier = size.Width;
|
moel@391
|
132 |
}
|
moel@391
|
133 |
}
|
moel@391
|
134 |
}
|
moel@391
|
135 |
|
moel@391
|
136 |
return maxSizeOfPositionTier;
|
moel@391
|
137 |
}
|
moel@391
|
138 |
|
moel@391
|
139 |
/// <summary>
|
moel@391
|
140 |
/// Adjust the plot margins.
|
moel@391
|
141 |
/// </summary>
|
moel@391
|
142 |
/// <param name="rc">
|
moel@391
|
143 |
/// The render context.
|
moel@391
|
144 |
/// </param>
|
moel@391
|
145 |
/// <returns>
|
moel@391
|
146 |
/// The adjust plot margins.
|
moel@391
|
147 |
/// </returns>
|
moel@391
|
148 |
private bool AdjustPlotMargins(IRenderContext rc)
|
moel@391
|
149 |
{
|
moel@391
|
150 |
bool isAdjusted = false;
|
moel@391
|
151 |
var newPlotMargins = new Dictionary<AxisPosition, double>
|
moel@391
|
152 |
{
|
moel@391
|
153 |
{ AxisPosition.Left, this.ActualPlotMargins.Left },
|
moel@391
|
154 |
{ AxisPosition.Top, this.ActualPlotMargins.Top },
|
moel@391
|
155 |
{ AxisPosition.Right, this.ActualPlotMargins.Right },
|
moel@391
|
156 |
{ AxisPosition.Bottom, this.ActualPlotMargins.Bottom }
|
moel@391
|
157 |
};
|
moel@391
|
158 |
|
moel@391
|
159 |
for (var position = AxisPosition.Left; position <= AxisPosition.Bottom; position++)
|
moel@391
|
160 |
{
|
moel@391
|
161 |
double maxValueOfPositionTier = 0;
|
moel@391
|
162 |
var axesOfPosition = this.Axes.Where(a => a.Position == position).ToList();
|
moel@391
|
163 |
foreach (var positionTier in axesOfPosition.Select(a => a.PositionTier).Distinct().OrderBy(l => l))
|
moel@391
|
164 |
{
|
moel@391
|
165 |
var axesOfPositionTier = axesOfPosition.Where(a => a.PositionTier == positionTier).ToList();
|
moel@391
|
166 |
double maxSizeOfPositionTier = MaxSizeOfPositionTier(rc, axesOfPositionTier);
|
moel@391
|
167 |
double minValueOfPositionTier = maxValueOfPositionTier;
|
moel@391
|
168 |
|
moel@391
|
169 |
if (Math.Abs(maxValueOfPositionTier) > 1e-5)
|
moel@391
|
170 |
{
|
moel@391
|
171 |
maxValueOfPositionTier += this.AxisTierDistance;
|
moel@391
|
172 |
}
|
moel@391
|
173 |
|
moel@391
|
174 |
maxValueOfPositionTier += maxSizeOfPositionTier;
|
moel@391
|
175 |
|
moel@391
|
176 |
foreach (Axis axis in axesOfPositionTier)
|
moel@391
|
177 |
{
|
moel@391
|
178 |
axis.PositionTierSize = maxSizeOfPositionTier;
|
moel@391
|
179 |
axis.PositionTierMinShift = minValueOfPositionTier;
|
moel@391
|
180 |
axis.PositionTierMaxShift = maxValueOfPositionTier;
|
moel@391
|
181 |
}
|
moel@391
|
182 |
}
|
moel@391
|
183 |
|
moel@391
|
184 |
if (maxValueOfPositionTier > newPlotMargins[position])
|
moel@391
|
185 |
{
|
moel@391
|
186 |
newPlotMargins[position] = maxValueOfPositionTier;
|
moel@391
|
187 |
isAdjusted = true;
|
moel@391
|
188 |
}
|
moel@391
|
189 |
}
|
moel@391
|
190 |
|
moel@391
|
191 |
if (isAdjusted)
|
moel@391
|
192 |
{
|
moel@391
|
193 |
this.ActualPlotMargins = new OxyThickness(
|
moel@391
|
194 |
newPlotMargins[AxisPosition.Left],
|
moel@391
|
195 |
newPlotMargins[AxisPosition.Top],
|
moel@391
|
196 |
newPlotMargins[AxisPosition.Right],
|
moel@391
|
197 |
newPlotMargins[AxisPosition.Bottom]);
|
moel@391
|
198 |
}
|
moel@391
|
199 |
|
moel@391
|
200 |
return isAdjusted;
|
moel@391
|
201 |
}
|
moel@391
|
202 |
|
moel@391
|
203 |
/// <summary>
|
moel@391
|
204 |
/// Measures the size of the title and subtitle.
|
moel@391
|
205 |
/// </summary>
|
moel@391
|
206 |
/// <param name="rc">
|
moel@391
|
207 |
/// The rendering context.
|
moel@391
|
208 |
/// </param>
|
moel@391
|
209 |
/// <returns>
|
moel@391
|
210 |
/// Size of the titles.
|
moel@391
|
211 |
/// </returns>
|
moel@391
|
212 |
private OxySize MeasureTitles(IRenderContext rc)
|
moel@391
|
213 |
{
|
moel@391
|
214 |
OxySize size1 = rc.MeasureText(this.Title, this.ActualTitleFont, this.TitleFontSize, this.TitleFontWeight);
|
moel@391
|
215 |
OxySize size2 = rc.MeasureText(
|
moel@391
|
216 |
this.Subtitle, this.SubtitleFont ?? this.ActualSubtitleFont, this.SubtitleFontSize, this.SubtitleFontWeight);
|
moel@391
|
217 |
double height = size1.Height + size2.Height;
|
moel@391
|
218 |
double width = Math.Max(size1.Width, size2.Width);
|
moel@391
|
219 |
return new OxySize(width, height);
|
moel@391
|
220 |
}
|
moel@391
|
221 |
|
moel@391
|
222 |
/// <summary>
|
moel@391
|
223 |
/// Renders the annotations.
|
moel@391
|
224 |
/// </summary>
|
moel@391
|
225 |
/// <param name="rc">
|
moel@391
|
226 |
/// The render context.
|
moel@391
|
227 |
/// </param>
|
moel@391
|
228 |
/// <param name="layer">
|
moel@391
|
229 |
/// The layer.
|
moel@391
|
230 |
/// </param>
|
moel@391
|
231 |
private void RenderAnnotations(IRenderContext rc, AnnotationLayer layer)
|
moel@391
|
232 |
{
|
moel@391
|
233 |
foreach (var a in this.Annotations.Where(a => a.Layer == layer))
|
moel@391
|
234 |
{
|
moel@391
|
235 |
a.Render(rc, this);
|
moel@391
|
236 |
}
|
moel@391
|
237 |
}
|
moel@391
|
238 |
|
moel@391
|
239 |
/// <summary>
|
moel@391
|
240 |
/// Renders the axes.
|
moel@391
|
241 |
/// </summary>
|
moel@391
|
242 |
/// <param name="rc">
|
moel@391
|
243 |
/// The render context.
|
moel@391
|
244 |
/// </param>
|
moel@391
|
245 |
/// <param name="layer">
|
moel@391
|
246 |
/// The layer.
|
moel@391
|
247 |
/// </param>
|
moel@391
|
248 |
private void RenderAxes(IRenderContext rc, AxisLayer layer)
|
moel@391
|
249 |
{
|
moel@391
|
250 |
for (int i = 0; i < 2; i++)
|
moel@391
|
251 |
{
|
moel@391
|
252 |
foreach (var a in this.Axes)
|
moel@391
|
253 |
{
|
moel@391
|
254 |
if (a.IsAxisVisible && a.Layer == layer)
|
moel@391
|
255 |
{
|
moel@391
|
256 |
a.Render(rc, this, layer, i);
|
moel@391
|
257 |
}
|
moel@391
|
258 |
}
|
moel@391
|
259 |
}
|
moel@391
|
260 |
}
|
moel@391
|
261 |
|
moel@391
|
262 |
/// <summary>
|
moel@391
|
263 |
/// Renders the series backgrounds.
|
moel@391
|
264 |
/// </summary>
|
moel@391
|
265 |
/// <param name="rc">
|
moel@391
|
266 |
/// The render context.
|
moel@391
|
267 |
/// </param>
|
moel@391
|
268 |
private void RenderBackgrounds(IRenderContext rc)
|
moel@391
|
269 |
{
|
moel@391
|
270 |
// Render the main background of the plot area (only if there are axes)
|
moel@391
|
271 |
// The border is rendered by DrawRectangleAsPolygon to ensure that it is pixel aligned with the tick marks.
|
moel@391
|
272 |
if (this.Axes.Count > 0 && this.PlotAreaBackground != null)
|
moel@391
|
273 |
{
|
moel@391
|
274 |
rc.DrawRectangleAsPolygon(this.PlotArea, this.PlotAreaBackground, null, 0);
|
moel@391
|
275 |
}
|
moel@391
|
276 |
|
moel@391
|
277 |
foreach (var s in this.VisibleSeries)
|
moel@391
|
278 |
{
|
moel@391
|
279 |
var s2 = s as XYAxisSeries;
|
moel@391
|
280 |
if (s2 == null || s2.Background == null)
|
moel@391
|
281 |
{
|
moel@391
|
282 |
continue;
|
moel@391
|
283 |
}
|
moel@391
|
284 |
|
moel@391
|
285 |
rc.DrawRectangle(s2.GetScreenRectangle(), s2.Background, null, 0);
|
moel@391
|
286 |
}
|
moel@391
|
287 |
}
|
moel@391
|
288 |
|
moel@391
|
289 |
/// <summary>
|
moel@391
|
290 |
/// Renders the border around the plot area.
|
moel@391
|
291 |
/// </summary>
|
moel@391
|
292 |
/// <remarks>
|
moel@391
|
293 |
/// The border will only by rendered if there are axes in the plot.
|
moel@391
|
294 |
/// </remarks>
|
moel@391
|
295 |
/// <param name="rc">
|
moel@391
|
296 |
/// The render context.
|
moel@391
|
297 |
/// </param>
|
moel@391
|
298 |
private void RenderBox(IRenderContext rc)
|
moel@391
|
299 |
{
|
moel@391
|
300 |
// The border is rendered by DrawBox to ensure that it is pixel aligned with the tick marks (cannot use DrawRectangle here).
|
moel@391
|
301 |
if (this.Axes.Count > 0)
|
moel@391
|
302 |
{
|
moel@391
|
303 |
rc.DrawRectangleAsPolygon(this.PlotArea, null, this.PlotAreaBorderColor, this.PlotAreaBorderThickness);
|
moel@399
|
304 |
|
moel@411
|
305 |
foreach (var axis in this.Axes)
|
moel@411
|
306 |
{
|
moel@411
|
307 |
if (!axis.IsAxisVisible)
|
moel@411
|
308 |
continue;
|
moel@399
|
309 |
|
moel@411
|
310 |
if (axis.IsHorizontal())
|
moel@411
|
311 |
{
|
moel@411
|
312 |
var start = this.PlotArea.Left +
|
moel@411
|
313 |
this.PlotArea.Width * axis.StartPosition;
|
moel@411
|
314 |
if (axis.StartPosition < 1 && axis.StartPosition > 0)
|
moel@411
|
315 |
rc.DrawLine(new[] {
|
moel@411
|
316 |
new ScreenPoint(start, this.PlotArea.Top),
|
moel@411
|
317 |
new ScreenPoint(start, this.PlotArea.Bottom) },
|
moel@411
|
318 |
this.PlotAreaBorderColor, this.PlotAreaBorderThickness,
|
moel@411
|
319 |
null, OxyPenLineJoin.Miter, true);
|
moel@399
|
320 |
|
moel@411
|
321 |
var end = this.PlotArea.Left +
|
moel@411
|
322 |
this.PlotArea.Width * axis.EndPosition;
|
moel@411
|
323 |
if (axis.EndPosition < 1 && axis.EndPosition > 0)
|
moel@411
|
324 |
rc.DrawLine(new[] {
|
moel@411
|
325 |
new ScreenPoint(end, this.PlotArea.Top),
|
moel@411
|
326 |
new ScreenPoint(end, this.PlotArea.Bottom) },
|
moel@411
|
327 |
this.PlotAreaBorderColor, this.PlotAreaBorderThickness,
|
moel@411
|
328 |
null, OxyPenLineJoin.Miter, true);
|
moel@411
|
329 |
}
|
moel@411
|
330 |
else
|
moel@411
|
331 |
{
|
moel@411
|
332 |
var start = this.PlotArea.Bottom -
|
moel@411
|
333 |
this.PlotArea.Height * axis.StartPosition;
|
moel@411
|
334 |
if (axis.StartPosition < 1 && axis.StartPosition > 0)
|
moel@411
|
335 |
rc.DrawLine(new[] {
|
moel@411
|
336 |
new ScreenPoint(this.PlotArea.Left, start),
|
moel@411
|
337 |
new ScreenPoint(this.PlotArea.Right, start) },
|
moel@411
|
338 |
this.PlotAreaBorderColor, this.PlotAreaBorderThickness,
|
moel@411
|
339 |
null, OxyPenLineJoin.Miter, true);
|
moel@411
|
340 |
|
moel@411
|
341 |
var end = this.PlotArea.Bottom -
|
moel@411
|
342 |
this.PlotArea.Height * axis.EndPosition;
|
moel@411
|
343 |
if (axis.EndPosition < 1 && axis.EndPosition > 0)
|
moel@411
|
344 |
rc.DrawLine(new[] {
|
moel@411
|
345 |
new ScreenPoint(this.PlotArea.Left, end),
|
moel@411
|
346 |
new ScreenPoint(this.PlotArea.Right, end) },
|
moel@411
|
347 |
this.PlotAreaBorderColor, this.PlotAreaBorderThickness,
|
moel@411
|
348 |
null, OxyPenLineJoin.Miter, true);
|
moel@411
|
349 |
}
|
moel@399
|
350 |
}
|
moel@411
|
351 |
|
moel@391
|
352 |
}
|
moel@391
|
353 |
}
|
moel@391
|
354 |
|
moel@391
|
355 |
/// <summary>
|
moel@391
|
356 |
/// Renders the series.
|
moel@391
|
357 |
/// </summary>
|
moel@391
|
358 |
/// <param name="rc">
|
moel@391
|
359 |
/// The render context.
|
moel@391
|
360 |
/// </param>
|
moel@391
|
361 |
private void RenderSeries(IRenderContext rc)
|
moel@391
|
362 |
{
|
moel@391
|
363 |
// Update undefined colors
|
moel@391
|
364 |
this.ResetDefaultColor();
|
moel@391
|
365 |
foreach (var s in this.VisibleSeries)
|
moel@391
|
366 |
{
|
moel@391
|
367 |
s.SetDefaultValues(this);
|
moel@391
|
368 |
}
|
moel@391
|
369 |
|
moel@391
|
370 |
foreach (var s in this.VisibleSeries)
|
moel@391
|
371 |
{
|
moel@391
|
372 |
s.Render(rc, this);
|
moel@391
|
373 |
}
|
moel@391
|
374 |
}
|
moel@391
|
375 |
|
moel@391
|
376 |
/// <summary>
|
moel@391
|
377 |
/// Renders the title and subtitle.
|
moel@391
|
378 |
/// </summary>
|
moel@391
|
379 |
/// <param name="rc">
|
moel@391
|
380 |
/// The render context.
|
moel@391
|
381 |
/// </param>
|
moel@391
|
382 |
private void RenderTitle(IRenderContext rc)
|
moel@391
|
383 |
{
|
moel@391
|
384 |
OxySize size1 = rc.MeasureText(this.Title, this.ActualTitleFont, this.TitleFontSize, this.TitleFontWeight);
|
moel@391
|
385 |
rc.MeasureText(
|
moel@391
|
386 |
this.Subtitle, this.SubtitleFont ?? this.ActualSubtitleFont, this.SubtitleFontSize, this.SubtitleFontWeight);
|
moel@391
|
387 |
|
moel@391
|
388 |
// double height = size1.Height + size2.Height;
|
moel@391
|
389 |
// double dy = (TitleArea.Top+TitleArea.Bottom-height)*0.5;
|
moel@391
|
390 |
double dy = this.TitleArea.Top;
|
moel@391
|
391 |
double dx = (this.TitleArea.Left + this.TitleArea.Right) * 0.5;
|
moel@391
|
392 |
|
moel@391
|
393 |
if (!string.IsNullOrEmpty(this.Title))
|
moel@391
|
394 |
{
|
moel@391
|
395 |
rc.DrawMathText(
|
moel@391
|
396 |
new ScreenPoint(dx, dy),
|
moel@391
|
397 |
this.Title,
|
moel@391
|
398 |
this.TitleColor ?? this.TextColor,
|
moel@391
|
399 |
this.ActualTitleFont,
|
moel@391
|
400 |
this.TitleFontSize,
|
moel@391
|
401 |
this.TitleFontWeight,
|
moel@391
|
402 |
0,
|
moel@391
|
403 |
HorizontalAlignment.Center,
|
moel@391
|
404 |
VerticalAlignment.Top);
|
moel@391
|
405 |
dy += size1.Height;
|
moel@391
|
406 |
}
|
moel@391
|
407 |
|
moel@391
|
408 |
if (!string.IsNullOrEmpty(this.Subtitle))
|
moel@391
|
409 |
{
|
moel@391
|
410 |
rc.DrawMathText(
|
moel@391
|
411 |
new ScreenPoint(dx, dy),
|
moel@391
|
412 |
this.Subtitle,
|
moel@391
|
413 |
this.SubtitleColor ?? this.TextColor,
|
moel@391
|
414 |
this.ActualSubtitleFont,
|
moel@391
|
415 |
this.SubtitleFontSize,
|
moel@391
|
416 |
this.SubtitleFontWeight,
|
moel@391
|
417 |
0,
|
moel@391
|
418 |
HorizontalAlignment.Center,
|
moel@391
|
419 |
VerticalAlignment.Top);
|
moel@391
|
420 |
}
|
moel@391
|
421 |
}
|
moel@391
|
422 |
|
moel@391
|
423 |
/// <summary>
|
moel@391
|
424 |
/// Calculates the plot area (subtract padding, title size and outside legends)
|
moel@391
|
425 |
/// </summary>
|
moel@391
|
426 |
/// <param name="rc">
|
moel@391
|
427 |
/// The rendering context.
|
moel@391
|
428 |
/// </param>
|
moel@391
|
429 |
private void UpdatePlotArea(IRenderContext rc)
|
moel@391
|
430 |
{
|
moel@391
|
431 |
var plotArea = new OxyRect(
|
moel@391
|
432 |
this.Padding.Left,
|
moel@391
|
433 |
this.Padding.Top,
|
moel@391
|
434 |
this.Width - this.Padding.Left - this.Padding.Right,
|
moel@391
|
435 |
this.Height - this.Padding.Top - this.Padding.Bottom);
|
moel@391
|
436 |
|
moel@391
|
437 |
var titleSize = this.MeasureTitles(rc);
|
moel@391
|
438 |
|
moel@391
|
439 |
if (titleSize.Height > 0)
|
moel@391
|
440 |
{
|
moel@391
|
441 |
double titleHeight = titleSize.Height + this.TitlePadding;
|
moel@391
|
442 |
plotArea.Height -= titleHeight;
|
moel@391
|
443 |
plotArea.Top += titleHeight;
|
moel@391
|
444 |
}
|
moel@391
|
445 |
|
moel@391
|
446 |
plotArea.Top += this.ActualPlotMargins.Top;
|
moel@391
|
447 |
plotArea.Height -= this.ActualPlotMargins.Top;
|
moel@391
|
448 |
|
moel@391
|
449 |
plotArea.Height -= this.ActualPlotMargins.Bottom;
|
moel@391
|
450 |
|
moel@391
|
451 |
plotArea.Left += this.ActualPlotMargins.Left;
|
moel@391
|
452 |
plotArea.Width -= this.ActualPlotMargins.Left;
|
moel@391
|
453 |
|
moel@391
|
454 |
plotArea.Width -= this.ActualPlotMargins.Right;
|
moel@391
|
455 |
|
moel@391
|
456 |
// Find the available size for the legend box
|
moel@391
|
457 |
double availableLegendWidth = plotArea.Width;
|
moel@391
|
458 |
double availableLegendHeight = plotArea.Height;
|
moel@391
|
459 |
if (this.LegendPlacement == LegendPlacement.Inside)
|
moel@391
|
460 |
{
|
moel@391
|
461 |
availableLegendWidth -= this.LegendMargin * 2;
|
moel@391
|
462 |
availableLegendHeight -= this.LegendMargin * 2;
|
moel@391
|
463 |
}
|
moel@391
|
464 |
|
moel@391
|
465 |
if (availableLegendWidth < 0)
|
moel@391
|
466 |
{
|
moel@391
|
467 |
availableLegendWidth = 0;
|
moel@391
|
468 |
}
|
moel@391
|
469 |
|
moel@391
|
470 |
if (availableLegendHeight < 0)
|
moel@391
|
471 |
{
|
moel@391
|
472 |
availableLegendHeight = 0;
|
moel@391
|
473 |
}
|
moel@391
|
474 |
|
moel@391
|
475 |
// Calculate the size of the legend box
|
moel@391
|
476 |
var legendSize = this.MeasureLegends(rc, new OxySize(availableLegendWidth, availableLegendHeight));
|
moel@391
|
477 |
|
moel@391
|
478 |
// Adjust the plot area after the size of the legend box has been calculated
|
moel@391
|
479 |
if (this.IsLegendVisible && this.LegendPlacement == LegendPlacement.Outside)
|
moel@391
|
480 |
{
|
moel@391
|
481 |
switch (this.LegendPosition)
|
moel@391
|
482 |
{
|
moel@391
|
483 |
case LegendPosition.LeftTop:
|
moel@391
|
484 |
case LegendPosition.LeftMiddle:
|
moel@391
|
485 |
case LegendPosition.LeftBottom:
|
moel@391
|
486 |
plotArea.Left += legendSize.Width + this.LegendMargin;
|
moel@391
|
487 |
plotArea.Width -= legendSize.Width + this.LegendMargin;
|
moel@391
|
488 |
break;
|
moel@391
|
489 |
case LegendPosition.RightTop:
|
moel@391
|
490 |
case LegendPosition.RightMiddle:
|
moel@391
|
491 |
case LegendPosition.RightBottom:
|
moel@391
|
492 |
plotArea.Width -= legendSize.Width + this.LegendMargin;
|
moel@391
|
493 |
break;
|
moel@391
|
494 |
case LegendPosition.TopLeft:
|
moel@391
|
495 |
case LegendPosition.TopCenter:
|
moel@391
|
496 |
case LegendPosition.TopRight:
|
moel@391
|
497 |
plotArea.Top += legendSize.Height + this.LegendMargin;
|
moel@391
|
498 |
plotArea.Height -= legendSize.Height + this.LegendMargin;
|
moel@391
|
499 |
break;
|
moel@391
|
500 |
case LegendPosition.BottomLeft:
|
moel@391
|
501 |
case LegendPosition.BottomCenter:
|
moel@391
|
502 |
case LegendPosition.BottomRight:
|
moel@391
|
503 |
plotArea.Height -= legendSize.Height + this.LegendMargin;
|
moel@391
|
504 |
break;
|
moel@391
|
505 |
}
|
moel@391
|
506 |
}
|
moel@391
|
507 |
|
moel@391
|
508 |
// Ensure the plot area is valid
|
moel@391
|
509 |
if (plotArea.Height < 0)
|
moel@391
|
510 |
{
|
moel@391
|
511 |
plotArea.Bottom = plotArea.Top + 1;
|
moel@391
|
512 |
}
|
moel@391
|
513 |
|
moel@391
|
514 |
if (plotArea.Width < 0)
|
moel@391
|
515 |
{
|
moel@391
|
516 |
plotArea.Right = plotArea.Left + 1;
|
moel@391
|
517 |
}
|
moel@391
|
518 |
|
moel@391
|
519 |
this.PlotArea = plotArea;
|
moel@391
|
520 |
this.PlotAndAxisArea = new OxyRect(
|
moel@391
|
521 |
plotArea.Left - this.ActualPlotMargins.Left,
|
moel@391
|
522 |
plotArea.Top - this.ActualPlotMargins.Top,
|
moel@391
|
523 |
plotArea.Width + this.ActualPlotMargins.Left + this.ActualPlotMargins.Right,
|
moel@391
|
524 |
plotArea.Height + this.ActualPlotMargins.Top + this.ActualPlotMargins.Bottom);
|
moel@391
|
525 |
this.TitleArea = new OxyRect(this.PlotArea.Left, this.Padding.Top, this.PlotArea.Width, titleSize.Height + (this.TitlePadding * 2));
|
moel@391
|
526 |
this.LegendArea = this.GetLegendRectangle(legendSize);
|
moel@391
|
527 |
}
|
moel@391
|
528 |
}
|
moel@391
|
529 |
} |