/// <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> /// 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 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 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> /// 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 += new ScreenVector(Math.Sin(rotate / 180.0 * Math.PI) * size.Height, 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 += new ScreenVector(-Math.Sin(rotate / 180.0 * Math.PI) * size.Height, Math.Cos(rotate / 180.0 * Math.PI) * size.Height); } } }
/// <summary> /// Distorts the specified points. /// </summary> /// <param name="points">The input points.</param> /// <returns> /// The distorted points. /// </returns> private ScreenPoint[] Distort(IEnumerable <ScreenPoint> points) { // See the Mathematica / Matplotlib solutions // http://jakevdp.github.io/blog/2012/10/07/xkcd-style-plots-in-matplotlib/ // http://jakevdp.github.io/blog/2013/07/10/XKCD-plots-in-matplotlib/ // http://mathematica.stackexchange.com/questions/11350/xkcd-style-graphs // http://www.mail-archive.com/[email protected]/msg25499.html // http://nbviewer.ipython.org/gist/anonymous/3835181 // http://matplotlib.org/xkcd/ // http://xkcd.com/ // The following code is just to show that some randomness is working - this should be improved IList <ScreenPoint> interpolated = this.Interpolate(points, this.InterpolationDistance).ToArray(); var result = new ScreenPoint[interpolated.Count]; var randomNumbers = this.GenerateRandomNumbers(interpolated.Count); randomNumbers = this.ApplyMovingAverage(randomNumbers, 5); var d = this.DistortionFactor; double d2 = d / 2; for (int i = 0; i < interpolated.Count; i++) { if (i == 0 || i == interpolated.Count - 1) { result[i] = interpolated[i]; continue; } var tangent = interpolated[i + 1] - interpolated[i - 1]; tangent.Normalize(); var normal = new ScreenVector(tangent.Y, -tangent.X); var delta = normal * ((randomNumbers[i] * d) - d2); result[i] = interpolated[i] + delta; } return(result); }
public override void Render(IRenderContext rc, PlotModel model) { // transform to screen coordinates var p0 = this.Transform(this.StartPoint); var p1 = this.Transform(this.EndPoint); var direction = p1 - p0; var normal = new ScreenVector(direction.Y, -direction.X); // the end points of the arrow head, scaled by length of arrow var p2 = p1 - (direction * 0.2) + (normal * 0.1); var p3 = p1 - (direction * 0.2) - (normal * 0.1); // draw the line segments rc.DrawLineSegments(new[] { p0, p1, p1, p2, p1, p3 }, this.ActualColor, this.StrokeThickness); }
/// <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 += new ScreenVector(Math.Sin(rotate / 180.0 * Math.PI) * size.Height, 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 += new ScreenVector(-Math.Sin(rotate / 180.0 * Math.PI) * size.Height, Math.Cos(rotate / 180.0 * Math.PI) * size.Height); } } }
/// <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 = StringHelper.SplitLines(text); var textSize = this.MeasureText(text, fontFamily, fontSize, fontWeight); var lineHeight = textSize.Height / lines.Length; var lineOffset = new ScreenVector(-Math.Sin(rotate / 180.0 * Math.PI) * lineHeight, +Math.Cos(rotate / 180.0 * Math.PI) * lineHeight); if (this.UseVerticalTextAlignmentWorkaround) { // offset the position, and set the valign to neutral value of `Bottom` double offsetRatio = valign == VerticalAlignment.Bottom ? (1.0 - lines.Length) : valign == VerticalAlignment.Top ? 1.0 : (1.0 - (lines.Length / 2.0)); valign = VerticalAlignment.Bottom; p += lineOffset * offsetRatio; 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 += lineOffset; } } else { 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 -= lineOffset; } } 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 += lineOffset; } } } }
/// <summary> /// Called when a touch gesture is moving. /// </summary> /// <param name="touches">The touches.</param> /// <param name="evt">The event arguments.</param> public override void TouchesMoved(NSSet touches, UIEvent evt) { // it seems to be easier to handle touch events here than using UIPanGesturRecognizer and UIPinchGestureRecognizer base.TouchesMoved(touches, evt); // convert the touch points to an array var ta = touches.ToArray<UITouch>(); if (ta.Length > 0) { // get current and previous location of the first touch point var t1 = ta[0]; var l1 = t1.LocationInView(this).ToScreenPoint(); var pl1 = t1.PreviousLocationInView(this).ToScreenPoint(); var l = l1; var t = l1 - pl1; var s = new ScreenVector(1, 1); if (ta.Length > 1) { // get current and previous location of the second touch point var t2 = ta[1]; var l2 = t2.LocationInView(this).ToScreenPoint(); var pl2 = t2.PreviousLocationInView(this).ToScreenPoint(); var d = l1 - l2; var pd = pl1 - pl2; if (!this.KeepAspectRatioWhenPinching) { var scalex = System.Math.Abs(pd.X) > 0 ? System.Math.Abs(d.X / pd.X) : 1; var scaley = System.Math.Abs(pd.Y) > 0 ? System.Math.Abs(d.Y / pd.Y) : 1; s = new ScreenVector(scalex, scaley); } else { var scale = pd.Length > 0 ? d.Length / pd.Length : 1; s = new ScreenVector(scale, scale); } } var e = new OxyTouchEventArgs { Position = l, DeltaTranslation = t, DeltaScale = s }; this.ActualController.HandleTouchDelta(this, e); } }
/// <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> /// Distorts the specified points. /// </summary> /// <param name="points">The input points.</param> /// <returns> /// The distorted points. /// </returns> private ScreenPoint[] Distort(IEnumerable<ScreenPoint> points) { // See the Mathematica / Matplotlib solutions // http://jakevdp.github.io/blog/2012/10/07/xkcd-style-plots-in-matplotlib/ // http://jakevdp.github.io/blog/2013/07/10/XKCD-plots-in-matplotlib/ // http://mathematica.stackexchange.com/questions/11350/xkcd-style-graphs // http://www.mail-archive.com/[email protected]/msg25499.html // http://nbviewer.ipython.org/gist/anonymous/3835181 // http://matplotlib.org/xkcd/ // http://xkcd.com/ // The following code is just to show that some randomness is working - this should be improved IList<ScreenPoint> interpolated = this.Interpolate(points, this.InterpolationDistance).ToArray(); var result = new ScreenPoint[interpolated.Count]; var randomNumbers = this.GenerateRandomNumbers(interpolated.Count); randomNumbers = this.ApplyMovingAverage(randomNumbers, 5); var d = this.DistortionFactor; double d2 = d / 2; for (int i = 0; i < interpolated.Count; i++) { if (i == 0 || i == interpolated.Count - 1) { result[i] = interpolated[i]; continue; } var tangent = interpolated[i + 1] - interpolated[i - 1]; tangent.Normalize(); var normal = new ScreenVector(tangent.Y, -tangent.X); var delta = normal * ((randomNumbers[i] * d) - d2); result[i] = interpolated[i] + delta; } return result; }