/// <summary> /// Initializes a new instance of the <see cref="OsmSharp.Math.Primitives.RectangleF2D"/> class. /// </summary> /// <param name="x">The x coordinate.</param> /// <param name="y">The y coordinate.</param> /// <param name="width">Width.</param> /// <param name="height">Height.</param> /// <param name="directionY">Direction y.</param> public RectangleF2D(double x, double y, double width, double height, VectorF2D directionY) { _bottomLeft = new PointF2D (x, y); directionY = directionY.Normalize (); _vectorY = directionY * height; _vectorX = directionY.Rotate90 (true) * width; }
/// <summary> /// Initializes a new instance of the <see cref="OsmSharp.Math.Primitives.RectangleF2D"/> class. /// </summary> /// <param name="x">The x coordinate of the bottom-left corner.</param> /// <param name="y">The y coordinate of the bottom-left corner.</param> /// <param name="width">Width.</param> /// <param name="height">Height.</param> /// <remarks>This creates a rectangle in the direction of the x- and y-axis, performance is almost always better when using <see cref="OsmSharp.Math.Primitives.BoxF2D"/> in this case.</remarks> public RectangleF2D(double x, double y, double width, double height) { _bottomLeft = new PointF2D (x, y); _vectorX = new VectorF2D (width, 0); _vectorY = new VectorF2D (0, height); }
/// <summary> /// Initializes a new instance of the <see cref="OsmSharp.Math.Primitives.RectangleF2D"/> class. /// </summary> /// <param name="x">The x coordinate of the bottom-left corner.</param> /// <param name="y">The y coordinate of the bottom-left corner.</param> /// <param name="width">Width.</param> /// <param name="height">Height.</param> /// <param name="angleY">The angle relative to the y-axis.</param> public RectangleF2D(double x, double y, double width, double height, Degree angleY) { _bottomLeft = new PointF2D (x, y); VectorF2D directionY = VectorF2D.FromAngleY (angleY); _vectorY = directionY * height; _vectorX = directionY.Rotate90 (true) * width; }
/// <summary> /// Initializes a new instance of the <see cref="OsmSharp.Math.Primitives.RectangleF2D"/> class. /// </summary> /// <param name="bottomLeft">Bottom left.</param> /// <param name="width">Width.</param> /// <param name="height">Height.</param> /// <param name="directionY">Direction y.</param> public RectangleF2D(PointF2D bottomLeft, double width, double height, VectorF2D directionY) { _bottomLeft = bottomLeft; VectorF2D directionYNormal = directionY.Normalize (); _vectorY = directionYNormal * height; _vectorX = directionYNormal.Rotate90 (true) * width; }
/// <summary> /// Creates a new RectangleF2D from given bounds, center and direction. /// </summary> /// <param name="centerX"></param> /// <param name="centerY"></param> /// <param name="width">Width.</param> /// <param name="height">Height.</param> /// <param name="directionY">The direction of the y-axis.</param> /// <returns></returns> public static RectangleF2D FromBoundsAndCenter(double width, double height, double centerX, double centerY, VectorF2D directionY) { VectorF2D directionYNormal = directionY.Normalize(); VectorF2D directionXNormal = directionYNormal.Rotate90(true); PointF2D center = new PointF2D(centerX, centerY); PointF2D bottomLeft = center - (directionYNormal * (height / 2)) - (directionXNormal * (width / 2)); return new RectangleF2D(bottomLeft, width, height, directionY); }
/// <summary> /// Initializes a new instance of the <see cref="OsmSharp.Math.Primitives.RectangleF2D"/> class. /// </summary> /// <param name="x">The x coordinate of the bottom-left corner.</param> /// <param name="y">The y coordinate of the bottom-left corner.</param> /// <param name="width">Width.</param> /// <param name="height">Height.</param> /// <remarks>This creates a rectangle in the direction of the x- and y-axis, performance is almost always better when using <see cref="OsmSharp.Math.Primitives.BoxF2D"/> in this case.</remarks> public RectangleF2D(double x, double y, double width, double height) { _bottomLeft = new PointF2D(x, y); _vectorX = new VectorF2D(width, 0); _vectorY = new VectorF2D(0, height); }
public void DrawLineTextSegment(Target2DWrapper <Graphics> target, double[] x, double[] y, string text, int color, double size, int?haloColor, int?haloRadius, double middlePosition, float[] characterWidths, double textLength, Font font, float characterHeight, Brush haloBrush, Brush brush) { // see if the text is 'upside down'. double averageAngle = 0; double first = middlePosition - (textLength / 2.0); PointF2D current = Polyline2D.PositionAtPosition(x, y, first); for (int idx = 0; idx < text.Length; idx++) { double nextPosition = middlePosition - (textLength / 2.0) + ((textLength / (text.Length)) * (idx + 1)); PointF2D next = Polyline2D.PositionAtPosition(x, y, nextPosition); // Translate to the final position, the center of line-segment between 'current' and 'next' PointF2D position = current + ((next - current) / 2.0); // calculate the angle. VectorF2D vector = next - current; VectorF2D horizontal = new VectorF2D(1, 0); double angleDegrees = ((Degree)horizontal.Angle(vector)).Value; averageAngle = averageAngle + angleDegrees; current = next; } averageAngle = averageAngle / text.Length; // reverse if 'upside down'. double[] xText = x; double[] yText = y; if (averageAngle > 90 && averageAngle < 180 + 90) { // the average angle is > PI => means upside down. xText = x.Reverse <double>().ToArray <double>(); yText = y.Reverse <double>().ToArray <double>(); } // calculate a central position along the line. first = middlePosition - (textLength / 2.0); current = Polyline2D.PositionAtPosition(xText, yText, first); double nextPosition2 = first; for (int idx = 0; idx < text.Length; idx++) { nextPosition2 = nextPosition2 + characterWidths[idx]; //double nextPosition = middle - (textLength / 2.0) + ((textLength / (text.Length)) * (idx + 1)); PointF2D next = Polyline2D.PositionAtPosition(xText, yText, nextPosition2); char currentChar = text[idx]; using (GraphicsPath characterPath = new GraphicsPath()) { characterPath.AddString(currentChar.ToString(), font.FontFamily, (int)font.Style, font.Size, Point.Empty, StringFormat.GenericTypographic); var pathBounds = characterPath.GetBounds(); // Transformation matrix to move the character to the correct location. // Note that all actions on the Matrix class are prepended, so we apply them in reverse. var transform = new Matrix(); // Translate to the final position, the center of line-segment between 'current' and 'next' PointF2D position = current; //PointF2D position = current + ((next - current) / 2.0); double[] transformed = this.Tranform(position[0], position[1]); transform.Translate((float)transformed[0], (float)transformed[1]); // calculate the angle. VectorF2D vector = next - current; VectorF2D horizontal = new VectorF2D(1, 0); double angleDegrees = ((Degree)horizontal.Angle(vector)).Value; // Rotate the character transform.Rotate((float)angleDegrees); // Translate the character so the centre of its base is over the origin transform.Translate(0, -characterHeight / 2.5f); //transform.Scale((float)this.FromPixels(_target, _view, 1), // (float)this.FromPixels(_target, _view, 1)); characterPath.Transform(transform); if (haloColor.HasValue && haloRadius.HasValue && haloRadius.Value > 0) { GraphicsPath haloPath = characterPath.Clone() as GraphicsPath; using (haloPath) { haloPath.Widen(new Pen(haloBrush, haloRadius.Value * 2)); // Draw the character target.Target.FillPath(haloBrush, haloPath); } } // Draw the character target.Target.FillPath(brush, characterPath); } current = next; } }
/// <summary> /// Creates a new RectangleF2D from given bounds, center and angle. /// </summary> /// <param name="centerX"></param> /// <param name="centerY"></param> /// <param name="width">Width.</param> /// <param name="height">Height.</param> /// <param name="angleY">The angle.</param> /// <returns></returns> public static RectangleF2D FromBoundsAndCenter(double width, double height, double centerX, double centerY, Degree angleY) { return(RectangleF2D.FromBoundsAndCenter(width, height, centerX, centerY, VectorF2D.FromAngleY(angleY))); }
/// <summary> /// Creates a new RectangleF2D from given bounds, center and direction. /// </summary> /// <param name="centerX"></param> /// <param name="centerY"></param> /// <param name="width">Width.</param> /// <param name="height">Height.</param> /// <param name="directionY">The direction of the y-axis.</param> /// <returns></returns> public static RectangleF2D FromBoundsAndCenter(double width, double height, double centerX, double centerY, VectorF2D directionY) { VectorF2D directionYNormal = directionY.Normalize(); VectorF2D directionXNormal = directionYNormal.Rotate90(true); PointF2D center = new PointF2D(centerX, centerY); PointF2D bottomLeft = center - (directionYNormal * (height / 2)) - (directionXNormal * (width / 2)); return(new RectangleF2D(bottomLeft, width, height, directionY)); }
private void DrawLineTextSegment(Target2DWrapper <CGContextWrapper> target, double[] xTransformed, double[] yTransformed, ushort[] glyphs, int color, int?haloColor, int?haloRadius, double middlePosition, float[] characterWidths, double textLength, float charachterHeight, CTFont font) { // see if text is 'upside down' double averageAngle = 0; double first = middlePosition - (textLength / 2.0); PointF2D current = Polyline2D.PositionAtPosition(xTransformed, yTransformed, first); for (int idx = 0; idx < glyphs.Length; idx++) { double nextPosition = middlePosition - (textLength / 2.0) + ((textLength / (glyphs.Length)) * (idx + 1)); PointF2D next = Polyline2D.PositionAtPosition(xTransformed, yTransformed, nextPosition); // translate to the final position, the center of the line segment between 'current' and 'next'. //PointF2D position = current + ((next - current) / 2.0); // calculate the angle. VectorF2D vector = next - current; VectorF2D horizontal = new VectorF2D(1, 0); double angleDegrees = ((Degree)horizontal.Angle(vector)).Value; averageAngle = averageAngle + angleDegrees; current = next; } averageAngle = averageAngle / glyphs.Length; // revers if 'upside down' double[] xText = xTransformed; double[] yText = yTransformed; if (averageAngle > 90 && averageAngle < 180 + 90) { xText = xTransformed.Reverse().ToArray(); yText = yTransformed.Reverse().ToArray(); } first = middlePosition - (textLength / 2.0); current = Polyline2D.PositionAtPosition(xText, yText, first); double nextPosition2 = first; for (int idx = 0; idx < glyphs.Length; idx++) { nextPosition2 = nextPosition2 + characterWidths[idx]; PointF2D next = Polyline2D.PositionAtPosition(xText, yText, nextPosition2); //ushort currentChar = glyphs [idx]; PointF2D position = current; target.Target.CGContext.SaveState(); // translate to the final position, the center of the line segment between 'current' and 'next'. // double[] transformed = this.Tranform(position[0], position[1]); // target.Target.CGContext.TranslateCTM ( // (float)transformed [0], // (float)transformed [1]); target.Target.CGContext.TranslateCTM((float)position[0], (float)position[1]); // calculate the angle. VectorF2D vector = next - current; VectorF2D horizontal = new VectorF2D(1, 0); double angleDegrees = (horizontal.Angle(vector)).Value; // rotate the character. target.Target.CGContext.RotateCTM((float)angleDegrees); // rotate the text to point down no matter what the map tilt is. //target.Target.CGContext.RotateCTM ((float)_view.Angle.Value); // translate the character so the center of its base is over the origin. target.Target.CGContext.TranslateCTM(0, charachterHeight / 3.0f); // rotate 'upside down' target.Target.CGContext.ConcatCTM(new CGAffineTransform(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f)); target.Target.CGContext.BeginPath(); CGPath path = font.GetPathForGlyph(glyphs[idx]); if (haloRadius.HasValue && haloColor.HasValue) { // also draw the halo. using (CGPath haloPath = path.CopyByStrokingPath( haloRadius.Value * 2, CGLineCap.Round, CGLineJoin.Round, 0)) { SimpleColor haloSimpleColor = SimpleColor.FromArgb(haloColor.Value); target.Target.CGContext.SetFillColor(haloSimpleColor.R / 256.0f, haloSimpleColor.G / 256.0f, haloSimpleColor.B / 256.0f, haloSimpleColor.A / 256.0f); target.Target.CGContext.BeginPath(); target.Target.CGContext.AddPath(haloPath); target.Target.CGContext.DrawPath(CGPathDrawingMode.Fill); } } // set the fill color as the regular text-color. SimpleColor simpleColor = SimpleColor.FromArgb(color); target.Target.CGContext.SetFillColor(simpleColor.R / 256.0f, simpleColor.G / 256.0f, simpleColor.B / 256.0f, simpleColor.A / 256.0f); // draw the text paths. target.Target.CGContext.BeginPath(); target.Target.CGContext.AddPath(path); target.Target.CGContext.DrawPath(CGPathDrawingMode.Fill); // target.Target.CGContext.AddPath (path); // if (haloRadius.HasValue && haloColor.HasValue) { // also draw the halo. // target.Target.CGContext.DrawPath (CGPathDrawingMode.FillStroke); // } else { // target.Target.CGContext.DrawPath (CGPathDrawingMode.Fill); // } //target.Target.CGContext.ClosePath (); target.Target.CGContext.RestoreState(); current = next; } }
/// <summary> /// Draws the line text segment. /// </summary> /// <param name="target">Target.</param> /// <param name="xTransformed">X transformed.</param> /// <param name="yTransformed">Y transformed.</param> /// <param name="text">Text.</param> /// <param name="color">Color.</param> /// <param name="size">Size.</param> /// <param name="haloColor">Halo color.</param> /// <param name="haloRadius">Halo radius.</param> /// <param name="middlePosition">Middle position.</param> /// <param name="characterWidths">Character widths.</param> /// <param name="textLength">Text length.</param> /// <param name="characterHeight">Character height.</param> public void DrawLineTextSegment(Target2DWrapper <global::Android.Graphics.Canvas> target, double[] xTransformed, double[] yTransformed, string text, int color, double size, int?haloColor, int?haloRadius, double middlePosition, float[] characterWidths, double textLength, float characterHeight) { _paint.Color = new global::Android.Graphics.Color(color); _paint.SetStyle(global::Android.Graphics.Paint.Style.Fill); // see if the text is 'upside down'. double averageAngle = 0; double first = middlePosition - (textLength / 2.0); PointF2D current = Polyline2D.PositionAtPosition(xTransformed, yTransformed, first); bool isVisible = false; for (int idx = 0; idx < text.Length; idx++) { double nextPosition = middlePosition - (textLength / 2.0) + ((textLength / (text.Length)) * (idx + 1)); PointF2D next = Polyline2D.PositionAtPosition(xTransformed, yTransformed, nextPosition); // calculate the angle. VectorF2D vector = next - current; VectorF2D horizontal = new VectorF2D(1, 0); double angleDegrees = ((Degree)horizontal.Angle(vector)).Value; averageAngle = averageAngle + angleDegrees; current = next; double[] untransformed = this.TransformReverse(next [0], next [1]); if (_view.Contains(untransformed [0], untransformed [1])) { isVisible = true; } } averageAngle = averageAngle / text.Length; if (!isVisible) { return; } // reverse if 'upside down'. double[] xText = xTransformed; double[] yText = yTransformed; // if (averageAngle > 90 && averageAngle < 180 + 90) // { // the average angle is > PI => means upside down. // for (int idx = 0; idx < (xTransformed.Length / 2) + 1; idx++) { // double other = xTransformed [xTransformed.Length - idx - 1]; // xTransformed [xTransformed.Length - idx - 1] = xTransformed [0]; // xTransformed [0] = other; // other = yTransformed [yTransformed.Length - idx - 1]; // yTransformed [yTransformed.Length - idx - 1] = yTransformed [0]; // yTransformed [0] = other; // } // } // calculate a central position along the line. first = middlePosition - (textLength / 2.0); current = Polyline2D.PositionAtPosition(xText, yText, first); double nextPosition2 = first; for (int idx = 0; idx < text.Length; idx++) { nextPosition2 = nextPosition2 + characterWidths[idx]; //double nextPosition = middle - (textLength / 2.0) + ((textLength / (text.Length)) * (idx + 1)); PointF2D next = Polyline2D.PositionAtPosition(xText, yText, nextPosition2); char currentChar = text[idx]; global::Android.Graphics.Path characterPath = new global::Android.Graphics.Path();; _paint.GetTextPath(text, idx, idx + 1, 0, 0, characterPath); using (characterPath) { // Transformation matrix to move the character to the correct location. // Note that all actions on the Matrix class are prepended, so we apply them in reverse. using (var transform = new Matrix()) { // Translate to the final position, the center of line-segment between 'current' and 'next' PointF2D position = current; //PointF2D position = current + ((next - current) / 2.0); //double[] transformed = this.Tranform(position[0], position[1]); transform.SetTranslate((float)position [0], (float)position [1]); // calculate the angle. VectorF2D vector = next - current; VectorF2D horizontal = new VectorF2D(1, 0); double angleDegrees = ((Degree)horizontal.Angle(vector)).Value; // Rotate the character transform.PreRotate((float)angleDegrees); // Translate the character so the centre of its base is over the origin transform.PreTranslate(0, characterHeight / 2.5f); //transform.Scale((float)this.FromPixels(_target, _view, 1), // (float)this.FromPixels(_target, _view, 1)); characterPath.Transform(transform); if (haloColor.HasValue && haloRadius.HasValue && haloRadius.Value > 0) { _paint.SetStyle(global::Android.Graphics.Paint.Style.FillAndStroke); _paint.StrokeWidth = haloRadius.Value; _paint.Color = new global::Android.Graphics.Color(haloColor.Value); using (global::Android.Graphics.Path haloPath = new global::Android.Graphics.Path()) { _paint.GetFillPath(characterPath, haloPath); // Draw the character target.Target.DrawPath(haloPath, _paint); } } // Draw the character _paint.SetStyle(global::Android.Graphics.Paint.Style.Fill); _paint.StrokeWidth = 0; _paint.Color = new global::Android.Graphics.Color(color); target.Target.DrawPath(characterPath, _paint); } } current = next; } }
/// <summary> /// Calculates and returns the line intersection. /// </summary> /// <param name="line"></param> /// <param name="doSegment"></param> /// <returns></returns> public PrimitiveF2D Intersection(LineF2D line, bool doSegment) { if (line == this) { // if the lines equal, the full lines intersect. return(line); } else if (line.A == this.A && line.B == this.B && line.C == this.C) { // if the lines equal in direction and position; return the smallest segment. KeyValuePair <double, PointF2D> point1 = new KeyValuePair <double, PointF2D>( 0, this.Point1); KeyValuePair <double, PointF2D> point2 = new KeyValuePair <double, PointF2D>( this.Point1.Distance(this.Point2), this.Point2); // sort. KeyValuePair <double, PointF2D> temp; if (point2.Key < point1.Key) { // point2 smaller than point1. temp = point2; point2 = point1; point1 = temp; } KeyValuePair <double, PointF2D> point = new KeyValuePair <double, PointF2D>( this.Point1.Distance(line.Point1), line.Point1); if (point.Key < point2.Key) // sort. { // point smaller than point2. temp = point; point = point2; point2 = temp; } if (point2.Key < point1.Key) { // point2 smaller than point1. temp = point2; point2 = point1; point1 = temp; } point = new KeyValuePair <double, PointF2D>( this.Point1.Distance(line.Point2), line.Point2); if (point.Key < point2.Key) // sort. { // point smaller than point2. temp = point; point = point2; point2 = temp; } if (point2.Key < point1.Key) { // point2 smaller than point1. temp = point2; point2 = point1; point1 = temp; } // this line is a segment. if (doSegment && (this.IsSegment1 || this.IsSegment2)) { // test where the intersection lies. double this_distance = this.Point1.Distance(this.Point2); if (this.IsSegment) { // if in any directions one of the points are further away from the point. double this_distance_1 = System.Math.Min(this.Point1.Distance(point1.Value), this.Point1.Distance(point2.Value)); if (this_distance_1 > this_distance) { // the point is further away. return(null); } } else if (this.IsSegment1) { // if in any directions one of the points are further away from the point. double this_distance_1 = this.Point1.Distance(point1.Value); if (this_distance_1 > this_distance) { // the point is further away. return(null); } } else if (this.IsSegment2) { // if in any directions one of the points are further away from the point. double this_distance_2 = this.Point2.Distance(point1.Value); if (this_distance_2 > this_distance) { // the point is further away. return(null); } } } // other line is a segment. if (doSegment && (line.IsSegment1 || line.IsSegment2)) { // test where the intersection lies. double this_distance = line.Point1.Distance(line.Point2); if (line.IsSegment) {// if in any directions one of the points are further away from the point. double this_distance_1 = System.Math.Min(line.Point1.Distance(point1.Value), line.Point1.Distance(point2.Value)); if (this_distance_1 > this_distance) { // the point is further away. return(null); } } else if (line.IsSegment1) { // if in any directions one of the points are further away from the point. double this_distance_1 = line.Point1.Distance(point1.Value); if (this_distance_1 > this_distance) { // the point is further away. return(null); } } else if (line.IsSegment2) { // if in any directions one of the points are further away from the point. double this_distance_2 = line.Point2.Distance(point2.Value); if (this_distance_2 > this_distance) { // the point is further away. return(null); } } } return(new LineF2D( point1.Value, point2.Value, true)); } else { // line.A = A1, line.B = B1, line.C = C1, this.A = A2, this.B = B2, this.C = C2 double det = (line.A * this.B - this.A * line.B); if (det == 0) // TODO: implement an accuracy threshold epsilon. { // lines are parallel; no intersections. return(null); } else { // lines are not the same and not parallel so they will intersect. double x = (this.B * line.C - line.B * this.C) / det; double y = (line.A * this.C - this.A * line.C) / det; // create the point. PointF2D point = new PointF2D(new double[] { x, y }); // this line is a segment. if (doSegment && (this.IsSegment1 || this.IsSegment2)) { // test where the intersection lies. double this_distance = this.Point1.Distance(this.Point2); if (this.IsSegment) {// if in any directions one of the points are further away from the point. double this_distance_1 = this.Point1.Distance(point); if (this_distance_1 > this_distance) { // the point is further away. return(null); } double this_distance_2 = this.Point2.Distance(point); if (this_distance_2 > this_distance) { // the point is further away. return(null); } } else if (this.IsSegment1 && this.Point2.Distance(point) > this_distance) { // only check this direction and only if the points lies far enough away from point2. VectorF2D pointDirection = point - this.Point2; VectorF2D point2ToPoint1 = this.Direction.Inverse; if (pointDirection.CompareNormalized(point2ToPoint1, 0.001)) { // point lies in the direction of the segmented point1 and far away from point2 return(null); } } else if (this.IsSegment1 && this.Point1.Distance(point) > this_distance) { // only check this direction and only if the points lies far enough away from point1. VectorF2D pointDirection = point - this.Point1; VectorF2D point1ToPoint2 = this.Direction; if (pointDirection.CompareNormalized(point1ToPoint2, 0.001)) { // point lies in the direction of the segmented point1 and far away from point2 return(null); } } } // line this is a segment. if (doSegment && (line.IsSegment1 || line.IsSegment2)) { // test where the intersection lies. double line_distance = line.Point1.Distance(line.Point2); if (line.IsSegment) {// if in any directions one of the points are further away from the point. double line_distance_1 = line.Point1.Distance(point); if (line_distance_1 > line_distance) { // the point is further away. return(null); } double line_distance_2 = line.Point2.Distance(point); if (line_distance_2 > line_distance) { // the point is further away. return(null); } } else if (line.IsSegment1 && line.Point2.Distance(point) > line_distance) { // only check line direction and only if the points lies far enough away from point2. VectorF2D pointDirection = point - line.Point2; VectorF2D point2ToPoint1 = line.Direction.Inverse; if (pointDirection.CompareNormalized(point2ToPoint1, 0.001)) { // point lies in the direction of the segmented point1 and far away from point2 return(null); } } else if (line.IsSegment1 && line.Point1.Distance(point) > line_distance) { // only check line direction and only if the points lies far enough away from point1. VectorF2D pointDirection = point - line.Point1; VectorF2D point1ToPoint2 = line.Direction; if (pointDirection.CompareNormalized(point1ToPoint2, 0.001)) { // point lies in the direction of the segmented point1 and far away from point2 return(null); } } } // the intersection is valid. return(point); } } }
/// <summary> /// Calculates a polygon out of a list of points. The resulting polygon the convex hull of all points. /// </summary> /// <param name="points"></param> /// <returns></returns> public static IList <PointF2D> Calculate(IList <PointF2D> points) { if (points.Count < 3) { throw new ArgumentOutOfRangeException(string.Format("Cannot calculate the convex hull of {0} points!", points.Count)); } // find the 'left-most' and 'top-most' point. PointF2D start = points[0]; foreach (PointF2D point in points) { if (start[0] > point[0]) { start = point; } else if (start[0] == point[0]) { if (start[1] < point[01]) { start = point; } } } // produce the first reference vector. double[] before_start_coords = new double[] { start[0], start[1] - 10 }; PointF2D before_start = new PointF2D(before_start_coords); VectorF2D reference = start - before_start; // start the gift-wrapping! List <PointF2D> result = new List <PointF2D>(); PointF2D current = start; result.Add(current); do { // find the point with the smallest angle. double angle = double.MaxValue; PointF2D next = null; foreach (PointF2D point in points) { if (point != current) { VectorF2D next_vector = point - current; double next_angle = reference.Angle(next_vector).Value; if (next_angle < angle) { angle = next_angle; next = point; } } } // the found point is the next one. reference = next - current; current = next; result.Add(current); }while(current != start); // return the result. return(result); }
public static RectangleF2D FromBoundsAndCenter(double width, double height, double centerX, double centerY, VectorF2D directionY) { VectorF2D vectorF2D1 = directionY.Normalize(); VectorF2D vectorF2D2 = vectorF2D1.Rotate90(true); return(new RectangleF2D(new PointF2D(centerX, centerY) - vectorF2D1 * (height / 2.0) - vectorF2D2 * (width / 2.0), width, height, directionY)); }
/// <summary> /// Creates a new line. /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="is_segment1"></param> /// <param name="is_segment2"></param> public LineF2D(PointF2D a, PointF2D b, bool is_segment1, bool is_segment2) { _a = a; _b = b; _dir = _b - _a; _is_segment1 = is_segment1; _is_segment2 = is_segment2; }
private void DrawLineTextSegment(Target2DWrapper <CGContextWrapper> target, double[] x, double[] y, ushort[] glyphs, int color, int?haloColor, int?haloRadius, double middlePosition, float[] characterWidths, double textLength, float charachterHeight, CTFont font) { // see if text is 'upside down' double averageAngle = 0; double first = middlePosition - (textLength / 2.0); PointF2D current = Polyline2D.PositionAtPosition(x, y, first); for (int idx = 0; idx < glyphs.Length; idx++) { double nextPosition = middlePosition - (textLength / 2.0) + ((textLength / (glyphs.Length)) * (idx + 1)); PointF2D next = Polyline2D.PositionAtPosition(x, y, nextPosition); // translate to the final position, the center of the line segment between 'current' and 'next'. //PointF2D position = current + ((next - current) / 2.0); // calculate the angle. VectorF2D vector = next - current; VectorF2D horizontal = new VectorF2D(1, 0); double angleDegrees = ((Degree)horizontal.Angle(vector)).Value; averageAngle = averageAngle + angleDegrees; current = next; } averageAngle = averageAngle / glyphs.Length; // revers if 'upside down' double[] xText = x; double[] yText = y; if (averageAngle > 90 && averageAngle < 180 + 90) { xText = x.Reverse().ToArray(); yText = y.Reverse().ToArray(); } first = middlePosition - (textLength / 2.0); current = Polyline2D.PositionAtPosition(xText, yText, first); //target.Target.CGContext.SaveState (); //target.Target.CGContext.TranslateCTM (xText[0], yText[0]); double nextPosition2 = first; for (int idx = 0; idx < glyphs.Length; idx++) { nextPosition2 = nextPosition2 + characterWidths [idx]; PointF2D next = Polyline2D.PositionAtPosition(xText, yText, nextPosition2); //ushort currentChar = glyphs [idx]; PointF2D position = current; target.Target.CGContext.SaveState(); // translate to the final position, the center of the line segment between 'current' and 'next'. double[] transformed = this.Tranform(position[0], position[1]); target.Target.CGContext.TranslateCTM( (float)transformed [0], (float)transformed [1]); // calculate the angle. VectorF2D vector = next - current; VectorF2D horizontal = new VectorF2D(1, 0); double angleDegrees = (horizontal.Angle(vector)).Value; // rotate the character. target.Target.CGContext.RotateCTM((float)angleDegrees); // // // translate the character so the center of its base is over the origin. target.Target.CGContext.TranslateCTM(0, charachterHeight / 3.0f); // rotate 'upside down' target.Target.CGContext.ConcatCTM(new CGAffineTransform(1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f)); target.Target.CGContext.BeginPath(); CGPath path = font.GetPathForGlyph(glyphs [idx]); target.Target.CGContext.AddPath(path); if (haloRadius.HasValue && haloColor.HasValue) // also draw the halo. { target.Target.CGContext.DrawPath(CGPathDrawingMode.FillStroke); } else { target.Target.CGContext.DrawPath(CGPathDrawingMode.Fill); } //target.Target.CGContext.ClosePath (); target.Target.CGContext.RestoreState(); current = next; } }