/// <summary> /// Calculates the bounds with respect to rotation angle and horizontal/vertical alignment. /// </summary> /// <param name="bounds">The size of the object to calculate bounds for.</param> /// <param name="angle">The rotation angle (degrees).</param> /// <param name="horizontalAlignment">The horizontal alignment.</param> /// <param name="verticalAlignment">The vertical alignment.</param> /// <returns>A minimum bounding rectangle.</returns> public static OxyRect GetBounds(this OxySize bounds, double angle, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) { var u = horizontalAlignment == HorizontalAlignment.Left ? 0 : horizontalAlignment == HorizontalAlignment.Center ? 0.5 : 1; var v = verticalAlignment == VerticalAlignment.Top ? 0 : verticalAlignment == VerticalAlignment.Middle ? 0.5 : 1; var origin = new ScreenVector(u * bounds.Width, v * bounds.Height); if (angle == 0) { return(new OxyRect(-origin.X, -origin.Y, bounds.Width, bounds.Height)); } // the corners of the rectangle var p0 = new ScreenVector(0, 0) - origin; var p1 = new ScreenVector(bounds.Width, 0) - origin; var p2 = new ScreenVector(bounds.Width, bounds.Height) - origin; var p3 = new ScreenVector(0, bounds.Height) - origin; var theta = angle * Math.PI / 180.0; var costh = Math.Cos(theta); var sinth = Math.Sin(theta); Func <ScreenVector, ScreenVector> rotate = p => new ScreenVector((costh * p.X) - (sinth * p.Y), (sinth * p.X) + (costh * p.Y)); var q0 = rotate(p0); var q1 = rotate(p1); var q2 = rotate(p2); var q3 = rotate(p3); var x = Math.Min(Math.Min(q0.X, q1.X), Math.Min(q2.X, q3.X)); var y = Math.Min(Math.Min(q0.Y, q1.Y), Math.Min(q2.Y, q3.Y)); var w = Math.Max(Math.Max(q0.X - x, q1.X - x), Math.Max(q2.X - x, q3.X - x)); var h = Math.Max(Math.Max(q0.Y - y, q1.Y - y), Math.Max(q2.Y - y, q3.Y - y)); return(new OxyRect(x, y, w, h)); }
/// <summary> /// Gets the polygon outline of the specified rotated and aligned box. /// </summary> /// <param name="size">The size of the box.</param> /// <param name="origin">The origin of the box.</param> /// <param name="angle">The rotation angle of the box.</param> /// <param name="horizontalAlignment">The horizontal alignment of the box.</param> /// <param name="verticalAlignment">The vertical alignment of the box.</param> /// <returns>A sequence of points defining the polygon outline of the box.</returns> public static IEnumerable <ScreenPoint> GetPolygon(this OxySize size, ScreenPoint origin, double angle, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) { var u = horizontalAlignment == HorizontalAlignment.Left ? 0 : horizontalAlignment == HorizontalAlignment.Center ? 0.5 : 1; var v = verticalAlignment == VerticalAlignment.Top ? 0 : verticalAlignment == VerticalAlignment.Middle ? 0.5 : 1; var offset = new ScreenVector(u * size.Width, v * size.Height); // the corners of the rectangle var p0 = new ScreenVector(0, 0) - offset; var p1 = new ScreenVector(size.Width, 0) - offset; var p2 = new ScreenVector(size.Width, size.Height) - offset; var p3 = new ScreenVector(0, size.Height) - offset; if (angle != 0) { var theta = angle * Math.PI / 180.0; var costh = Math.Cos(theta); var sinth = Math.Sin(theta); Func <ScreenVector, ScreenVector> rotate = p => new ScreenVector((costh * p.X) - (sinth * p.Y), (sinth * p.X) + (costh * p.Y)); p0 = rotate(p0); p1 = rotate(p1); p2 = rotate(p2); p3 = rotate(p3); } yield return(origin + p0); yield return(origin + p1); yield return(origin + p2); yield return(origin + p3); }
/// <summary> /// Calculates the maximum size of the specified axes. /// </summary> /// <param name="rc"> /// The render context. /// </param> /// <param name="axesOfPositionTier"> /// The axes of position tier. /// </param> /// <returns> /// The maximum size. /// </returns> private static double MaxSizeOfPositionTier(IRenderContext rc, IEnumerable <Axis> axesOfPositionTier) { double maxSizeOfPositionTier = 0; foreach (var axis in axesOfPositionTier) { OxySize size = axis.Measure(rc); if (axis.IsHorizontal()) { if (size.Height > maxSizeOfPositionTier) { maxSizeOfPositionTier = size.Height; } } else { if (size.Width > maxSizeOfPositionTier) { maxSizeOfPositionTier = size.Width; } } } return(maxSizeOfPositionTier); }
/// <summary> /// Draws or measures text containing sub- and superscript. /// </summary> /// <param name="rc">The render context.</param> /// <param name="pt">The point.</param> /// <param name="text">The text.</param> /// <param name="textColor">Color of the text.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">The font size.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="angle">The angle.</param> /// <param name="ha">The horizontal alignment.</param> /// <param name="va">The vertical alignment.</param> /// <param name="maxsize">The maximum size of the text.</param> /// <param name="measure">Measure the size of the text if set to <c>true</c>.</param> /// <returns>The size of the text.</returns> /// <example>Subscript: H_{2}O /// Superscript: E=mc^{2} /// Both: A^{2}_{i,j}</example> public static OxySize DrawMathText( this IRenderContext rc, ScreenPoint pt, string text, OxyColor textColor, string fontFamily, double fontSize, double fontWeight, double angle, HorizontalAlignment ha, VerticalAlignment va, OxySize? maxsize, bool measure) { if (string.IsNullOrEmpty(text)) { return OxySize.Empty; } if (text.Contains("^{") || text.Contains("_{")) { double x = pt.X; double y = pt.Y; // Measure var size = InternalDrawMathText(rc, x, y, text, textColor, fontFamily, fontSize, fontWeight, true, angle); switch (ha) { case HorizontalAlignment.Right: x -= size.Width; break; case HorizontalAlignment.Center: x -= size.Width * 0.5; break; } switch (va) { case VerticalAlignment.Bottom: y -= size.Height; break; case VerticalAlignment.Middle: y -= size.Height * 0.5; break; } InternalDrawMathText(rc, x, y, text, textColor, fontFamily, fontSize, fontWeight, false, angle); return measure ? size : OxySize.Empty; } rc.DrawText(pt, text, textColor, fontFamily, fontSize, fontWeight, angle, ha, va, maxsize); if (measure) { return rc.MeasureText(text, fontFamily, fontSize, fontWeight); } return OxySize.Empty; }
/// <summary> /// Gets the coordinates of the (rotated) background rectangle. /// </summary> /// <param name="position"> /// The position. /// </param> /// <param name="size"> /// The size. /// </param> /// <param name="padding"> /// The padding. /// </param> /// <param name="rotation"> /// The rotation. /// </param> /// <param name="horizontalAlignment"> /// The horizontal alignment. /// </param> /// <param name="verticalAlignment"> /// The vertical alignment. /// </param> /// <returns> /// The background rectangle coordinates. /// </returns> private static IList <ScreenPoint> GetTextBounds( ScreenPoint position, OxySize size, OxyThickness padding, double rotation, HorizontalTextAlign horizontalAlignment, VerticalTextAlign verticalAlignment) { double left, right, top, bottom; switch (horizontalAlignment) { case HorizontalTextAlign.Center: left = -size.Width * 0.5; right = -left; break; case HorizontalTextAlign.Right: left = -size.Width; right = 0; break; default: left = 0; right = size.Width; break; } switch (verticalAlignment) { case VerticalTextAlign.Middle: top = -size.Height * 0.5; bottom = -top; break; case VerticalTextAlign.Bottom: top = -size.Height; bottom = 0; break; default: top = 0; bottom = size.Height; break; } double cost = Math.Cos(rotation / 180 * Math.PI); double sint = Math.Sin(rotation / 180 * Math.PI); var u = new ScreenVector(cost, sint); var v = new ScreenVector(-sint, cost); var polygon = new ScreenPoint[4]; polygon[0] = position + u * (left - padding.Left) + v * (top - padding.Top); polygon[1] = position + u * (right + padding.Right) + v * (top - padding.Top); polygon[2] = position + u * (right + padding.Right) + v * (bottom + padding.Bottom); polygon[3] = position + u * (left - padding.Left) + v * (bottom + padding.Bottom); return(polygon); }
/// <summary> /// Measures the size of the title and subtitle. /// </summary> /// <param name="rc"> /// The rendering context. /// </param> /// <returns> /// Size of the titles. /// </returns> private OxySize MeasureTitles(IRenderContext rc) { OxySize size1 = rc.MeasureText(this.Title, this.ActualTitleFont, this.TitleFontSize, this.TitleFontWeight); OxySize size2 = rc.MeasureText( this.Subtitle, this.SubtitleFont ?? this.ActualSubtitleFont, this.SubtitleFontSize, this.SubtitleFontWeight); double height = size1.Height + size2.Height; double width = Math.Max(size1.Width, size2.Width); return(new OxySize(width, height)); }
public OxySize MeasureText(string text, string fontFamily = "", double fontSize = 10, double fontWeight = 500) { FontDescription desc = new FontDescription(); OxySize size = new OxySize(); int width, height; desc.Family = fontFamily; desc.Size = Pango.Units.FromPixels ((int) fontSize); desc.Weight = PangoWeightFromDouble (fontWeight); layout.FontDescription = desc; layout.SetMarkup (GLib.Markup.EscapeText (text)); layout.GetPixelSize (out width, out height); size.Width = (double) width; size.Height = (double) height; return size; }
/// <summary> /// Renders the title and subtitle. /// </summary> /// <param name="rc"> /// The render context. /// </param> private void RenderTitle(IRenderContext rc) { OxySize size1 = rc.MeasureText(this.Title, this.ActualTitleFont, this.TitleFontSize, this.TitleFontWeight); rc.MeasureText( this.Subtitle, this.SubtitleFont ?? this.ActualSubtitleFont, this.SubtitleFontSize, this.SubtitleFontWeight); // double height = size1.Height + size2.Height; // double dy = (TitleArea.Top+TitleArea.Bottom-height)*0.5; double dy = this.TitleArea.Top; double dx = (this.TitleArea.Left + this.TitleArea.Right) * 0.5; if (!string.IsNullOrEmpty(this.Title)) { rc.DrawMathText( new ScreenPoint(dx, dy), this.Title, this.TitleColor ?? this.TextColor, this.ActualTitleFont, this.TitleFontSize, this.TitleFontWeight, 0, HorizontalAlignment.Center, VerticalAlignment.Top); dy += size1.Height; } if (!string.IsNullOrEmpty(this.Subtitle)) { rc.DrawMathText( new ScreenPoint(dx, dy), this.Subtitle, this.SubtitleColor ?? this.TextColor, this.ActualSubtitleFont, this.SubtitleFontSize, this.SubtitleFontWeight, 0, HorizontalAlignment.Center, VerticalAlignment.Top); } }
/// <summary> /// The measure string. /// </summary> /// <param name="faceName"> /// The font face name. /// </param> /// <param name="height"> /// The height. /// </param> /// <param name="weight"> /// The weight. /// </param> /// <param name="str"> /// The string. /// </param> /// <returns> /// The size of the rendered string. /// </returns> public static OxySize MeasureString(string faceName, int height, int weight, string str) { var lines = Regex.Split(str, "\r\n"); OxySize result = new OxySize(0, 0); foreach (var line in lines) { var hfont = CreateFont(height, 0, 0, 0, weight, 0, 0, 0, 0, 0, 0, 0, 0, faceName); var hdc = GetDC(IntPtr.Zero); var oldobj = SelectObject(hdc, hfont); var temp = GetTextExtent(hdc, line); SelectObject(hdc, oldobj); DeleteObject(hfont); DeleteDC(hdc); var lineSpacing = temp.Height / 3.0; result.Height += temp.Height + lineSpacing; result.Width = Math.Max(temp.Width * 1.28, result.Width); } return(result); }
public void RenderTitle(string title, string subtitle) { OxySize size1 = rc.MeasureText(title, plot.TitleFont, plot.TitleFontSize, plot.TitleFontWeight); OxySize size2 = rc.MeasureText(subtitle, plot.TitleFont, plot.TitleFontSize, plot.TitleFontWeight); double height = size1.Height + size2.Height; double dy = (plot.AxisMargins.Top - height) * 0.5; double dx = (plot.Bounds.Left + plot.Bounds.Right) * 0.5; if (!String.IsNullOrEmpty(title)) { rc.DrawText( new ScreenPoint(dx, dy), title, plot.TextColor, plot.TitleFont, plot.TitleFontSize, plot.TitleFontWeight, 0, HorizontalTextAlign.Center, VerticalTextAlign.Top); } if (!String.IsNullOrEmpty(subtitle)) { rc.DrawText(new ScreenPoint(dx, dy + size1.Height), subtitle, plot.TextColor, plot.TitleFont, plot.SubtitleFontSize, plot.SubtitleFontWeight, 0, HorizontalTextAlign.Center, VerticalTextAlign.Top); } }
/// <summary> /// Renders the axis title. /// </summary> /// <param name="axis"> /// The axis. /// </param> /// <param name="titlePosition"> /// The title position. /// </param> private void RenderAxisTitle(Axis axis, double titlePosition) { bool isHorizontal = axis.IsHorizontal(); OxySize?maxSize = null; if (axis.ClipTitle) { // Calculate the title clipping dimensions double screenLength = isHorizontal ? Math.Abs(axis.ScreenMax.X - axis.ScreenMin.X) : Math.Abs(axis.ScreenMax.Y - axis.ScreenMin.Y); maxSize = new OxySize(screenLength * axis.TitleClippingLength, double.MaxValue); } double angle = -90; var halign = HorizontalAlignment.Center; var valign = VerticalAlignment.Top; var lpt = this.GetAxisTitlePositionAndAlignment(axis, titlePosition, ref angle, ref halign, ref valign); this.rc.SetToolTip(axis.ToolTip); this.rc.DrawMathText( lpt, axis.ActualTitle, axis.ActualTitleColor, axis.ActualTitleFont, axis.ActualTitleFontSize, axis.ActualTitleFontWeight, angle, halign, valign, maxSize); this.rc.SetToolTip(null); }
/// <summary> /// Draws text. /// </summary> /// <param name="p">The position.</param> /// <param name="text">The text.</param> /// <param name="fill">The text color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font (in device independent units, 1/96 inch).</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text (in device independent units, 1/96 inch).</param> public void DrawText( ScreenPoint p, string text, OxyColor fill, string fontFamily = null, double fontSize = 10, double fontWeight = 500, double rotate = 0, HorizontalAlignment halign = HorizontalAlignment.Left, VerticalAlignment valign = VerticalAlignment.Top, OxySize? maxSize = null) { }
/// <summary> /// Gets the rectangle of the legend box. /// </summary> /// <param name="legendSize">Size of the legend box.</param> /// <returns>A rectangle.</returns> private OxyRect GetLegendRectangle(OxySize legendSize) { double top = 0; double left = 0; if (this.LegendPlacement == LegendPlacement.Outside) { switch (this.LegendPosition) { case LegendPosition.LeftTop: case LegendPosition.LeftMiddle: case LegendPosition.LeftBottom: left = this.PlotAndAxisArea.Left - legendSize.Width - this.LegendMargin; break; case LegendPosition.RightTop: case LegendPosition.RightMiddle: case LegendPosition.RightBottom: left = this.PlotAndAxisArea.Right + this.LegendMargin; break; case LegendPosition.TopLeft: case LegendPosition.TopCenter: case LegendPosition.TopRight: top = this.PlotAndAxisArea.Top - legendSize.Height - this.LegendMargin; break; case LegendPosition.BottomLeft: case LegendPosition.BottomCenter: case LegendPosition.BottomRight: top = this.PlotAndAxisArea.Bottom + this.LegendMargin; break; } switch (this.LegendPosition) { case LegendPosition.TopLeft: case LegendPosition.BottomLeft: left = this.PlotArea.Left; break; case LegendPosition.TopRight: case LegendPosition.BottomRight: left = this.PlotArea.Right - legendSize.Width; break; case LegendPosition.LeftTop: case LegendPosition.RightTop: top = this.PlotArea.Top; break; case LegendPosition.LeftBottom: case LegendPosition.RightBottom: top = this.PlotArea.Bottom - legendSize.Height; break; case LegendPosition.LeftMiddle: case LegendPosition.RightMiddle: top = (this.PlotArea.Top + this.PlotArea.Bottom - legendSize.Height) * 0.5; break; case LegendPosition.TopCenter: case LegendPosition.BottomCenter: left = (this.PlotArea.Left + this.PlotArea.Right - legendSize.Width) * 0.5; break; } } else { switch (this.LegendPosition) { case LegendPosition.LeftTop: case LegendPosition.LeftMiddle: case LegendPosition.LeftBottom: left = this.PlotArea.Left + this.LegendMargin; break; case LegendPosition.RightTop: case LegendPosition.RightMiddle: case LegendPosition.RightBottom: left = this.PlotArea.Right - legendSize.Width - this.LegendMargin; break; case LegendPosition.TopLeft: case LegendPosition.TopCenter: case LegendPosition.TopRight: top = this.PlotArea.Top + this.LegendMargin; break; case LegendPosition.BottomLeft: case LegendPosition.BottomCenter: case LegendPosition.BottomRight: top = this.PlotArea.Bottom - legendSize.Height - this.LegendMargin; break; } switch (this.LegendPosition) { case LegendPosition.TopLeft: case LegendPosition.BottomLeft: left = this.PlotArea.Left + this.LegendMargin; break; case LegendPosition.TopRight: case LegendPosition.BottomRight: left = this.PlotArea.Right - legendSize.Width - this.LegendMargin; break; case LegendPosition.LeftTop: case LegendPosition.RightTop: top = this.PlotArea.Top + this.LegendMargin; break; case LegendPosition.LeftBottom: case LegendPosition.RightBottom: top = this.PlotArea.Bottom - legendSize.Height - this.LegendMargin; break; case LegendPosition.LeftMiddle: case LegendPosition.RightMiddle: top = (this.PlotArea.Top + this.PlotArea.Bottom - legendSize.Height) * 0.5; break; case LegendPosition.TopCenter: case LegendPosition.BottomCenter: left = (this.PlotArea.Left + this.PlotArea.Right - legendSize.Width) * 0.5; break; } } return new OxyRect(left, top, legendSize.Width, legendSize.Height); }
/// <summary> /// Renders the legend for the specified series. /// </summary> /// <param name="rc">The render context.</param> /// <param name="s">The series.</param> /// <param name="rect">The position and size of the legend.</param> private void RenderLegend(IRenderContext rc, Series.Series s, OxyRect rect) { var actualItemAlignment = this.LegendItemAlignment; if (this.LegendOrientation == LegendOrientation.Horizontal) { // center/right alignment is not supported for horizontal orientation actualItemAlignment = HorizontalAlignment.Left; } double x = rect.Left; switch (actualItemAlignment) { case HorizontalAlignment.Center: x = (rect.Left + rect.Right) / 2; if (this.LegendSymbolPlacement == LegendSymbolPlacement.Left) { x -= (this.LegendSymbolLength + this.LegendSymbolMargin) / 2; } else { x -= (this.LegendSymbolLength + this.LegendSymbolMargin) / 2; } break; case HorizontalAlignment.Right: x = rect.Right; // if (LegendSymbolPlacement == LegendSymbolPlacement.Right) x -= this.LegendSymbolLength + this.LegendSymbolMargin; break; } if (this.LegendSymbolPlacement == LegendSymbolPlacement.Left) { x += this.LegendSymbolLength + this.LegendSymbolMargin; } double y = rect.Top; var maxsize = new OxySize(Math.Max(rect.Width - this.LegendSymbolLength - this.LegendSymbolMargin, 0), rect.Height); rc.SetToolTip(s.ToolTip); var textSize = rc.DrawMathText( new ScreenPoint(x, y), s.Title, this.LegendTextColor.GetActualColor(this.TextColor), this.LegendFont ?? this.DefaultFont, this.LegendFontSize, this.LegendFontWeight, 0, actualItemAlignment, VerticalAlignment.Top, maxsize, true); double x0 = x; switch (actualItemAlignment) { case HorizontalAlignment.Center: x0 = x - (textSize.Width * 0.5); break; case HorizontalAlignment.Right: x0 = x - textSize.Width; break; } var symbolRect = new OxyRect( this.LegendSymbolPlacement == LegendSymbolPlacement.Right ? x0 + textSize.Width + this.LegendSymbolMargin : x0 - this.LegendSymbolMargin - this.LegendSymbolLength, rect.Top, this.LegendSymbolLength, textSize.Height); s.RenderLegend(rc, symbolRect); rc.SetToolTip(null); }
/// <summary> /// Calculates the bounds with respect to rotation angle and horizontal/vertical alignment. /// </summary> /// <param name="bounds">The size of the object to calculate bounds for.</param> /// <param name="angle">The rotation angle (degrees).</param> /// <returns>A minimum bounding rectangle.</returns> private static OxySize MeasureRotatedRectangleBound(OxySize bounds, double angle) { var oxyRect = bounds.GetBounds(angle, HorizontalAlignment.Center, VerticalAlignment.Middle); return(new OxySize(oxyRect.Width, oxyRect.Height)); }
/// <summary> /// Renders the axis title. /// </summary> /// <param name="axis"> /// The axis. /// </param> /// <param name="titlePosition"> /// The title position. /// </param> private void RenderAxisTitle(Axis axis, double titlePosition) { bool isHorizontal = axis.IsHorizontal(); OxySize? maxSize = null; if (axis.ClipTitle) { // Calculate the title clipping dimensions double screenLength = isHorizontal ? Math.Abs(axis.ScreenMax.X - axis.ScreenMin.X) : Math.Abs(axis.ScreenMax.Y - axis.ScreenMin.Y); maxSize = new OxySize(screenLength * axis.TitleClippingLength, double.MaxValue); } double angle = -90; var halign = HorizontalTextAlign.Center; var valign = VerticalTextAlign.Top; var lpt = this.GetAxisTitlePositionAndAlignment(axis, titlePosition, ref angle, ref halign, ref valign); this.rc.SetToolTip(axis.ToolTip); this.rc.DrawText( lpt, axis.ActualTitle, axis.ActualTitleColor, axis.ActualTitleFont, axis.ActualTitleFontSize, axis.ActualTitleFontWeight, angle, halign, valign, maxSize); this.rc.SetToolTip(null); }
/// <summary> /// Draws the clipped text. /// </summary> /// <param name="rc">The rendering context.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="p">The position.</param> /// <param name="text">The text.</param> /// <param name="fill">The fill color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="horizontalAlignment">The horizontal align.</param> /// <param name="verticalAlignment">The vertical align.</param> /// <param name="maxSize">Size of the max.</param> public static void DrawClippedText( this IRenderContext rc, OxyRect clippingRectangle, ScreenPoint p, string text, OxyColor fill, string fontFamily = null, double fontSize = 10, double fontWeight = 500, double rotate = 0, HorizontalTextAlign horizontalAlignment = HorizontalTextAlign.Left, VerticalTextAlign verticalAlignment = VerticalTextAlign.Top, OxySize? maxSize = null) { if (clippingRectangle.Contains(p.X, p.Y)) { rc.DrawText(p, text, fill, fontFamily, fontSize, fontWeight, rotate, horizontalAlignment, verticalAlignment, maxSize); } }
/// <summary> /// Renders the legend for the specified series. /// </summary> /// <param name="rc"> /// The render context. /// </param> /// <param name="s"> /// The series. /// </param> /// <param name="rect"> /// The position and size of the legend. /// </param> private void RenderLegend(IRenderContext rc, Series.Series s, OxyRect rect) { double x = rect.Left; switch (this.LegendItemAlignment) { case HorizontalAlignment.Center: x = (rect.Left + rect.Right) / 2; if (this.LegendSymbolPlacement == LegendSymbolPlacement.Left) { x -= (this.LegendSymbolLength + this.LegendSymbolMargin) / 2; } else { x -= (this.LegendSymbolLength + this.LegendSymbolMargin) / 2; } break; case HorizontalAlignment.Right: x = rect.Right; // if (LegendSymbolPlacement == LegendSymbolPlacement.Right) x -= this.LegendSymbolLength + this.LegendSymbolMargin; break; } if (this.LegendSymbolPlacement == LegendSymbolPlacement.Left) { x += this.LegendSymbolLength + this.LegendSymbolMargin; } double y = rect.Top; var maxsize = new OxySize(Math.Max(rect.Right - x, 0), Math.Max(rect.Bottom - y, 0)); var textSize = rc.DrawMathText( new ScreenPoint(x, y), s.Title, this.LegendTextColor ?? this.TextColor, this.LegendFont ?? this.DefaultFont, this.LegendFontSize, this.LegendFontWeight, 0, this.LegendItemAlignment, VerticalAlignment.Top, maxsize, true); double x0 = x; switch (this.LegendItemAlignment) { case HorizontalAlignment.Center: x0 = x - (textSize.Width * 0.5); break; case HorizontalAlignment.Right: x0 = x - textSize.Width; break; } var symbolRect = new OxyRect( this.LegendSymbolPlacement == LegendSymbolPlacement.Right ? x0 + textSize.Width + this.LegendSymbolMargin : x0 - this.LegendSymbolMargin - this.LegendSymbolLength, rect.Top, this.LegendSymbolLength, textSize.Height); s.RenderLegend(rc, symbolRect); }
/// <summary> /// Draws the clipped text. /// </summary> /// <param name="rc">The rendering context.</param> /// <param name="clippingRectangle">The clipping rectangle.</param> /// <param name="p">The position.</param> /// <param name="text">The text.</param> /// <param name="fill">The fill color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="horizontalAlignment">The horizontal align.</param> /// <param name="verticalAlignment">The vertical align.</param> /// <param name="maxSize">Size of the max.</param> public static void DrawClippedText( this IRenderContext rc, OxyRect clippingRectangle, ScreenPoint p, string text, OxyColor fill, string fontFamily = null, double fontSize = 10, double fontWeight = 500, double rotate = 0, HorizontalAlignment horizontalAlignment = HorizontalAlignment.Left, VerticalAlignment verticalAlignment = VerticalAlignment.Top, OxySize? maxSize = null) { if (rc.SetClip(clippingRectangle)) { rc.DrawText(p, text, fill, fontFamily, fontSize, fontWeight, rotate, horizontalAlignment, verticalAlignment, maxSize); rc.ResetClip(); return; } // fall back simply check position if (clippingRectangle.Contains(p.X, p.Y)) { rc.DrawText(p, text, fill, fontFamily, fontSize, fontWeight, rotate, horizontalAlignment, verticalAlignment, maxSize); } }
/// <summary> /// Initializes a new instance of the <see cref="OxyRect"/> struct by location and size. /// </summary> /// <param name="p0">The location.</param> /// <param name="size">The size.</param> public OxyRect(ScreenPoint p0, OxySize size) : this(p0.X, p0.Y, size.Width, size.Height) { }
/// <summary> /// Calculates the bounds with respect to rotation angle and horizontal/vertical alignment. /// </summary> /// <param name="bounds">The size of the object to calculate bounds for.</param> /// <param name="angle">The rotation angle (degrees).</param> /// <returns>A minimum bounding rectangle.</returns> private static OxySize MeasureRotatedRectangleBound(OxySize bounds, double angle) { var oxyRect = bounds.GetBounds(angle, HorizontalAlignment.Center, VerticalAlignment.Middle); return new OxySize(oxyRect.Width, oxyRect.Height); }
/// <summary> /// Draws the text. /// </summary> /// <param name="p">The position of the text.</param> /// <param name="text">The text.</param> /// <param name="fill">The fill color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text.</param> public override void DrawText( ScreenPoint p, string text, OxyColor fill, string fontFamily, double fontSize, double fontWeight, double rotate, HorizontalAlignment halign, VerticalAlignment valign, OxySize? maxSize) { this.rc.DrawText(p, text, fill, this.GetFontFamily(fontFamily), fontSize, fontWeight, rotate, halign, valign, maxSize); }
/// <summary> /// Renders the legend for the specified series. /// </summary> /// <param name="rc"> /// The render context. /// </param> /// <param name="s"> /// The series. /// </param> /// <param name="rect"> /// The position and size of the legend. /// </param> private void RenderLegend(IRenderContext rc, Series s, OxyRect rect) { double x = rect.Left; switch (this.LegendItemAlignment) { case HorizontalTextAlign.Center: x = (rect.Left + rect.Right) / 2; if (this.LegendSymbolPlacement == LegendSymbolPlacement.Left) { x -= (this.LegendSymbolLength + this.LegendSymbolMargin) / 2; } else { x -= (this.LegendSymbolLength + this.LegendSymbolMargin) / 2; } break; case HorizontalTextAlign.Right: x = rect.Right; // if (LegendSymbolPlacement == LegendSymbolPlacement.Right) x -= this.LegendSymbolLength + this.LegendSymbolMargin; break; } if (this.LegendSymbolPlacement == LegendSymbolPlacement.Left) { x += this.LegendSymbolLength + this.LegendSymbolMargin; } double y = rect.Top; var maxsize = new OxySize(Math.Max(rect.Right - x, 0), Math.Max(rect.Bottom - y, 0)); var textSize = rc.DrawMathText( new ScreenPoint(x, y), s.Title, this.LegendTextColor ?? this.TextColor, this.LegendFont ?? this.DefaultFont, this.LegendFontSize, this.LegendFontWeight, 0, this.LegendItemAlignment, VerticalTextAlign.Top, maxsize, true); double x0 = x; switch (this.LegendItemAlignment) { case HorizontalTextAlign.Center: x0 = x - (textSize.Width * 0.5); break; case HorizontalTextAlign.Right: x0 = x - textSize.Width; break; } var symbolRect = new OxyRect( this.LegendSymbolPlacement == LegendSymbolPlacement.Right ? x0 + textSize.Width + this.LegendSymbolMargin : x0 - this.LegendSymbolMargin - this.LegendSymbolLength, rect.Top, this.LegendSymbolLength, textSize.Height); s.RenderLegend(rc, symbolRect); }
/// <summary> /// The draw text. /// </summary> /// <param name="p"> /// The p. /// </param> /// <param name="text"> /// The text. /// </param> /// <param name="fill"> /// The fill. /// </param> /// <param name="fontFamily"> /// The font family. /// </param> /// <param name="fontSize"> /// The font size. /// </param> /// <param name="fontWeight"> /// The font weight. /// </param> /// <param name="rotate"> /// The rotate. /// </param> /// <param name="halign"> /// The halign. /// </param> /// <param name="valign"> /// The valign. /// </param> /// <param name="maxSize"> /// The maximum size of the text. /// </param> public override void DrawText( ScreenPoint p, string text, OxyColor fill, string fontFamily, double fontSize, double fontWeight, double rotate, HorizontalTextAlign halign, VerticalTextAlign valign, OxySize? maxSize) { FontStyle fs = FontStyle.Regular; if (fontWeight >= 700) { fs = FontStyle.Bold; } var font = new Font(fontFamily, (float)fontSize * FontsizeFactor, fs); var sf = new StringFormat { Alignment = StringAlignment.Near }; SizeF size = this.g.MeasureString(text, font); if (maxSize != null) { if (size.Width > maxSize.Value.Width) { size.Width = (float)maxSize.Value.Width; } if (size.Height > maxSize.Value.Height) { size.Height = (float)maxSize.Value.Height; } } float dx = 0; if (halign == HorizontalTextAlign.Center) { dx = -size.Width / 2; // sf.Alignment = StringAlignment.Center; } if (halign == HorizontalTextAlign.Right) { dx = -size.Width; // sf.Alignment = StringAlignment.Far; } float dy = 0; sf.LineAlignment = StringAlignment.Near; if (valign == VerticalTextAlign.Middle) { // sf.LineAlignment = StringAlignment.Center; dy = -size.Height / 2; } if (valign == VerticalTextAlign.Bottom) { // sf.LineAlignment = StringAlignment.Far; dy = -size.Height; } this.g.TranslateTransform((float)p.X, (float)p.Y); if (Math.Abs(rotate) > double.Epsilon) { this.g.RotateTransform((float)rotate); } this.g.TranslateTransform(dx, dy); var layoutRectangle = new RectangleF(0, 0, size.Width, size.Height); this.g.DrawString(text, font, this.ToBrush(fill), layoutRectangle, sf); this.g.ResetTransform(); }
/// <summary> /// Measures the legends. /// </summary> /// <param name="rc">The render context.</param> /// <param name="availableSize">The available size for the legend box.</param> /// <returns>The size of the legend box.</returns> private OxySize MeasureLegends(IRenderContext rc, OxySize availableSize) { return(this.RenderOrMeasureLegends(rc, new OxyRect(0, 0, availableSize.Width, availableSize.Height), true)); }
/// <summary> /// Gets the rectangle of the legend box. /// </summary> /// <param name="legendSize">Size of the legend box.</param> /// <returns>A rectangle.</returns> private OxyRect GetLegendRectangle(OxySize legendSize) { double top = 0; double left = 0; if (this.LegendPlacement == LegendPlacement.Outside) { switch (this.LegendPosition) { case LegendPosition.LeftTop: case LegendPosition.LeftMiddle: case LegendPosition.LeftBottom: left = this.PlotAndAxisArea.Left - legendSize.Width - this.LegendMargin; break; case LegendPosition.RightTop: case LegendPosition.RightMiddle: case LegendPosition.RightBottom: left = this.PlotAndAxisArea.Right + this.LegendMargin; break; case LegendPosition.TopLeft: case LegendPosition.TopCenter: case LegendPosition.TopRight: top = this.PlotAndAxisArea.Top - legendSize.Height - this.LegendMargin; break; case LegendPosition.BottomLeft: case LegendPosition.BottomCenter: case LegendPosition.BottomRight: top = this.PlotAndAxisArea.Bottom + this.LegendMargin; break; } switch (this.LegendPosition) { case LegendPosition.TopLeft: case LegendPosition.BottomLeft: left = this.PlotArea.Left; break; case LegendPosition.TopRight: case LegendPosition.BottomRight: left = this.PlotArea.Right - legendSize.Width; break; case LegendPosition.LeftTop: case LegendPosition.RightTop: top = this.PlotArea.Top; break; case LegendPosition.LeftBottom: case LegendPosition.RightBottom: top = this.PlotArea.Bottom - legendSize.Height; break; case LegendPosition.LeftMiddle: case LegendPosition.RightMiddle: top = (this.PlotArea.Top + this.PlotArea.Bottom - legendSize.Height) * 0.5; break; case LegendPosition.TopCenter: case LegendPosition.BottomCenter: left = (this.PlotArea.Left + this.PlotArea.Right - legendSize.Width) * 0.5; break; } } else { switch (this.LegendPosition) { case LegendPosition.LeftTop: case LegendPosition.LeftMiddle: case LegendPosition.LeftBottom: left = this.PlotArea.Left + this.LegendMargin; break; case LegendPosition.RightTop: case LegendPosition.RightMiddle: case LegendPosition.RightBottom: left = this.PlotArea.Right - legendSize.Width - this.LegendMargin; break; case LegendPosition.TopLeft: case LegendPosition.TopCenter: case LegendPosition.TopRight: top = this.PlotArea.Top + this.LegendMargin; break; case LegendPosition.BottomLeft: case LegendPosition.BottomCenter: case LegendPosition.BottomRight: top = this.PlotArea.Bottom - legendSize.Height - this.LegendMargin; break; } switch (this.LegendPosition) { case LegendPosition.TopLeft: case LegendPosition.BottomLeft: left = this.PlotArea.Left + this.LegendMargin; break; case LegendPosition.TopRight: case LegendPosition.BottomRight: left = this.PlotArea.Right - legendSize.Width - this.LegendMargin; break; case LegendPosition.LeftTop: case LegendPosition.RightTop: top = this.PlotArea.Top + this.LegendMargin; break; case LegendPosition.LeftBottom: case LegendPosition.RightBottom: top = this.PlotArea.Bottom - legendSize.Height - this.LegendMargin; break; case LegendPosition.LeftMiddle: case LegendPosition.RightMiddle: top = (this.PlotArea.Top + this.PlotArea.Bottom - legendSize.Height) * 0.5; break; case LegendPosition.TopCenter: case LegendPosition.BottomCenter: left = (this.PlotArea.Left + this.PlotArea.Right - legendSize.Width) * 0.5; break; } } return(new OxyRect(left, top, legendSize.Width, legendSize.Height)); }
/// <summary> /// The internal draw math text. /// </summary> /// <param name="rc"> /// The render context. /// </param> /// <param name="x"> /// The x. /// </param> /// <param name="y"> /// The y. /// </param> /// <param name="s"> /// The s. /// </param> /// <param name="textColor"> /// The text color. /// </param> /// <param name="fontFamily"> /// The font family. /// </param> /// <param name="fontSize"> /// The font size. /// </param> /// <param name="fontWeight"> /// The font weight. /// </param> /// <param name="measureOnly"> /// The measure only. /// </param> /// <returns> /// The size of the text. /// </returns> private static OxySize InternalDrawMathText( IRenderContext rc, double x, double y, string s, OxyColor textColor, string fontFamily, double fontSize, double fontWeight, bool measureOnly) { int i = 0; double currentX = x; double maximumX = x; double maxHeight = 0; // http://en.wikipedia.org/wiki/Subscript_and_superscript double superscriptY = y + fontSize * SuperAlignment; double superscriptFontSize = fontSize * SuperSize; double subscriptY = y + fontSize * SubAlignment; double subscriptFontSize = fontSize * SubSize; Func <double, double, string, double, OxySize> drawText = (xb, yb, text, fSize) => { if (!measureOnly) { rc.DrawText(new ScreenPoint(xb, yb), text, textColor, fontFamily, fSize, fontWeight); } return(rc.MeasureText(text, fontFamily, fSize, fontWeight)); }; while (i < s.Length) { // Superscript if (i + 1 < s.Length && s[i] == '^' && s[i + 1] == '{') { int i1 = s.IndexOf('}', i); if (i1 != -1) { string supString = s.Substring(i + 2, i1 - i - 2); i = i1 + 1; OxySize size = drawText(currentX, superscriptY, supString, superscriptFontSize); if (currentX + size.Width > maximumX) { maximumX = currentX + size.Width; } continue; } } // Subscript if (i + 1 < s.Length && s[i] == '_' && s[i + 1] == '{') { int i1 = s.IndexOf('}', i); if (i1 != -1) { string subString = s.Substring(i + 2, i1 - i - 2); i = i1 + 1; OxySize size = drawText(currentX, subscriptY, subString, subscriptFontSize); if (currentX + size.Width > maximumX) { maximumX = currentX + size.Width; } continue; } } // Regular text int i2 = s.IndexOfAny("^_".ToCharArray(), i); string regularString; if (i2 == -1) { regularString = s.Substring(i); i = s.Length; } else { regularString = s.Substring(i, i2 - i); i = i2; } currentX = maximumX + 2; OxySize size2 = drawText(currentX, y, regularString, fontSize); currentX += size2.Width + 2; maxHeight = Math.Max(maxHeight, size2.Height); maximumX = currentX; } return(new OxySize(maximumX - x, maxHeight)); }
/// <summary> /// Draws the text. /// </summary> /// <param name="p">The position of the text.</param> /// <param name="text">The text.</param> /// <param name="fill">The fill color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text.</param> public override void DrawText( ScreenPoint p, string text, OxyColor fill, string fontFamily, double fontSize, double fontWeight, double rotate, HorizontalAlignment halign, VerticalAlignment valign, OxySize? maxSize) { this.doc.SaveState(); this.doc.SetFont(fontFamily, fontSize / 96 * 72, fontWeight > 500); this.doc.SetFillColor(fill); double width, height; this.doc.MeasureText(text, out width, out height); if (maxSize != null) { if (width > maxSize.Value.Width) { width = Math.Max(maxSize.Value.Width, 0); } if (height > maxSize.Value.Height) { height = Math.Max(maxSize.Value.Height, 0); } } double dx = 0; if (halign == HorizontalAlignment.Center) { dx = -width / 2; } if (halign == HorizontalAlignment.Right) { dx = -width; } double dy = 0; if (valign == VerticalAlignment.Middle) { dy = -height / 2; } if (valign == VerticalAlignment.Top) { dy = -height; } double y = this.doc.PageHeight - p.Y; this.doc.Translate(p.X, y); if (Math.Abs(rotate) > 1e-6) { this.doc.Rotate(-rotate); } this.doc.Translate(dx, dy); // this.doc.DrawRectangle(0, 0, width, height); this.doc.SetClippingRectangle(0, 0, width, height); this.doc.DrawText(0, 0, text); this.doc.RestoreState(); }
/// <summary> /// Draws text containing sub- and superscript. /// </summary> /// <param name="rc">The render context.</param> /// <param name="pt">The point.</param> /// <param name="text">The text.</param> /// <param name="textColor">Color of the text.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">The font size.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="angle">The angle.</param> /// <param name="ha">The horizontal alignment.</param> /// <param name="va">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text.</param> /// <example>Subscript: H_{2}O /// Superscript: E=mc^{2} /// Both: A^{2}_{i,j}</example> public static void DrawMathText( this IRenderContext rc, ScreenPoint pt, string text, OxyColor textColor, string fontFamily, double fontSize, double fontWeight, double angle, HorizontalAlignment ha, VerticalAlignment va, OxySize? maxSize = null) { DrawMathText(rc, pt, text, textColor, fontFamily, fontSize, fontWeight, angle, ha, va, maxSize, false); }
/// <summary> /// Draws the text. /// </summary> /// <param name="p">The position of the text.</param> /// <param name="text">The text.</param> /// <param name="fill">The fill color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text.</param> public abstract void DrawText( ScreenPoint p, string text, OxyColor fill, string fontFamily, double fontSize, double fontWeight, double rotate, HorizontalAlignment halign, VerticalAlignment valign, OxySize? maxSize);
/// <summary> /// Measures the size of the axis (maximum axis label width/height). /// </summary> /// <param name="rc"> /// The render context. /// </param> /// <returns> /// The size of the axis. /// </returns> public virtual OxySize Measure(IRenderContext rc) { IList<double> majorTickValues; IList<double> minorTickValues; IList<double> majorLabelValues; this.GetTickValues(out majorLabelValues, out majorTickValues, out minorTickValues); var maximumTextSize = new OxySize(); foreach (double v in majorLabelValues) { string s = this.FormatValue(v); var size = rc.MeasureText(s, this.ActualFont, this.ActualFontSize, this.ActualFontWeight); if (size.Width > maximumTextSize.Width) { maximumTextSize.Width = size.Width; } if (size.Height > maximumTextSize.Height) { maximumTextSize.Height = size.Height; } } var labelTextSize = rc.MeasureText( this.ActualTitle, this.ActualFont, this.ActualFontSize, this.ActualFontWeight); double width = 0; double height = 0; if (this.IsHorizontal()) { switch (this.TickStyle) { case TickStyle.Outside: height += this.MajorTickSize; break; case TickStyle.Crossing: height += this.MajorTickSize * 0.75; break; } height += this.AxisTickToLabelDistance; height += maximumTextSize.Height; if (labelTextSize.Height > 0) { height += this.AxisTitleDistance; height += labelTextSize.Height; } } else { switch (this.TickStyle) { case TickStyle.Outside: width += this.MajorTickSize; break; case TickStyle.Crossing: width += this.MajorTickSize * 0.75; break; } width += this.AxisTickToLabelDistance; width += maximumTextSize.Width; if (labelTextSize.Height > 0) { width += this.AxisTitleDistance; width += labelTextSize.Height; } } return new OxySize(width, height); }
/// <summary> /// Renders or measures the legends. /// </summary> /// <param name="rc">The render context.</param> /// <param name="rect">Provides the available size if measuring, otherwise it provides the position and size of the legend.</param> /// <param name="measureOnly">Specify if the size of the legend box should be measured only (not rendered).</param> /// <returns>The size of the legend box.</returns> private OxySize RenderOrMeasureLegends(IRenderContext rc, OxyRect rect, bool measureOnly = false) { // Render background and border around legend if (!measureOnly && rect.Width > 0 && rect.Height > 0) { rc.DrawRectangleAsPolygon(rect, this.LegendBackground, this.LegendBorder, this.LegendBorderThickness); } double availableWidth = rect.Width; double availableHeight = rect.Height; double x = this.LegendPadding; double top = this.LegendPadding; var size = new OxySize(); // Render/measure the legend title if (!string.IsNullOrEmpty(this.LegendTitle)) { OxySize titleSize; if (measureOnly) { titleSize = rc.MeasureMathText( this.LegendTitle, this.LegendTitleFont ?? this.DefaultFont, this.LegendTitleFontSize, this.LegendTitleFontWeight); } else { titleSize = rc.DrawMathText( new ScreenPoint(rect.Left + x, rect.Top + top), this.LegendTitle, this.LegendTitleColor.GetActualColor(this.TextColor), this.LegendTitleFont ?? this.DefaultFont, this.LegendTitleFontSize, this.LegendTitleFontWeight, 0, HorizontalAlignment.Left, VerticalAlignment.Top, null, true); } top += titleSize.Height; size = new OxySize(x + titleSize.Width + this.LegendPadding, top + titleSize.Height); } double y = top; double lineHeight = 0; // tolerance for floating-point number comparisons const double Epsilon = 1e-3; // the maximum item with in the column being rendered (only used for vertical orientation) double maxItemWidth = 0; var items = this.LegendItemOrder == LegendItemOrder.Reverse ? this.Series.Reverse().Where(s => s.IsVisible) : this.Series.Where(s => s.IsVisible); // When orientation is vertical and alignment is center or right, the items cannot be rendered before // the max item width has been calculated. Render the items for each column, and at the end. var seriesToRender = new Dictionary <Series.Series, OxyRect>(); Action renderItems = () => { foreach (var sr in seriesToRender) { var itemRect = sr.Value; var itemSeries = sr.Key; double rwidth = availableWidth; if (itemRect.Left + rwidth + this.LegendPadding > rect.Left + availableWidth) { rwidth = rect.Left + availableWidth - itemRect.Left - this.LegendPadding; } double rheight = itemRect.Height; if (rect.Top + rheight + this.LegendPadding > rect.Top + availableHeight) { rheight = rect.Top + availableHeight - rect.Top - this.LegendPadding; } var r = new OxyRect(itemRect.Left, itemRect.Top, Math.Max(rwidth, 0), Math.Max(rheight, 0)); this.RenderLegend(rc, itemSeries, r); } seriesToRender.Clear(); }; foreach (var s in items) { // Skip series with empty title if (string.IsNullOrEmpty(s.Title) || !s.RenderInLegend) { continue; } var textSize = rc.MeasureMathText(s.Title, this.LegendFont ?? this.DefaultFont, this.LegendFontSize, this.LegendFontWeight); double itemWidth = this.LegendSymbolLength + this.LegendSymbolMargin + textSize.Width; double itemHeight = textSize.Height; if (this.LegendOrientation == LegendOrientation.Horizontal) { // Add spacing between items if (x > this.LegendPadding) { x += this.LegendItemSpacing; } // Check if the item is too large to fit within the available width if (x + itemWidth > availableWidth - this.LegendPadding + Epsilon) { // new line x = this.LegendPadding; y += lineHeight + this.LegendLineSpacing; lineHeight = 0; } // Update the max size of the current line lineHeight = Math.Max(lineHeight, textSize.Height); if (!measureOnly) { seriesToRender.Add(s, new OxyRect(rect.Left + x, rect.Top + y, itemWidth, itemHeight)); } x += itemWidth; // Update the max width and height of the legend box size = new OxySize(Math.Max(size.Width, x), Math.Max(size.Height, y + textSize.Height)); } else { if (y + itemHeight > availableHeight - this.LegendPadding + Epsilon) { renderItems(); y = top; x += maxItemWidth + this.LegendColumnSpacing; maxItemWidth = 0; } if (!measureOnly) { seriesToRender.Add(s, new OxyRect(rect.Left + x, rect.Top + y, itemWidth, itemHeight)); } y += itemHeight + this.LegendLineSpacing; // Update the max size of the items in the current column maxItemWidth = Math.Max(maxItemWidth, itemWidth); // Update the max width and height of the legend box size = new OxySize(Math.Max(size.Width, x + itemWidth), Math.Max(size.Height, y)); } } renderItems(); if (size.Width > 0) { size = new OxySize(size.Width + this.LegendPadding, size.Height); } if (size.Height > 0) { size = new OxySize(size.Width, size.Height + this.LegendPadding); } if (size.Width > availableWidth) { size = new OxySize(availableWidth, size.Height); } if (size.Height > availableHeight) { size = new OxySize(size.Width, availableHeight); } if (!double.IsNaN(this.LegendMaxWidth) && size.Width > this.LegendMaxWidth) { size = new OxySize(this.LegendMaxWidth, size.Height); } if (!double.IsNaN(this.LegendMaxHeight) && size.Height > this.LegendMaxHeight) { size = new OxySize(size.Width, this.LegendMaxHeight); } return(size); }
public static PlotModel DrawTextMaxSize() { var model = new PlotModel(); model.Annotations.Add(new DelegateAnnotation(rc => { const string Font = "Arial"; const double FontSize = 32d; const double FontWeight = FontWeights.Bold; const double D = FontSize * 1.6; const double X = 20; const double X2 = 200; double y = 20; var testStrings = new[] { "iii", "jjj", "OxyPlot", "Bottom", "100", "KML" }; foreach (var text in testStrings) { var maxSize = rc.MeasureText(text, Font, FontSize, FontWeight); var p = new ScreenPoint(X, y); rc.DrawText(p, text, OxyColors.Black, Font, FontSize, FontWeight, maxSize: maxSize); var rect = new OxyRect(p, maxSize); rc.DrawRectangle(rect, OxyColors.Undefined, OxyColors.Black); var p2 = new ScreenPoint(X2, y); var maxSize2 = new OxySize(maxSize.Width / 2, maxSize.Height / 2); rc.DrawText(p2, text, OxyColors.Black, Font, FontSize, FontWeight, maxSize: maxSize2); var rect2 = new OxyRect(p2, maxSize2); rc.DrawRectangle(rect2, OxyColors.Undefined, OxyColors.Black); y += D; } })); return model; }
/// <summary> /// Renders the series on the specified render context. /// </summary> /// <param name="rc">The rendering context.</param> /// <param name="model">The model.</param> public override void Render(IRenderContext rc, PlotModel model) { if (this.XAxis == null) { return; } this.symbolPosition = model.PlotArea.Bottom; this.symbolSize = rc.MeasureText(this.Symbol, this.FontFamily, this.FontSize); foreach (var v in this.Values) { if (double.IsNaN(v) || v < this.XAxis.ActualMinimum || v > this.XAxis.ActualMaximum) { continue; } double x = this.XAxis.Transform(v); rc.DrawText( new ScreenPoint(x, this.symbolPosition), this.Symbol, this.Color, this.FontFamily, this.FontSize, FontWeights.Normal, 0, HorizontalAlignment.Center, VerticalAlignment.Bottom); } }
/// <summary> /// Draws text with metrics. /// </summary> /// <param name="text">The text.</param> /// <param name="font">The font.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="expectedWidth">The expected width.</param> /// <param name="expectedHeight">The expected height.</param> /// <param name="baseline">The baseline position.</param> /// <param name="xheight">The x-height position.</param> /// <param name="ascent">The ascent position.</param> /// <param name="descent">The descent position.</param> /// <param name="before">The before position.</param> /// <param name="after">The after position.</param> /// <param name="platform">The platform.</param> /// <returns> /// A plot model. /// </returns> private static PlotModel DrawTextWithMetrics(string text, string font, double fontSize, double expectedWidth, double expectedHeight, double baseline, double xheight, double ascent, double descent, double before, double after, string platform) { // http://msdn.microsoft.com/en-us/library/ms742190(v=vs.110).aspx // http://msdn.microsoft.com/en-us/library/xwf9s90b(v=vs.110).aspx // http://msdn.microsoft.com/en-us/library/windows/desktop/ms533824(v=vs.85).aspx // https://developer.apple.com/library/mac/documentation/TextFonts/Conceptual/CocoaTextArchitecture/FontHandling/FontHandling.html var model = new PlotModel(); model.Annotations.Add( new DelegateAnnotation( rc => { var size = rc.MeasureText(text, font, fontSize); var expectedSize = new OxySize(expectedWidth, expectedHeight); rc.DrawText(new ScreenPoint(300, 50), "Font size: " + fontSize, OxyColors.Black, font, 12); rc.DrawText(new ScreenPoint(300, 70), "Actual size: " + size.ToString("0.00", CultureInfo.InvariantCulture), OxyColors.Black, font, 12); rc.DrawText(new ScreenPoint(300, 90), "Size on " + platform + ": " + expectedSize.ToString("0.00", CultureInfo.InvariantCulture), OxyColors.Green, font, 12); var p = new ScreenPoint(20, 50); rc.DrawText(p, text, OxyColors.Black, font, fontSize); rc.FillCircle(p, 3, OxyColors.Black); // actual bounds rc.DrawRectangle(new OxyRect(p, size), OxyColors.Undefined, OxyColors.Black); // Expected bounds (WPF) rc.DrawRectangle(new OxyRect(p, expectedSize), OxyColors.Undefined, OxyColors.Green); var color = OxyColor.FromAColor(180, OxyColors.Red); var pen = new OxyPen(color); // Expected vertical positions (WPF) var x1 = p.X - 10; var x2 = p.X + expectedSize.Width + 10; rc.DrawLine(x1, baseline, x2, baseline, pen); rc.DrawLine(x1, xheight, x2, xheight, pen); rc.DrawLine(x1, ascent, x2, ascent, pen); rc.DrawLine(x1, descent, x2, descent, pen); // Expected horizonal positions (WPF) var y1 = p.Y - 10; var y2 = p.Y + expectedSize.Height + 10; rc.DrawLine(before, y1, before, y2, pen); rc.DrawLine(after, y1, after, y2, pen); })); model.MouseDown += (s, e) => Debug.WriteLine(e.Position); return model; }
/// <summary> /// Measures the legends. /// </summary> /// <param name="rc">The render context.</param> /// <param name="availableSize">The available size for the legend box.</param> /// <returns>The size of the legend box.</returns> private OxySize MeasureLegends(IRenderContext rc, OxySize availableSize) { return this.RenderOrMeasureLegends(rc, new OxyRect(0, 0, availableSize.Width, availableSize.Height), true); }
/// <summary> /// Gets the coordinates of the (rotated) background rectangle. /// </summary> /// <param name="position"> /// The position. /// </param> /// <param name="size"> /// The size. /// </param> /// <param name="padding"> /// The padding. /// </param> /// <param name="rotation"> /// The rotation. /// </param> /// <param name="horizontalAlignment"> /// The horizontal alignment. /// </param> /// <param name="verticalAlignment"> /// The vertical alignment. /// </param> /// <returns> /// The background rectangle coordinates. /// </returns> private static IList<ScreenPoint> GetTextBounds( ScreenPoint position, OxySize size, OxyThickness padding, double rotation, HorizontalTextAlign horizontalAlignment, VerticalTextAlign verticalAlignment) { double left, right, top, bottom; switch (horizontalAlignment) { case HorizontalTextAlign.Center: left = -size.Width * 0.5; right = -left; break; case HorizontalTextAlign.Right: left = -size.Width; right = 0; break; default: left = 0; right = size.Width; break; } switch (verticalAlignment) { case VerticalTextAlign.Middle: top = -size.Height * 0.5; bottom = -top; break; case VerticalTextAlign.Bottom: top = -size.Height; bottom = 0; break; default: top = 0; bottom = size.Height; break; } double cost = Math.Cos(rotation / 180 * Math.PI); double sint = Math.Sin(rotation / 180 * Math.PI); var u = new ScreenVector(cost, sint); var v = new ScreenVector(-sint, cost); var polygon = new ScreenPoint[4]; polygon[0] = position + u * (left - padding.Left) + v * (top - padding.Top); polygon[1] = position + u * (right + padding.Right) + v * (top - padding.Top); polygon[2] = position + u * (right + padding.Right) + v * (bottom + padding.Bottom); polygon[3] = position + u * (left - padding.Left) + v * (bottom + padding.Bottom); return polygon; }
/// <summary> /// Renders or measures the legends. /// </summary> /// <param name="rc">The render context.</param> /// <param name="rect">Provides the available size if measuring, otherwise it provides the position and size of the legend.</param> /// <param name="measureOnly">Specify if the size of the legend box should be measured only (not rendered).</param> /// <returns>The size of the legend box.</returns> private OxySize RenderOrMeasureLegends(IRenderContext rc, OxyRect rect, bool measureOnly = false) { // Render background and border around legend if (!measureOnly && rect.Width > 0 && rect.Height > 0) { rc.DrawRectangleAsPolygon(rect, this.LegendBackground, this.LegendBorder, this.LegendBorderThickness); } double availableWidth = rect.Width; double availableHeight = rect.Height; double x = this.LegendPadding; double top = this.LegendPadding; var size = new OxySize(); // Render/measure the legend title if (!string.IsNullOrEmpty(this.LegendTitle)) { OxySize titleSize; if (measureOnly) { titleSize = rc.MeasureMathText( this.LegendTitle, this.LegendTitleFont ?? this.DefaultFont, this.LegendTitleFontSize, this.LegendTitleFontWeight); } else { titleSize = rc.DrawMathText( new ScreenPoint(rect.Left + x, rect.Top + top), this.LegendTitle, this.LegendTitleColor.GetActualColor(this.TextColor), this.LegendTitleFont ?? this.DefaultFont, this.LegendTitleFontSize, this.LegendTitleFontWeight, 0, HorizontalAlignment.Left, VerticalAlignment.Top, null, true); } top += titleSize.Height; size.Width = x + titleSize.Width + this.LegendPadding; size.Height = top + titleSize.Height; } double y = top; double lineHeight = 0; // tolerance for floating-point number comparisons const double Epsilon = 1e-3; // the maximum item with in the column being rendered (only used for vertical orientation) double maxItemWidth = 0; var items = this.LegendItemOrder == LegendItemOrder.Reverse ? this.Series.Reverse().Where(s => s.IsVisible) : this.Series.Where(s => s.IsVisible); // When orientation is vertical and alignment is center or right, the items cannot be rendered before // the max item width has been calculated. Render the items for each column, and at the end. var seriesToRender = new Dictionary<Series.Series, OxyRect>(); Action renderItems = () => { foreach (var sr in seriesToRender) { var itemRect = sr.Value; var itemSeries = sr.Key; double rwidth = availableWidth; if (itemRect.Left + rwidth + this.LegendPadding > rect.Left + availableWidth) { rwidth = rect.Left + availableWidth - itemRect.Left - this.LegendPadding; } double rheight = itemRect.Height; if (rect.Top + rheight + this.LegendPadding > rect.Top + availableHeight) { rheight = rect.Top + availableHeight - rect.Top - this.LegendPadding; } var r = new OxyRect(itemRect.Left, itemRect.Top, Math.Max(rwidth, 0), Math.Max(rheight, 0)); this.RenderLegend(rc, itemSeries, r); } seriesToRender.Clear(); }; foreach (var s in items) { // Skip series with empty title if (string.IsNullOrEmpty(s.Title)) { continue; } var textSize = rc.MeasureMathText(s.Title, this.LegendFont ?? this.DefaultFont, this.LegendFontSize, this.LegendFontWeight); double itemWidth = this.LegendSymbolLength + this.LegendSymbolMargin + textSize.Width; double itemHeight = textSize.Height; if (this.LegendOrientation == LegendOrientation.Horizontal) { // Add spacing between items if (x > this.LegendPadding) { x += this.LegendItemSpacing; } // Check if the item is too large to fit within the available width if (x + itemWidth > availableWidth - this.LegendPadding + Epsilon) { // new line x = this.LegendPadding; y += lineHeight; lineHeight = 0; } // Update the max size of the current line lineHeight = Math.Max(lineHeight, textSize.Height); if (!measureOnly) { seriesToRender.Add(s, new OxyRect(rect.Left + x, rect.Top + y, itemWidth, itemHeight)); } x += itemWidth; // Update the max width of the legend box size.Width = Math.Max(size.Width, x); // Update the max height of the legend box size.Height = Math.Max(size.Height, y + textSize.Height); } else { if (y + itemHeight > availableHeight - this.LegendPadding + Epsilon) { renderItems(); y = top; x += maxItemWidth + this.LegendColumnSpacing; maxItemWidth = 0; } if (!measureOnly) { seriesToRender.Add(s, new OxyRect(rect.Left + x, rect.Top + y, itemWidth, itemHeight)); } y += itemHeight; // Update the max size of the items in the current column maxItemWidth = Math.Max(maxItemWidth, itemWidth); // Update the max width of the legend box size.Width = Math.Max(size.Width, x + itemWidth); // Update the max height of the legend box size.Height = Math.Max(size.Height, y); } } renderItems(); if (size.Width > 0) { size.Width += this.LegendPadding; } if (size.Height > 0) { size.Height += this.LegendPadding; } if (size.Width > availableWidth) { size.Width = availableWidth; } if (size.Height > availableHeight) { size.Height = availableHeight; } if (!double.IsNaN(this.LegendMaxWidth) && size.Width > this.LegendMaxWidth) { size.Width = this.LegendMaxWidth; } return size; }
/// <summary> /// Draws the text. /// </summary> /// <param name="p">The p.</param> /// <param name="text">The text.</param> /// <param name="c">The c.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotate.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">Size of the max.</param> public override void DrawText( ScreenPoint p, string text, OxyColor c, string fontFamily, double fontSize, double fontWeight, double rotate, HorizontalAlignment halign, VerticalAlignment valign, OxySize? maxSize) { if (string.IsNullOrEmpty(text)) { return; } var lines = Regex.Split(text, "\r\n"); if (valign == VerticalAlignment.Bottom) { for (var i = lines.Length - 1; i >= 0; i--) { var line = lines[i]; var size = this.MeasureText(line, fontFamily, fontSize, fontWeight); this.w.WriteText(p, line, c, fontFamily, fontSize, fontWeight, rotate, halign, valign); p.X += Math.Sin(rotate / 180.0 * Math.PI) * size.Height; p.Y -= Math.Cos(rotate / 180.0 * Math.PI) * size.Height; } } else { foreach (var line in lines) { var size = this.MeasureText(line, fontFamily, fontSize, fontWeight); this.w.WriteText(p, line, c, fontFamily, fontSize, fontWeight, rotate, halign, valign); p.X -= Math.Sin(rotate / 180.0 * Math.PI) * size.Height; p.Y += Math.Cos(rotate / 180.0 * Math.PI) * size.Height; } } }
/// <summary> /// The measure string. /// </summary> /// <param name="faceName"> /// The font face name. /// </param> /// <param name="height"> /// The height. /// </param> /// <param name="weight"> /// The weight. /// </param> /// <param name="str"> /// The string. /// </param> /// <returns> /// The size of the rendered string. /// </returns> public static OxySize MeasureString(string faceName, int height, int weight, string str) { var lines = Regex.Split(str, "\r\n"); OxySize result = new OxySize(0, 0); foreach (var line in lines) { var hfont = CreateFont(height, 0, 0, 0, weight, 0, 0, 0, 0, 0, 0, 0, 0, faceName); var hdc = GetDC(IntPtr.Zero); var oldobj = SelectObject(hdc, hfont); var temp = GetTextExtent(hdc, line); SelectObject(hdc, oldobj); DeleteObject(hfont); DeleteDC(hdc); var lineSpacing = temp.Height / 3.0; result.Height += temp.Height + lineSpacing; result.Width = Math.Max(temp.Width * 1.28, result.Width); } return result; }
/// <summary> /// Draws the text. /// </summary> /// <param name="p">The p.</param> /// <param name="text">The text.</param> /// <param name="fill">The fill color.</param> /// <param name="fontFamily">The font family.</param> /// <param name="fontSize">Size of the font.</param> /// <param name="fontWeight">The font weight.</param> /// <param name="rotate">The rotation angle.</param> /// <param name="halign">The horizontal alignment.</param> /// <param name="valign">The vertical alignment.</param> /// <param name="maxSize">The maximum size of the text.</param> public override void DrawText( ScreenPoint p, string text, OxyColor fill, string fontFamily, double fontSize, double fontWeight, double rotate, HorizontalAlignment halign, VerticalAlignment valign, OxySize? maxSize) { if (text == null) { return; } var fontStyle = fontWeight < 700 ? FontStyle.Regular : FontStyle.Bold; using (var font = CreateFont(fontFamily, fontSize, fontStyle)) { this.stringFormat.Alignment = StringAlignment.Near; this.stringFormat.LineAlignment = StringAlignment.Near; var size = this.g.MeasureString(text, font, int.MaxValue, this.stringFormat); if (maxSize != null) { if (size.Width > maxSize.Value.Width) { size.Width = (float)maxSize.Value.Width; } if (size.Height > maxSize.Value.Height) { size.Height = (float)maxSize.Value.Height; } } float dx = 0; if (halign == HorizontalAlignment.Center) { dx = -size.Width / 2; } if (halign == HorizontalAlignment.Right) { dx = -size.Width; } float dy = 0; this.stringFormat.LineAlignment = StringAlignment.Near; if (valign == VerticalAlignment.Middle) { dy = -size.Height / 2; } if (valign == VerticalAlignment.Bottom) { dy = -size.Height; } var graphicsState = this.g.Save(); this.g.TranslateTransform((float)p.X, (float)p.Y); var layoutRectangle = new RectangleF(0, 0, size.Width, size.Height); if (Math.Abs(rotate) > double.Epsilon) { this.g.RotateTransform((float)rotate); layoutRectangle.Height += (float)(fontSize / 18.0); } this.g.TranslateTransform(dx, dy); this.g.DrawString(text, font, fill.ToBrush(), layoutRectangle, this.stringFormat); this.g.Restore(graphicsState); } }
/// <summary> /// Calculates the plot area (subtract padding, title size and outside legends) /// </summary> /// <param name="rc">The rendering context.</param> private void UpdatePlotArea(IRenderContext rc) { var plotArea = new OxyRect( this.PlotBounds.Left + this.Padding.Left, this.PlotBounds.Top + this.Padding.Top, Math.Max(0, this.Width - this.Padding.Left - this.Padding.Right), Math.Max(0, this.Height - this.Padding.Top - this.Padding.Bottom)); var titleSize = this.MeasureTitles(rc); if (titleSize.Height > 0) { var titleHeight = titleSize.Height + this.TitlePadding; plotArea = new OxyRect(plotArea.Left, plotArea.Top + titleHeight, plotArea.Width, Math.Max(0, plotArea.Height - titleHeight)); } plotArea = plotArea.Deflate(this.ActualPlotMargins); if (this.IsLegendVisible) { // Make space for legends OxySize maxLegendSize = new OxySize(0, 0); double legendMargin = 0; // first run Outside Left-Side legends foreach (var legend in this.Legends.Where(l => l.LegendPlacement == LegendPlacement.Outside && (l.IsLegendVisible && (l.LegendPosition == LegendPosition.LeftTop || l.LegendPosition == LegendPosition.LeftMiddle || l.LegendPosition == LegendPosition.LeftBottom)))) { // Find the available size for the legend box var availableLegendWidth = plotArea.Width; var availableLegendHeight = double.IsNaN(legend.LegendMaxHeight) ? plotArea.Height : Math.Min(plotArea.Height, legend.LegendMaxHeight); var lsiz = legend.GetLegendSize(rc, new OxySize(availableLegendWidth, availableLegendHeight)); legend.LegendSize = lsiz; maxLegendSize = new OxySize(maxLegendSize.Width > lsiz.Width ? maxLegendSize.Width : lsiz.Width, maxLegendSize.Height > lsiz.Height ? maxLegendSize.Height : lsiz.Height); if (legend.LegendMargin > legendMargin) { legendMargin = legend.LegendMargin; } } // Adjust the plot area after the size of the legend has been calculated if (maxLegendSize.Width > 0 || maxLegendSize.Height > 0) { plotArea = new OxyRect(plotArea.Left + maxLegendSize.Width + legendMargin, plotArea.Top, Math.Max(0, plotArea.Width - (maxLegendSize.Width + legendMargin)), plotArea.Height); } maxLegendSize = new OxySize(0, 0); legendMargin = 0; // second run Outside Right-Side legends foreach (var legend in this.Legends.Where(l => l.LegendPlacement == LegendPlacement.Outside && (l.IsLegendVisible && (l.LegendPosition == LegendPosition.RightTop || l.LegendPosition == LegendPosition.RightMiddle || l.LegendPosition == LegendPosition.RightBottom)))) { // Find the available size for the legend box var availableLegendWidth = plotArea.Width; var availableLegendHeight = double.IsNaN(legend.LegendMaxHeight) ? plotArea.Height : Math.Min(plotArea.Height, legend.LegendMaxHeight); var lsiz = legend.GetLegendSize(rc, new OxySize(availableLegendWidth, availableLegendHeight)); legend.LegendSize = lsiz; maxLegendSize = new OxySize(maxLegendSize.Width > lsiz.Width ? maxLegendSize.Width : lsiz.Width, maxLegendSize.Height > lsiz.Height ? maxLegendSize.Height : lsiz.Height); if (legend.LegendMargin > legendMargin) { legendMargin = legend.LegendMargin; } } // Adjust the plot area after the size of the legend has been calculated if (maxLegendSize.Width > 0 || maxLegendSize.Height > 0) { plotArea = new OxyRect(plotArea.Left, plotArea.Top, Math.Max(0, plotArea.Width - (maxLegendSize.Width + legendMargin)), plotArea.Height); } maxLegendSize = new OxySize(0, 0); legendMargin = 0; // third run Outside Top legends foreach (var legend in this.Legends.Where(l => l.LegendPlacement == LegendPlacement.Outside && (l.IsLegendVisible && (l.LegendPosition == LegendPosition.TopLeft || l.LegendPosition == LegendPosition.TopCenter || l.LegendPosition == LegendPosition.TopRight)))) { // Find the available size for the legend box var availableLegendWidth = plotArea.Width; var availableLegendHeight = double.IsNaN(legend.LegendMaxHeight) ? plotArea.Height : Math.Min(plotArea.Height, legend.LegendMaxHeight); var lsiz = legend.GetLegendSize(rc, new OxySize(availableLegendWidth, availableLegendHeight)); legend.LegendSize = lsiz; maxLegendSize = new OxySize(maxLegendSize.Width > lsiz.Width ? maxLegendSize.Width : lsiz.Width, maxLegendSize.Height > lsiz.Height ? maxLegendSize.Height : lsiz.Height); if (legend.LegendMargin > legendMargin) { legendMargin = legend.LegendMargin; } } // Adjust the plot area after the size of the legend has been calculated if (maxLegendSize.Width > 0 || maxLegendSize.Height > 0) { plotArea = new OxyRect(plotArea.Left, plotArea.Top + maxLegendSize.Height + legendMargin, plotArea.Width, Math.Max(0, plotArea.Height - (maxLegendSize.Height + legendMargin))); } maxLegendSize = new OxySize(0, 0); legendMargin = 0; // fourth run Outside Bottom legends foreach (var legend in this.Legends.Where(l => l.LegendPlacement == LegendPlacement.Outside && (l.IsLegendVisible && (l.LegendPosition == LegendPosition.BottomLeft || l.LegendPosition == LegendPosition.BottomCenter || l.LegendPosition == LegendPosition.BottomRight)))) { // Find the available size for the legend box var availableLegendWidth = plotArea.Width; var availableLegendHeight = double.IsNaN(legend.LegendMaxHeight) ? plotArea.Height : Math.Min(plotArea.Height, legend.LegendMaxHeight); var lsiz = legend.GetLegendSize(rc, new OxySize(availableLegendWidth, availableLegendHeight)); legend.LegendSize = lsiz; maxLegendSize = new OxySize(maxLegendSize.Width > lsiz.Width ? maxLegendSize.Width : lsiz.Width, maxLegendSize.Height > lsiz.Height ? maxLegendSize.Height : lsiz.Height); if (legend.LegendMargin > legendMargin) { legendMargin = legend.LegendMargin; } } // Adjust the plot area after the size of the legend has been calculated if (maxLegendSize.Width > 0 || maxLegendSize.Height > 0) { plotArea = new OxyRect(plotArea.Left, plotArea.Top, plotArea.Width, Math.Max(0, plotArea.Height - (maxLegendSize.Height + legendMargin))); } // Finally calculate size of inside legends foreach (var legend in this.Legends.Where(l => l.LegendPlacement == LegendPlacement.Inside && l.IsLegendVisible)) { // Find the available size for the legend box var availableLegendWidth = plotArea.Width; var availableLegendHeight = double.IsNaN(legend.LegendMaxHeight) ? plotArea.Height : Math.Min(plotArea.Height, legend.LegendMaxHeight); if (legend.LegendPlacement == LegendPlacement.Inside) { availableLegendWidth -= legend.LegendMargin * 2; availableLegendHeight -= legend.LegendMargin * 2; } legend.LegendSize = legend.GetLegendSize(rc, new OxySize(availableLegendWidth, availableLegendHeight)); } } // Ensure the plot area is valid if (plotArea.Height < 0) { plotArea = new OxyRect(plotArea.Left, plotArea.Top, plotArea.Width, 1); } if (plotArea.Width < 0) { plotArea = new OxyRect(plotArea.Left, plotArea.Top, 1, plotArea.Height); } this.PlotArea = plotArea; this.PlotAndAxisArea = plotArea.Inflate(this.ActualPlotMargins); switch (this.TitleHorizontalAlignment) { case TitleHorizontalAlignment.CenteredWithinView: this.TitleArea = new OxyRect( this.PlotBounds.Left, this.PlotBounds.Top + this.Padding.Top, this.Width, titleSize.Height + (this.TitlePadding * 2)); break; default: this.TitleArea = new OxyRect( this.PlotArea.Left, this.PlotBounds.Top + this.Padding.Top, this.PlotArea.Width, titleSize.Height + (this.TitlePadding * 2)); break; } // Calculate the legend area for each legend. foreach (var l in this.Legends) { l.LegendArea = l.GetLegendRectangle(l.LegendSize); } }