// Add a new gap an an array of gaps. The resulting array is simplified. The original array may be null. public static CircleGap[] AddGap(CircleGap[] original, float startAngle, float stopAngle) { CircleGap newGap; CircleGap[] newGaps; // Create the new gap. newGap = new CircleGap(startAngle, stopAngle); // Add to the old gaps. if (original == null) { newGaps = new CircleGap[1] { newGap } } ; else { newGaps = new CircleGap[original.Length + 1]; Array.Copy(original, newGaps, original.Length); newGaps[original.Length] = newGap; } // Simplify return(SimplifyGaps(newGaps)); }
// Add a gap defined by two points. The gap is assumed to be <=180 degrees. public static CircleGap[] AddGap(PointF center, CircleGap[] original, PointF pt1, PointF pt2) { if (center == pt1 || center == pt2) { return(original); } float angle1 = Geometry.Angle(center, pt1); float angle2 = Geometry.Angle(center, pt2); CircleGap.OrderGapAngles(ref angle1, ref angle2); return(AddGap(original, angle1, angle2)); }
public override void LeftButtonDrag(Pane pane, PointF location, PointF locationStart, float pixelSize, ref bool displayUpdateNeeded) { Debug.Assert(pane == Pane.Map); // Get the new set of gaps. CircleGap[] newGaps = CircleGap.AddGap(courseObjStart.location, courseObjStart.gaps, startDrag, location); CircleGap[] newMovableGaps = CircleGap.AddGap(courseObjStart.location, courseObjStart.movableGaps, startDrag, location); // Put the new gaps into the highlight. courseObjDrag = (PointCourseObj)courseObjStart.Clone(); courseObjDrag.gaps = newGaps; courseObjDrag.movableGaps = newMovableGaps; displayUpdateNeeded = true; }
// Add a new gap an an array of gaps. The resulting array is simplified. The original array may be null. public static CircleGap[] AddGap(CircleGap[] original, float startAngle, float stopAngle) { CircleGap newGap; CircleGap[] newGaps; // Create the new gap. newGap = new CircleGap(startAngle, stopAngle); // Add to the old gaps. if (original == null) newGaps = new CircleGap[1] { newGap }; else { newGaps = new CircleGap[original.Length + 1]; Array.Copy(original, newGaps, original.Length); newGaps[original.Length] = newGap; } // Simplify return SimplifyGaps(newGaps); }
// Move a gap start/stop point to a new location. Return the new gap array. The gap array is NOT simplified. public static CircleGap[] MoveStartStopPoint(PointF center, float radius, CircleGap[] gaps, PointF oldPt, PointF newPt) { CircleGap[] newGaps = (CircleGap[])gaps.Clone(); if (newPt != center) { for (int i = 0; i < newGaps.Length; ++i) { PointF startPt = Geometry.MoveDistance(center, radius, gaps[i].startAngle); PointF stopPt = Geometry.MoveDistance(center, radius, gaps[i].stopAngle); float newStart, newStop; if (Geometry.Distance(startPt, oldPt) < 0.01) { // Moving start point of the gap. newStart = Geometry.Angle(center, newPt); newStop = gaps[i].stopAngle; } else if (Geometry.Distance(stopPt, oldPt) < 0.01) { // Moving end point of the gap. newStart = gaps[i].startAngle; newStop = Geometry.Angle(center, newPt); } else { continue; } if (OrderGapAngles(ref newStart, ref newStop)) { float t = newStart; newStart = newStop; newStop = t; } newGaps[i] = new CircleGap(newStart, newStop); break; } } return(newGaps); }
// Add a gap defined by two points. The gap is assumed to be <=180 degrees. public static CircleGap[] AddGap(PointF center, CircleGap[] original, PointF pt1, PointF pt2) { if (center == pt1 || center == pt2) return original; float angle1 = Geometry.Angle(center, pt1); float angle2 = Geometry.Angle(center, pt2); CircleGap.OrderGapAngles(ref angle1, ref angle2); return AddGap(original, angle1, angle2); }
// Return start and stop points of the gaps, as used by MapModel. public static float[] StartsAndStops(CircleGap[] gaps) { if (gaps == null || gaps.Length == 0) return null; float[] arcs = new float[gaps.Length * 2]; for (int i = 0; i < gaps.Length; i += 1) { arcs[i * 2] = gaps[i].startAngle; arcs[i * 2 + 1] = gaps[i].stopAngle; } return arcs; }
// Simplify a gap array. Sorts in order, combines overlapping gaps, and removes gaps before and beyond end of length. public static CircleGap[] SimplifyGaps(CircleGap[] gaps) { if (gaps == null || gaps.Length == 0) { return(null); } List <CircleGap> newGaps = new List <CircleGap>(); // Remove gaps that have start before end. // Simplify to end angle between 0 and 360, startAngle between -360 and end angle. int i; for (i = 0; i < gaps.Length; ++i) { if (gaps[i].stopAngle <= gaps[i].startAngle) { continue; } float stop = gaps[i].stopAngle % 360.0F; if (stop < 0) { stop += 360.0F; } float start = gaps[i].startAngle % 360.0F; if (start < 0) { start += 360.0F; } if (start > stop) { start -= 360.0F; } newGaps.Add(new CircleGap(start, stop)); } // Sort gaps by their start angle. newGaps.Sort(delegate(CircleGap gap1, CircleGap gap2) { if (gap1.startAngle < gap2.startAngle) { return(-1); } else if (gap1.startAngle > gap2.startAngle) { return(1); } else { return(0); } }); // Combine gaps that overlap. i = 0; while (i < newGaps.Count - 1) { if (newGaps[i].stopAngle >= newGaps[i + 1].startAngle) { if (newGaps[i].stopAngle < newGaps[i + 1].stopAngle) { newGaps[i] = new CircleGap(newGaps[i].startAngle, newGaps[i + 1].stopAngle); } newGaps.RemoveAt(i + 1); } else { ++i; } } // Last and first can overlap while (newGaps.Count >= 2 && newGaps[0].startAngle < newGaps[newGaps.Count - 1].stopAngle - 360.0F) { newGaps[0] = new CircleGap(newGaps[newGaps.Count - 1].startAngle - 360.0F, newGaps[0].stopAngle); newGaps.RemoveAt(newGaps.Count - 1); } // And we're done. return((newGaps.Count > 0) ? newGaps.ToArray() : null); }
// Remove a gap (if any) from an array of gaps. public static CircleGap[] RemoveGap(CircleGap[] original, float angle) { angle = angle % 360F; if (angle < 0) angle += 360F; if (original == null) return null; List<CircleGap> newgaps = new List<CircleGap>(SimplifyGaps(original)); for (int i = 0; i < newgaps.Count; ++i) { if ((newgaps[i].startAngle <= angle && newgaps[i].stopAngle >= angle) || (newgaps[i].startAngle <= angle-360F && newgaps[i].stopAngle >= angle-360F)) newgaps.RemoveAt(i); } if (newgaps.Count == 0) return null; else return newgaps.ToArray(); }
// Simplify a gap array. Sorts in order, combines overlapping gaps, and removes gaps before and beyond end of length. public static CircleGap[] SimplifyGaps(CircleGap[] gaps) { if (gaps == null || gaps.Length == 0) return null; List<CircleGap> newGaps = new List<CircleGap>(); // Remove gaps that have start before end. // Simplify to end angle between 0 and 360, startAngle between -360 and end angle. int i; for (i = 0; i < gaps.Length; ++i) { if (gaps[i].stopAngle <= gaps[i].startAngle) continue; float stop = gaps[i].stopAngle % 360.0F; if (stop < 0) stop += 360.0F; float start = gaps[i].startAngle % 360.0F; if (start < 0) start += 360.0F; if (start > stop) start -= 360.0F; newGaps.Add(new CircleGap(start, stop)); } // Sort gaps by their start angle. newGaps.Sort(delegate(CircleGap gap1, CircleGap gap2) { if (gap1.startAngle < gap2.startAngle) return -1; else if (gap1.startAngle > gap2.startAngle) return 1; else return 0; }); // Combine gaps that overlap. i = 0; while (i < newGaps.Count - 1) { if (newGaps[i].stopAngle >= newGaps[i + 1].startAngle) { if (newGaps[i].stopAngle < newGaps[i + 1].stopAngle) newGaps[i] = new CircleGap(newGaps[i].startAngle, newGaps[i + 1].stopAngle); newGaps.RemoveAt(i + 1); } else ++i; } // Last and first can overlap while (newGaps.Count >= 2 && newGaps[0].startAngle < newGaps[newGaps.Count - 1].stopAngle - 360.0F) { newGaps[0] = new CircleGap(newGaps[newGaps.Count - 1].startAngle - 360.0F, newGaps[0].stopAngle); newGaps.RemoveAt(newGaps.Count - 1); } // And we're done. return (newGaps.Count > 0) ? newGaps.ToArray() : null; }
// Get the start/stop points of the gaps. Primarily useful for finding where the handles should be. public static PointF[] GapStartStopPoints(PointF center, float radius, CircleGap[] gaps) { if (gaps == null || gaps.Length == 0) return null; PointF[] pts = new PointF[gaps.Length * 2]; for (int i = 0; i < gaps.Length; ++i) { pts[i * 2] = Geometry.MoveDistance(center, radius, gaps[i].startAngle); pts[i * 2 + 1] = Geometry.MoveDistance(center, radius, gaps[i].stopAngle); } return pts; }
// Move a gap start/stop point to a new location. Return the new gap array. The gap array is NOT simplified. public static CircleGap[] MoveStartStopPoint(PointF center, float radius, CircleGap[] gaps, PointF oldPt, PointF newPt) { CircleGap[] newGaps = (CircleGap[])gaps.Clone(); if (newPt != center) { for (int i = 0; i < newGaps.Length; ++i) { PointF startPt = Geometry.MoveDistance(center, radius, gaps[i].startAngle); PointF stopPt = Geometry.MoveDistance(center, radius, gaps[i].stopAngle); float newStart, newStop; if (Geometry.Distance(startPt, oldPt) < 0.01) { // Moving start point of the gap. newStart = Geometry.Angle(center, newPt); newStop = gaps[i].stopAngle; } else if (Geometry.Distance(stopPt, oldPt) < 0.01) { // Moving end point of the gap. newStart = gaps[i].startAngle; newStop = Geometry.Angle(center, newPt); } else { continue; } if (OrderGapAngles(ref newStart, ref newStop)) { float t = newStart; newStart = newStop; newStop = t; } newGaps[i] = new CircleGap(newStart, newStop); break; } } return newGaps; }
// Put the gaps into a old-style bit field approximating these gaps. public static uint ComputeApproximateOldStyleGaps(CircleGap[] gaps) { if (gaps == null || gaps.Length == 0) return 0xFFFFFFFF; // no gaps uint bits = 0xFFFFFFFF; // Start with all circle on. foreach (CircleGap gap in gaps) { // Go through each bit and determine if on or off. A bit inefficient, but simple // to get right. for (int bitNum = 0; bitNum < 32; ++bitNum) { float angle = (bitNum + 0.5F) * 360F / 32F; // center point of gap represented by this bit if ((angle >= gap.startAngle && angle <= gap.stopAngle) || ((angle - 360F) >= gap.startAngle && (angle - 360F) <= gap.stopAngle)) { // Clear bit. bits = Util.SetBit(bits, bitNum, false); } } } return bits; }
// Encode a gap array as text. Format is 45-180,181-185,... public static string EncodeGaps(CircleGap[] gaps) { if (gaps == null || gaps.Length == 0) return ""; StringBuilder builder = new StringBuilder(); for (int i = 0; i < gaps.Length; ++i) { if (i != 0) builder.Append(","); builder.AppendFormat(CultureInfo.InvariantCulture, "{0:R}:{1:R}", gaps[i].startAngle, gaps[i].stopAngle); } return builder.ToString(); }
// Remove a gap at a particular radians public static CircleGap[] RemoveGap(CircleGap[] start, double radians) { float angleInDegrees = (float)(180 * radians / Math.PI); return CircleGap.RemoveGap(start, angleInDegrees); }
// Get a series of arc start angle/sweep angle pairs to draw a circle with the gaps. Gaps must be in simplified form. public static float[] ArcStartSweeps(CircleGap[] gaps) { if (gaps == null || gaps.Length == 0) return new float[2] { 0F, 360F }; float[] arcs = new float[gaps.Length * 2]; for (int i = 0; i < gaps.Length; i += 1) { float startArc = gaps[i].stopAngle; float endArc = (i == gaps.Length - 1) ? gaps[0].startAngle : gaps[i + 1].startAngle; arcs[i * 2] = -startArc; arcs[i * 2 + 1] = -((endArc - startArc + 360.0F) % 360.0F); } return arcs; }
public ControlCourseObj(Id<ControlPoint> controlId, Id<CourseControl> courseControlId, float scaleRatio, CourseAppearance appearance, CircleGap[] gaps, PointF location) : base(controlId, courseControlId, Id<Special>.None, scaleRatio, appearance, gaps, 0, 3.0F, location) { }
float radius; // radius of the object (for hit-testing) -- unscaled. #endregion Fields #region Constructors protected PointCourseObj(Id<ControlPoint> controlId, Id<CourseControl> courseControlId, Id<Special> specialId, float scaleRatio, CourseAppearance appearance, CircleGap[] gaps, float orientation, float radius, PointF location) : base(controlId, courseControlId, specialId, scaleRatio, appearance) { this.gaps = gaps; this.movableGaps = gaps; this.orientation = orientation; this.location = location; this.radius = radius; }
public FinishCourseObj(Id<ControlPoint> controlId, Id<CourseControl> courseControlId, float scaleRatio, CourseAppearance appearance, CircleGap[] gaps, PointF location, CrossHairOptions crossHairOptions) : base(controlId, courseControlId, Id<Special>.None, scaleRatio, appearance, gaps, 0, 3.5F, location) { this.crossHairOptions = crossHairOptions; }
// Change the gaps of a control for a given scale. public static void ChangeControlGaps(EventDB eventDB, Id<ControlPoint> controlId, float scale, CircleGap[] newGaps) { ControlPoint control = eventDB.GetControl(controlId); control = (ControlPoint) control.Clone(); int scaleInt = (int) Math.Round(scale); // scale is stored as int in the gaps to prevent rounding problems. if (newGaps == null || newGaps.Length == 0) { if (control.gaps != null) control.gaps.Remove(scaleInt); } else { if (control.gaps == null) control.gaps = new Dictionary<int, CircleGap[]>(); control.gaps[scaleInt] = newGaps; } eventDB.ReplaceControlPoint(controlId, control); }
// Add a gap at a particular radians to a circle gaps. public static CircleGap[] AddGap(CircleGap[] gaps, double radians) { float angleInDegrees = (float)(180 * radians / Math.PI); const float gapAngle = 30; return CircleGap.AddGap(gaps, angleInDegrees - gapAngle / 2, angleInDegrees + gapAngle / 2); }