// Add a new gap an an array of gaps. The resulting array is simplified. The original array may be null. public static LegGap[] AddGap(SymPath path, LegGap[] original, PointF pt1, PointF pt2) { float dist1, dist2; LegGap newGap; LegGap[] newGaps; // map points to closes points on the line. path.DistanceFromPoint(pt1, out pt1); path.DistanceFromPoint(pt2, out pt2); // Map to distances along the line. dist1 = path.LengthToPoint(pt1); dist2 = path.LengthToPoint(pt2); // Create the new gap. if (dist1 < dist2) newGap = new LegGap(dist1, dist2 - dist1); else newGap = new LegGap(dist2, dist1 - dist2); // Add to the old gaps. if (original == null) newGaps = new LegGap[1] { newGap }; else { newGaps = new LegGap[original.Length + 1]; Array.Copy(original, newGaps, original.Length); newGaps[original.Length] = newGap; } // Simplify return SimplifyGaps(newGaps, path.Length); }
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. LegGap[] newGaps = LegGap.AddGap(courseObjStart.path, courseObjStart.gaps, startDrag, location); // Put the new gaps into the highlight. courseObjDrag = new LegCourseObj(courseObjStart.controlId, courseObjStart.courseControlId, courseObjStart.courseControlId2, courseObjStart.courseObjRatio, courseObjStart.appearance, courseObjStart.path, newGaps); displayUpdateNeeded = true; }
// Add a new gap an an array of gaps. The resulting array is simplified. The original array may be null. public static LegGap[] AddGap(SymPath path, LegGap[] original, PointF pt1, PointF pt2) { float dist1, dist2; LegGap newGap; LegGap[] newGaps; // map points to closes points on the line. path.DistanceFromPoint(pt1, out pt1); path.DistanceFromPoint(pt2, out pt2); // Map to distances along the line. dist1 = path.LengthToPoint(pt1); dist2 = path.LengthToPoint(pt2); // Create the new gap. if (dist1 < dist2) { newGap = new LegGap(dist1, dist2 - dist1); } else { newGap = new LegGap(dist2, dist1 - dist2); } // Add to the old gaps. if (original == null) { newGaps = new LegGap[1] { newGap } } ; else { newGaps = new LegGap[original.Length + 1]; Array.Copy(original, newGaps, original.Length); newGaps[original.Length] = newGap; } // Simplify return(SimplifyGaps(newGaps, path.Length)); }
// Split a path into multiple paths based on an array of LegGaps. The gaps might extend beyond the end of the // or the beginning of the path. The gaps array need not be in simplified form. public static SymPath[] SplitPathWithGaps(SymPath pathInitial, LegGap[] gaps) { // Get the length of the path. float pathLength = pathInitial.Length; // Simply and sort the gaps. gaps = SimplifyGaps(gaps, pathLength); // If no gaps length, the entire path is correct. if (gaps == null) return new SymPath[1] { pathInitial }; // Transform into start/stop distances from beginning of path. float[] starts = new float[gaps.Length + 1]; float[] ends = new float[gaps.Length + 1]; starts[0] = 0; ends[gaps.Length] = pathLength; for (int i = 0; i < gaps.Length; ++i) { ends[i] = gaps[i].distanceFromStart; starts[i + 1] = gaps[i].distanceFromStart + gaps[i].length; } // Each 2 points is a new path. List<SymPath> list = new List<SymPath>(starts.Length); for (int i = 0; i < starts.Length; ++i) { SymPath p = pathInitial.Segment(pathInitial.PointAtLength(starts[i]), pathInitial.PointAtLength(ends[i])); if (p != null) list.Add(p); } return list.ToArray(); }
// Simplify a gap array. Sorts in order, combines overlapping gaps, and removes gaps before and beyond end of length. public static LegGap[] SimplifyGaps(LegGap[] gaps, float pathLength) { if (gaps == null || gaps.Length == 0) return null; List<LegGap> newGaps = new List<LegGap>(gaps); // Truncating to the length of the path. Doesn't remove gaps, but may set their length to negative, which // will cause them to be removed later. int i; for (i = 0; i < newGaps.Count; ++i) { if (newGaps[i].distanceFromStart + newGaps[i].length > pathLength) { newGaps[i] = new LegGap(newGaps[i].distanceFromStart, pathLength - newGaps[i].distanceFromStart); } if (newGaps[i].distanceFromStart < 0) { newGaps[i] = new LegGap(0, newGaps[i].length + newGaps[i].distanceFromStart); } } // Remove any gaps with 0 or negative length. i = 0; while (i < newGaps.Count) { if (newGaps[i].length <= 0) newGaps.RemoveAt(i); else ++i; } // Sort gaps by their start point. newGaps.Sort(delegate(LegGap gap1, LegGap gap2) { if (gap1.distanceFromStart < gap2.distanceFromStart) return -1; else if (gap1.distanceFromStart > gap2.distanceFromStart) return 1; else return 0; }); // Combine gaps that overlap. i = 0; while (i < newGaps.Count - 1) { if (newGaps[i].distanceFromStart + newGaps[i].length >= newGaps[i + 1].distanceFromStart) { if (newGaps[i].distanceFromStart + newGaps[i].length < newGaps[i + 1].distanceFromStart + newGaps[i + 1].length) newGaps[i] = new LegGap(newGaps[i].distanceFromStart, newGaps[i + 1].length + (newGaps[i + 1].distanceFromStart - newGaps[i].distanceFromStart)); newGaps.RemoveAt(i + 1); } else ++i; } // And we're done. return (newGaps.Count > 0) ? newGaps.ToArray() : null; }
// Move a gap start/stop point to a new location. Return the new gap array. The gap array is NOT simplified. public static LegGap[] MoveStartStopPoint(SymPath path, LegGap[] gaps, PointF oldPt, PointF newPt) { LegGap[] newGaps = (LegGap[]) gaps.Clone(); float newLengthAlongPath = path.LengthToPoint(newPt); for (int i = 0; i < newGaps.Length; ++i) { PointF startPt = path.PointAtLength(gaps[i].distanceFromStart); PointF endPt = path.PointAtLength(gaps[i].distanceFromStart + gaps[i].length); if (Geometry.Distance(startPt, oldPt) < 0.01) { // Moving start point of the gap. newGaps[i].length -= (newLengthAlongPath - newGaps[i].distanceFromStart); newGaps[i].distanceFromStart = newLengthAlongPath; } else if (Geometry.Distance(endPt, oldPt) < 0.01) { // Moving end point of the gap. newGaps[i].length = newLengthAlongPath - gaps[i].distanceFromStart; } } return newGaps; }
// Transform a gap array to a new path, keeping close gaps in the closest position on the new path. Remove far away gaps. public static LegGap[] MoveGapsToNewPath(LegGap[] oldGaps, SymPath oldPath, SymPath newPath) { oldGaps = SimplifyGaps(oldGaps, oldPath.Length); if (oldGaps == null) return null; PointF oldStart, oldEnd, newStart, newEnd; // ends points of the gaps float distanceStart, distanceEnd; // distance between oldStart and newStart, distance between oldEnd and newEnd. List<LegGap> newGaps = new List<LegGap>(); // Move gap to a new gap by converting start and end to point, finding closest points on new path. // If the gap has moved too far, just remove it, else transformit. for (int i = 0; i < oldGaps.Length; ++i) { oldStart = oldPath.PointAtLength(oldGaps[i].distanceFromStart); oldEnd = oldPath.PointAtLength(oldGaps[i].distanceFromStart + oldGaps[i].length); distanceStart = newPath.DistanceFromPoint(oldStart, out newStart); distanceEnd = newPath.DistanceFromPoint(oldEnd, out newEnd); // If the new gap is close enough to the old gap, then add the new gap. if (distanceStart + distanceEnd <= 2 * oldGaps[i].length) { float newDistanceToStart = newPath.LengthToPoint(newStart); float newDistanceToEnd = newPath.LengthToPoint(newEnd); if (newDistanceToStart < newDistanceToEnd) newGaps.Add(new LegGap(newDistanceToStart, newDistanceToEnd - newDistanceToStart)); else newGaps.Add(new LegGap(newDistanceToEnd, newDistanceToStart - newDistanceToEnd)); } } // Simply the new gap array. return SimplifyGaps(newGaps.ToArray(), newPath.Length); }
/* * Static functions that work on arrays of leg gaps. */ // Get the start/stop points of the gaps. Primarily useful for finding where the handles should be. public static PointF[] GapStartStopPoints(SymPath path, LegGap[] 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] = path.PointAtLength(gaps[i].distanceFromStart); pts[i * 2 + 1] = path.PointAtLength(gaps[i].distanceFromStart + gaps[i].length); } return pts; }
// Simplify a gap array. Sorts in order, combines overlapping gaps, and removes gaps before and beyond end of length. public static LegGap[] SimplifyGaps(LegGap[] gaps, float pathLength) { if (gaps == null || gaps.Length == 0) { return(null); } List <LegGap> newGaps = new List <LegGap>(gaps); // Truncating to the length of the path. Doesn't remove gaps, but may set their length to negative, which // will cause them to be removed later. int i; for (i = 0; i < newGaps.Count; ++i) { if (newGaps[i].distanceFromStart + newGaps[i].length > pathLength) { newGaps[i] = new LegGap(newGaps[i].distanceFromStart, pathLength - newGaps[i].distanceFromStart); } if (newGaps[i].distanceFromStart < 0) { newGaps[i] = new LegGap(0, newGaps[i].length + newGaps[i].distanceFromStart); } } // Remove any gaps with 0 or negative length. i = 0; while (i < newGaps.Count) { if (newGaps[i].length <= 0) { newGaps.RemoveAt(i); } else { ++i; } } // Sort gaps by their start point. newGaps.Sort(delegate(LegGap gap1, LegGap gap2) { if (gap1.distanceFromStart < gap2.distanceFromStart) { return(-1); } else if (gap1.distanceFromStart > gap2.distanceFromStart) { return(1); } else { return(0); } }); // Combine gaps that overlap. i = 0; while (i < newGaps.Count - 1) { if (newGaps[i].distanceFromStart + newGaps[i].length >= newGaps[i + 1].distanceFromStart) { if (newGaps[i].distanceFromStart + newGaps[i].length < newGaps[i + 1].distanceFromStart + newGaps[i + 1].length) { newGaps[i] = new LegGap(newGaps[i].distanceFromStart, newGaps[i + 1].length + (newGaps[i + 1].distanceFromStart - newGaps[i].distanceFromStart)); } newGaps.RemoveAt(i + 1); } else { ++i; } } // And we're done. return((newGaps.Count > 0) ? newGaps.ToArray() : null); }
// Create a path from pt1 to pt2, with the given radius around the legs. If the leg would // be of zero length, return null. If bends is non-null, then the path should include those bends. // If gaps is non-null, updates the gaps by subtracting the radius from them. private static SymPath GetLegPath(PointF pt1, double radius1, PointF pt2, double radius2, PointF[] bends, LegGap[] gaps) { double legLength = Geometry.Distance(pt1, pt2); // Check for no leg. if (legLength <= radius1 + radius2) return null; int bendCount = (bends == null) ? 0 : bends.Length; PointF[] coords = new PointF[2 + bendCount]; PointKind[] kinds = new PointKind[2 + bendCount]; // Set the end points. coords[0] = Geometry.DistanceAlongLine(pt1, (bendCount > 0) ? bends[0] : pt2, radius1); coords[coords.Length - 1] = Geometry.DistanceAlongLine(pt2, (bendCount > 0) ? bends[bends.Length - 1] : pt1, radius2); // Set the bends. if (bendCount > 0) Array.Copy(bends, 0, coords, 1, bendCount); // Create the path. for (int i = 0; i < kinds.Length; ++i) kinds[i] = PointKind.Normal; // Update the gaps (if any). if (gaps != null) { for (int i = 0; i < gaps.Length; ++i) gaps[i].distanceFromStart -= (float) radius1; } return new SymPath(coords, kinds); }
// Create a path from pt1 to pt2, with a radius aroudn the points correct for the given control kind. If the leg would // be of zero length, return null. The controlIds for the start and end points are optional -- if supplied, they are used // to deal with bends and gaps. If either is None, then the legs don't use bends or gaps. Returns the gaps to used // with the radius subtracted from them. public static SymPath GetLegPath(EventDB eventDB, PointF pt1, ControlPointKind kind1, Id<ControlPoint> controlId1, PointF pt2, ControlPointKind kind2, Id<ControlPoint> controlId2, float scaleRatio, CourseAppearance appearance, out LegGap[] gaps) { PointF[] bends = null; gaps = null; // Get bends and gaps if controls were supplied. if (controlId1.IsNotNone && controlId2.IsNotNone) { Id<Leg> legId = QueryEvent.FindLeg(eventDB, controlId1, controlId2); Leg leg = (legId.IsNotNone) ? eventDB.GetLeg(legId) : null; // Get the path of the line. if (leg != null) { bends = leg.bends; gaps = QueryEvent.GetLegGaps(eventDB, controlId1, controlId2); } } return GetLegPath(pt1, GetLegRadius(kind1, scaleRatio, appearance), pt2, GetLegRadius(kind2, scaleRatio, appearance), bends, gaps); }
float thickness; // thickness of the line (unscaled) #endregion Fields #region Constructors protected LineCourseObj(Id<ControlPoint> controlId, Id<CourseControl> courseControlId, Id<CourseControl> courseControlId2, Id<Special> specialId, float scaleRatio, CourseAppearance appearance, float thickness, SymPath path, LegGap[] gaps) : base(controlId, courseControlId, specialId, scaleRatio, appearance) { this.courseControlId2 = courseControlId2; this.thickness = thickness; this.path = path; this.gaps = this.movableGaps = gaps; }
public LegCourseObj(Id<ControlPoint> controlId, Id<CourseControl> courseControlId, Id<CourseControl> courseControlId2, float scaleRatio, CourseAppearance appearance, SymPath path, LegGap[] gaps) : base(controlId, courseControlId, courseControlId2, Id<Special>.None, scaleRatio, appearance, NormalCourseAppearance.lineThickness * appearance.lineWidth, path, gaps) { }
// Change the gaps associated with a leg. public static void ChangeLegGaps(EventDB eventDB, Id<ControlPoint> controlId1, Id<ControlPoint> controlId2, LegGap[] newGaps) { // Get the leg object for this leg. Create one if needed. Id<Leg> legId = QueryEvent.FindLeg(eventDB, controlId1, controlId2); Leg leg; if (legId.IsNone) leg = new Leg(controlId1, controlId2); else leg = (Leg) eventDB.GetLeg(legId).Clone(); // Change the gaps. leg.gaps = (newGaps == null) ? null : (LegGap[]) newGaps.Clone(); // Write the change leg object to the event DB. If the leg is vacuous, could involve removing the leg. if (leg.IsVacuous()) { if (legId.IsNotNone) eventDB.RemoveLeg(legId); } else { if (legId.IsNone) eventDB.AddLeg(leg); else eventDB.ReplaceLeg(legId, leg); } }