/// <summary> /// Inflates balloon body and target point /// </summary> public static void InflateBalloon(ref Rectangle body, ref Point target, int deltaBody, int deltaTarget) { Point center = new Point(body.Left + body.Width / 2, body.Top + body.Height / 2); PolarPoint pp = new PolarPoint(center, target); body.Inflate(deltaBody, deltaBody); pp.R += deltaTarget; target = pp.Point; target.Offset(center.X, center.Y); }
/// <summary> /// Calculates callout balloon vertexes suitable for curve drawing /// </summary> /// <param name="body">Balloon body coordinates</param> /// <param name="target">A point of balloon leg attachment</param> /// <param name="legSweep">Length of balloon leg attachment breach at balloon body edge, expressed in radians (arc length). A value such as PI/16 yields good results</param> /// <returns>An array of vertex points</returns> public static Point[] VectorizeBalloon(Rectangle body, Point target, double legSweep) { List <Point> result = new List <Point>(); Point center = new Point(body.Left + body.Width / 2, body.Top + body.Height / 2); PolarPoint trg = new PolarPoint(center, target); Point legStart = CartesianUtils.FindRayFromRectangleCenterSideIntersection( body, CartesianUtils.WrapAngle(trg.Theta, -legSweep / 2)); Point legEnd = CartesianUtils.FindRayFromRectangleCenterSideIntersection( body, CartesianUtils.WrapAngle(trg.Theta, +legSweep / 2)); result.Add(new Point(body.Left, body.Top)); result.Add(new Point(body.Right, body.Top)); result.Add(new Point(body.Right, body.Bottom)); result.Add(new Point(body.Left, body.Bottom)); result.Add(legStart); result.Add(target); result.Add(legEnd); //reorder points by azimuth so the curve can close and look like a balloon result.Sort((p1, p2) => { PolarPoint pp1 = new PolarPoint(center, p1); PolarPoint pp2 = new PolarPoint(center, p2); if (pp1.Theta > pp2.Theta) { return(-1); } else { return(+1); } }); return(result.ToArray()); }
/// <summary> /// Returns a point of intersection between a ray cast from the center of a rectangle /// under certain polar coordinate angle and a rectangle side /// </summary> public static Point FindRayFromRectangleCenterSideIntersection(Rectangle rect, double theta) { Point center = new Point(rect.Left + rect.Width / 2, rect.Top + rect.Height / 2); double rayLength = rect.Width > rect.Height ? rect.Width : rect.Height; //make ray "infinite" in comparison to rect if (rayLength < 100) { rayLength = 100; //safeguard } PolarPoint rayEnd = new PolarPoint(rayLength, theta); //create ray "end" point double k = (rayEnd.Point.X != 0)? ((double)rayEnd.Point.Y) / ((double)rayEnd.Point.X) : 0; //get line incline aka. y = kx int x, y; List <Point> lst = new List <Point>(); //north x = center.X + ((k != 0) ? (int)((rect.Top - center.Y) / k) : 0); y = rect.Top; if ((x >= rect.Left) && (x <= rect.Right)) { lst.Add(new Point(x, y)); } //south x = center.X + ((k != 0) ? (int)((rect.Bottom - center.Y) / k) : 0); y = rect.Bottom; if ((x >= rect.Left) && (x <= rect.Right)) { lst.Add(new Point(x, y)); } //east x = rect.Right; y = center.Y + (int)(k * (rect.Right - center.X)); if ((y >= rect.Top) && (y <= rect.Bottom)) { lst.Add(new Point(x, y)); } //west x = rect.Left; y = center.Y + (int)(k * (rect.Left - center.X)); if ((y >= rect.Top) && (y <= rect.Bottom)) { lst.Add(new Point(x, y)); } Point minPoint = new Point(int.MaxValue, int.MaxValue); int minDistance = int.MaxValue; Point re = rayEnd.Point; //rayEnd is relative to absolute 0,0 re.Offset(center.X, center.Y); // need to make relative to rectangle center foreach (Point p in lst) //find closest point { int dst = Distance(p, re); if (dst < minDistance) { minPoint = p; minDistance = dst; } } return(minPoint); }