/// <summary>Returns resulting point if this point is rotated around <paramref name='reference'/> by <paramref name='degrees'/>.</summary> /// <param name='reference'>The center of the rotation.</param> /// <param name='degrees'>Positive values means a counter-clockwise rotation.</param> public WPoint Rotate(WPoint reference, double degrees) { WPoint a = this - reference; WPoint b = a.RotateAroundOrigin(degrees); return(b + reference); }
/// <summary></summary> public WRectangle(double width, double height, WPoint corner, double rotation) { Width = width; Height = height; Corner = corner; Rotation = rotation; }
/// <summary>Finds the intersection points between the edge of this circle and circle <paramref name='b'/>.</summary> /// <returns>Null (no intersection), an array of length 1, or an array of length 2.</returns> public WPoint[] GetIntersectionPoints(WCircle b) { //following the method in math/intersectionCircleCircle.png WCircle a = this; double d = a.Center.Distance(b.Center); //distance between centers if (d > a.Radius + b.Radius) { return(null); //circles too far apart to intersect } if (a.ContainsOrIsContained(b)) { return(null); //one circle is wholly inside the other } //the radical line is the line between the two intersecting points of the circles //Point c is the center of the radical line, which is also on the line between the centers double dA = (Math.Pow(a.Radius, 2) - Math.Pow(b.Radius, 2) + Math.Pow(d, 2)) / (2 * d); //distance from centerA to pointC if (dA == a.Radius) { return(new WPoint[] { Geometry.PointOnLine(a.Center, b.Center, a.Radius) }); //circles intersect at single point } WPoint c = a.Center + dA * (b.Center - a.Center) / d; //h is the distance from pointC to either intersection point (the hypotenus of triangle centerA-C-intersection) double h = Math.Sqrt(Math.Pow(a.Radius, 2) - Math.Pow(dA, 2)); return(new WPoint[] { new WPoint(c.X + (h * (b.Y - a.Y) / d), c.Y - h * (b.X - a.X) / d), new WPoint(c.X - (h * (b.Y - a.Y) / d), c.Y + h * (b.X - a.X) / d) }); }
/// <summary></summary> public Intersection(WPoint point) { Type = IntersectionType.Point; this.points = new List <WPoint>() { point }; }
/// <exception cref='ArgumentException'>Points A and B cannot be the same.</exception> public WLine(WPoint a, WPoint b, bool isDirected) { if (a == b) { throw new ArgumentException("Points A and B cannot be the same."); } A = a; B = b; IsDirected = isDirected; }
/// <returns>Null (no intercepts), or array of length 1 or 2.</returns> public WPoint[] GetIntersectionPoints(WLine line) { //line does not intersect if perpendicular line from circle-center to line is longer than circle-radius WPoint perpendicularToCenter = line.GetPerpendicularIntersect(Center); if (perpendicularToCenter.Distance(Center) > Radius) { return(null); } //equation of circle with radius r and center (h, k) //is (x - h)^2 + (y - k)^2 = r^2 //line: y = mx + b //circle: (x - h)^2 + (y - k)^2 = r^2 //substitute y: (x - h)^2 + (mx + b - k)^2 = r^2 //expand: x^2 - 2hx + h^2 + m^2x^2 + 2(b - k)mx + (b - k)^2 - r^2 = 0 //group: (1 + m^2)x^2 + (-2h + 2(b - k)m)x + (h^2 + (b - k)^2 - r^2) = 0 //quadratic equation: if 0 = Ax^2 + Bx + C, then x = (-B +- sqrt(B^2 - 4AC)) / 2A double A = 1 + Math.Pow(line.Slope, 2); double B = (-2 * Center.X) + (2 * (line.YIntercept - Center.Y) * line.Slope); double C = Math.Pow(Center.X, 2) + Math.Pow(line.YIntercept - Center.Y, 2) - Math.Pow(Radius, 2); double x1 = (-1 * B + Math.Sqrt(Math.Pow(B, 2) - (4 * A * C))) / (2 * A); double x2 = (-1 * B - Math.Sqrt(Math.Pow(B, 2) - (4 * A * C))) / (2 * A); double y1 = line.Slope * x1 + line.YIntercept; double y2 = line.Slope * x2 + line.YIntercept; if (line.IsVertical) { x1 = line.A.X; x2 = line.A.X; //must use circle equation instead of line equation to find y's y1 = Center.Y + Math.Sqrt(Math.Pow(Radius, 2) - Math.Pow(x1 - Center.X, 2)); y2 = Center.Y - Math.Sqrt(Math.Pow(Radius, 2) - Math.Pow(x1 - Center.X, 2)); } if (line.IsHorizontal) { y1 = line.A.Y; y2 = line.A.Y; } WPoint point1 = new WPoint(x1, y1); WPoint point2 = new WPoint(x2, y2); List <WPoint> result = new List <WPoint>() { point1 }; if (point1 != point2) { result.Add(point2); } return(result.ToArray()); }
/// <summary> /// Returns true if this wedge contains point <paramref name='b'/>, including if <paramref name='b'/> lies on one of this wedge's edges. /// </summary> public bool Contains(WPoint b) { double distance = Circle.Center.Distance(b); if (distance > Circle.Radius) { return(false); } double degrees = Circle.DegreesAtPoint(b); return(Degrees.Overlaps(degrees)); }
/// <summary> /// Find the two tangent points on the circle that form lines to point <paramref name='b'/>. /// </summary> /// <returns>Array of length 2.</returns> public WPoint[] GetTangentPoints(WPoint b) { //point C and D are the tangents WCircle a = this; double distanceAB = a.Center.Distance(b); double degreesAB = DegreesAtPoint(b); double degreesAB_AC = Math.Cos(Radius / distanceAB); WPoint c = PointAtDegrees(degreesAB + degreesAB_AC); WPoint d = PointAtDegrees(degreesAB - degreesAB_AC); return(new WPoint[] { c, d }); }
/// <summary>Returns true if point <paramref name='c'/> lies on this line.</summary> public virtual bool Overlaps(WPoint c) { if (IsVertical) { return(Geometry.WithinMarginOfError(c.X, A.X)); } if (!Geometry.WithinMarginOfError(c.Y, (Slope * c.X) + YIntercept)) { return(false); } return(true); }
//todo: better names for PointOnLine and PointPastLine, they are misleading /// <summary> /// Calculates point along line AB, starting at A and moving towards B /// </summary> /// <exception cref='ArgumentException'>Point A and B cannot be the same.</exception> public static WPoint PointOnLine(WPoint a, WPoint b, double distance) { double lineLength = a.Distance(b); if (lineLength == 0) { throw new ArgumentException("Point A and B cannot be the same."); } double lengthRatio = distance / lineLength; double x = ((1d - lengthRatio) * a.X) + (lengthRatio * b.X); double y = ((1d - lengthRatio) * a.Y) + (lengthRatio * b.Y); return(new WPoint(x, y)); }
/// <summary>Returns true if point <paramref name='c'/> lies on this line segment.</summary> public override bool Overlaps(WPoint c) { if (IsVertical) { return(Geometry.WithinMarginOfError(c.X, A.X)); } if (!Geometry.WithinMarginOfError(c.Y, (Slope * c.X) + YIntercept)) { return(false); } return(c.X >= Math.Min(A.X, B.X) && c.X <= Math.Max(A.X, B.X) && c.Y >= Math.Min(A.Y, B.Y) && c.Y <= Math.Max(A.Y, B.Y)); }
//todo: couldn't LineDirection be moved to Line object? probably PointOnLine and PointPastLine could to. /// <summary> /// Given directed line A to B, what direction is it pointing? /// </summary> /// <remarks> /// North, South, East, and West answers are exact. So "North" means exactly North. /// The inbetween directions cover all remaining values. So "NorthWest" covers all values between North and West. /// </remarks> /// <exception cref='NotImplementedException'>Coordinate plane not supported.</exception> public static Direction LineDirection(WPoint a, WPoint b) { if (a == b) { return(Direction.None); } switch (CoordinatePlane) { case CoordinatePlanes.Screen: return(LineDirection_Screen(a, b)); case CoordinatePlanes.Paper: return(LineDirection_Paper(a, b)); default: throw new NotImplementedException("Coordinate plane not supported."); } }
//todo: verify that all line operations take vertical and horizontal lines into account /// <summary> /// Returns the point where a perpendicular line passing through point <paramref name='c'/> intersects this line. /// </summary> public WPoint GetPerpendicularIntersect(WPoint c) { if (IsVertical) { return(new WPoint(this.A.X, c.Y)); } if (IsHorizontal) { return(new WPoint(c.X, this.A.Y)); } double cSlope = PerpendicularSlope; double cYIntercept = c.Y - (cSlope * c.X); double x = (cYIntercept - this.YIntercept) / (this.Slope - cSlope); double y = (this.Slope * x) + this.YIntercept; return(new WPoint(x, y)); }
private static Direction LineDirection_Paper(WPoint a, WPoint b) { if (a.X == b.X) { return((a.Y < b.Y) ? Direction.North : Direction.South); } if (a.Y == b.Y) { return((a.X < b.X) ? Direction.East : Direction.West); } if (a.X < b.X) { return((a.Y < b.Y) ? Direction.NorthEast : Direction.SouthEast); } else { return((a.Y < b.Y) ? Direction.NorthWest : Direction.SouthWest); } }
/// <summary> /// Given a line from the center of this circle to a point (<paramref name='lineEnd'/>), what degrees is the line angle at? /// 0 degrees is East of center, increases clockwise. /// </summary> public double DegreesAtPoint(WPoint lineEnd) { if (Geometry.CoordinatePlane == Geometry.CoordinatePlanes.None) { throw new ArgumentException("Coordinate plane required."); } Geometry.Direction direction = Geometry.LineDirection(Center, lineEnd); switch (direction) { case Geometry.Direction.East: return(0); case Geometry.Direction.South: return(90); case Geometry.Direction.West: return(180); case Geometry.Direction.North: return(270); } double lineLength = Center.Distance(lineEnd); double radians = Math.Abs(Math.Asin((lineEnd.Y - Center.Y) / lineLength)); double degrees = Shapes.WCircle.RadiansToDegrees(radians) % DEGREES_IN_CIRCLE; switch (direction) { case Geometry.Direction.SouthEast: return(degrees); case Geometry.Direction.SouthWest: return(DEGREES_IN_HALF_CIRCLE - degrees); case Geometry.Direction.NorthWest: return(DEGREES_IN_HALF_CIRCLE + degrees); case Geometry.Direction.NorthEast: return(DEGREES_IN_CIRCLE - degrees); } throw new NotImplementedException(String.Format("Direction not supported: {0}.", direction)); }
/// <summary></summary> public WWedgeUnbound(WPoint center, WRangeCircular degreeRange) { Center = center; Degrees = degreeRange; }
/// <summary> /// Returns true if point <paramref name='b'/> lies within or on this circle. /// </summary> public bool Contains(WPoint b) { return(Center.Distance(b) <= Radius); }
/// <summary>Generates a vertical line through the specified point.</summary> public static WLine Vertical(WPoint point) { return(new WLine(point, new WPoint(point.X, point.Y + 1))); }
/// <summary> /// Returns the distance between this point and point <paramref name='b'/>. Always positive. /// </summary> public double Distance(WPoint b) { return(Math.Sqrt(Math.Pow(b.X - this.X, 2) + Math.Pow(b.Y - this.Y, 2))); }
/// <summary>Returns intersection between a line segment and another line segment.</summary> public override Intersection GetIntersection(WLineSegment that) { if (this.Parallel(that)) { bool thisAOnThat = this.A.Overlaps(that); bool thisBOnThat = this.B.Overlaps(that); bool thatAOnThis = that.A.Overlaps(this); bool thatBOnThis = that.B.Overlaps(this); if (!thisAOnThat && !thisBOnThat && !thatAOnThis && !thatBOnThis) { return(Intersection.NONE); } if (thisAOnThat && thisBOnThat) { return(new Intersection(this)); } if (thatAOnThis && thatBOnThis) { return(new Intersection(that)); } WPoint overlapThis = null; WPoint overlapThat = null; if (thisAOnThat) { overlapThis = this.A; } else if (thisBOnThat) { overlapThis = this.B; } if (thatAOnThis) { overlapThat = that.A; } else if (thatBOnThis) { overlapThat = that.B; } if (overlapThis == null || overlapThat == null) { //should not reach this, but just in case return(Intersection.NONE); } if (overlapThis == overlapThat) { return(new Intersection(overlapThis)); } return(new Intersection(new WLineSegment(overlapThis, overlapThat))); } Intersection possibleIntersection = (this.ToWLine()).GetIntersection(that.ToWLine()); if (possibleIntersection.IsPoint && this.Overlaps(possibleIntersection.Point) && that.Overlaps(possibleIntersection.Point)) { return(possibleIntersection); } return(Intersection.NONE); }
/// <summary></summary> public WCircle(WPoint center, double radius) { X = center.X; Y = center.Y; Radius = radius; }
/// <summary></summary> public WLineSegment(WPoint a, WPoint b, bool isDirected) : base(a, b, isDirected) { }
/// <summary></summary> public WLineSegment(WPoint a, WPoint b) : base(a, b) { }
/// <summary></summary> public WWedgeUnbound(WPoint center, double degreesRangeStart, double degreesRangeEnd) { Center = center; Degrees = new WRangeCircular(degreesRangeStart, degreesRangeEnd, WCircle.DEGREES_IN_CIRCLE); }
/// <summary>Rotation defaults to 0 degrees.</summary> public WRectangle(double width, double height, WPoint corner) : this(width, height, corner, 0) { }
/// <summary> /// Calculates point along line AB, starting at B and moving away from A /// </summary> public static WPoint PointPastLine(WPoint a, WPoint b, double distance) { double lineLength = a.Distance(b); return(PointOnLine(a, b, lineLength + distance)); }