/// <summary> /// Initialises a new polycurve /// </summary> /// <param name="curves"></param> public PolyCurve(IEnumerable <Curve> curves, GeometryAttributes attributes = null) : this(attributes) { foreach (Curve crv in curves) { SubCurves.Add(crv); } }
/// <summary> /// Automatically fillet any sharp corners between segments in the PolyCurve /// </summary> /// <param name="angleLimit">The smallest permissible angle</param> /// <param name="filletLength">The smallest permissible length of the added fillet curve</param> public void FilletSharpCorners(Angle angleLimit, double filletLength) { int iMax = SubCurves.Count - 1; if (Closed) { iMax += 1; } for (int i = 0; i < iMax; i++) { Curve crvA = SubCurves.GetWrapped(i); //TODO: Recursively fillet polys Curve crvB = SubCurves.GetWrapped(i + 1); Vector tA = crvA.TangentAt(1); Vector tB = crvB.TangentAt(0); Angle angle = (Math.PI - tA.AngleBetween(tB).Abs()); if (angle < angleLimit) { double cutBack = filletLength / (2 * Math.Sin(angle / 2)); if (crvA.Length > cutBack && crvB.Length > cutBack && crvA.TrimEnd(cutBack) && crvB.TrimStart(cutBack)) { Line fillet = new Line(crvA.EndPoint, crvB.StartPoint); SubCurves.Insert(i + 1, fillet); i++; iMax++; } //TODO: Deal with failed trims better } } }
/// <summary> /// Reverse the direction of this curve /// </summary> public override void Reverse() { foreach (var subCrv in SubCurves) { subCrv.Reverse(); } SubCurves.Reverse(); }
/// <summary> /// Add a new sub-curve to this PolyCurve. /// Note that to form a valid polycurve the new sub-curve *must* /// start at a point within tolerance of the end point of the /// last sub-curve. If autoConnect is set to true, this condition /// will be checked for and a line segment automatically added to /// connect the curve ends if necessary. /// </summary> /// <param name="subCurve">The curve to add.</param> /// <param name="autoConnect">If true, a line segment will automatically be added between /// the end of the last curve and the start of the newly-added one to close /// any gap between them, if necessary.</param> public void Add(Curve subCurve, bool autoConnect) { if (autoConnect == true && SubCurves.Count > 0) { Vector endPt = SubCurves.Last().EndPoint; if (endPt.DistanceToSquared(subCurve.StartPoint) > (Tolerance.Distance * Tolerance.Distance)) { //Out of tolerance; create a line: Add(new Line(endPt, subCurve.StartPoint)); } } Add(subCurve); }
/// <summary> /// Add a new line segment to the end of this polycurve. /// The line will run from the end of the last subcurve in this polycurve /// to the specified point. /// This polycurve must contain at least one subcurve already in order for the start /// point to be determined. /// </summary> /// <param name="lineEnd">The end point of the new line segment.</param> /// <returns></returns> public Line AddLine(Vector lineEnd) { if (SubCurves.Count > 0) { Vector startPoint = SubCurves.Last().EndPoint; Line result = new Line(startPoint, lineEnd); Add(result); return(result); } else { return(null); } }
/// <summary> /// Add a new arc segment to the end of this polycurve. /// The arc will run from the end of the last subcurve in this polycurve /// to the specified point, following the specified tangent at its start. /// This polycurve must contain at least one subcurve already in order for the /// start point to be determined. /// </summary> /// <param name="endX">The x-coordinate of the end point of the new arc segement</param> /// <param name="endY">The y-coordinate of the end point of the new arc segement</param> /// <param name="endZ">The z-coordinate of the end point of the new arc segement</param> /// <returns></returns> public Curve AddArcTangent(Vector startTangent, Vector arcEnd) { if (SubCurves.Count > 0) { Curve last = SubCurves.Last(); Vector startPt = last.EndPoint; Curve result = Arc.StartTangentEnd(startPt, startTangent, arcEnd); if (result != null) { Add(result); } return(result); } else { return(null); } }
/// <summary> /// Collapse any segments of this curve which have a length shorter than the /// value specified. The end-points of the curve will be kept the same, but /// short polyline segments and polycurve subcurves will be removed and the /// adjacent curves adjusted accordingly. /// </summary> /// <param name="minLength">The length below which segments will be removed.</param> /// <returns>True if any segments were removed.</returns> public override bool CollapseShortSegments(double minLength) { bool removedAny = false; for (int i = 0; i < SubCurves.Count; i++) { if (SubCurves.Count > 1) { var subCrv = SubCurves[i]; double sCLength = subCrv.Length; if (sCLength < minLength) { if (!Closed && i == 0) { // Snap next to start var nextCrv = SubCurves[1]; nextCrv.Start.Position = subCrv.StartPoint; } else if (!Closed && i == SubCurves.Count - 1) { // Snap prev to end var prevCrv = SubCurves[i - 1]; prevCrv.End.Position = subCrv.EndPoint; } else { // Snap adjacent to mid-point var nextCrv = SubCurves.GetWrapped(i + 1); var prevCrv = SubCurves.GetWrapped(i - 1); Vector midPt = subCrv.PointAtLength(sCLength / 2); } SubCurves.RemoveAt(i); i--; removedAny = true; } } } if (removedAny) { NotifyGeometryUpdated(); } return(removedAny); }
/// <summary> /// Reduce the length of this curve from the end /// by the specified value /// </summary> /// <param name="length">The length to cut back from the curve end</param> /// <returns>True if successful, false if not.</returns> public override bool TrimEnd(double length) { bool result = false; double l0 = 0; for (int i = 0; i < SubCurves.Count; i++) { Curve subCrv = SubCurves[SubCurves.Count - 1 - i]; double crvLength = subCrv.Length; double l1 = l0 + crvLength; if (l1 > length) { result = subCrv.TrimStart(length - l0); for (int j = 0; j < i; j++) { SubCurves.RemoveAt(0); } break; } } return(result); }
/// <summary> /// Add a new sub-curve to this PolyCurve. /// Note that to form a valid polycurve the new sub-curve *must* /// start at a point within tolerance of the end point of the /// last sub-curve. /// </summary> /// <param name="subCurve">The curve to add.</param> public void Add(Curve subCurve) { SubCurves.Add(subCurve); }
/// <summary> /// Curve constructor. Initialises a polycurve starting with the specifed curve /// </summary> /// <param name="curve"></param> public PolyCurve(Curve curve, GeometryAttributes attributes = null) : this(attributes) { SubCurves.Add(curve); }
/// <summary> /// Extract from this PolyCurve a chain of subcurves, starting with the /// longest and continuing to select the longest edge until no more can /// be added without adding an edge which is within an angle tolerance of /// an existing curve within the chain. /// </summary> /// <returns></returns> public PolyCurve ExtractLongCurveChain(Angle maxAngle) { Curve longest = SubCurves.GetLongest(); if (longest != null) { var tangents = new List <Vector>(); tangents.Add(longest.TangentAt(0.5)); var result = new PolyCurve(longest); int iL = SubCurves.IndexOf(longest); int i0 = iL - 1; int i1 = iL + 1; bool closed = Closed; bool tryPre = closed || i0 >= 0; bool tryPost = closed || i1 < SubCurves.Count; while (tryPre || tryPost) { Curve pre = tryPre ? SubCurves.GetWrapped(i0, closed) : null; Curve post = tryPost ? SubCurves.GetWrapped(i1, closed) : null; if (post != null && (pre == null || post.Length > pre.Length)) { Vector tang = post.TangentAt(0.5); if (result.SubCurves.Contains(post.GUID) || tangents.MaximumAngleBetween(tang).Abs() > maxAngle) { tryPost = false; } else { result.SubCurves.Add(post); tangents.Add(tang); i1++; if (!closed && i1 >= SubCurves.Count) { tryPost = false; } } } else if (pre != null) { Vector tang = pre.TangentAt(0.5); if (result.SubCurves.Contains(pre.GUID) || tangents.MaximumAngleBetween(tang).Abs() > maxAngle) { tryPre = false; } else { result.SubCurves.Insert(0, pre); tangents.Add(tang); i0--; if (!closed && i0 < 0) { tryPre = false; } } } else { tryPre = false; tryPost = false; } } return(result); } return(null); }