Esempio n. 1
0
		/// <summary>
		/// Dissects a polyline into multiple polylines using a dash pattern.
		/// </summary>
		/// <param name="linePoints">The line points of the polyline that is dissected.</param>
		/// <param name="startIndex">Fractional start index of the intermediate polyline that is then dissected. A value of 0 means the original start of the polyline, a value of 0.5 means the start is halfway between the first and the second point of the original polyline and so on.</param>
		/// <param name="endIndex">Fractional end index of the  intermediate polyline that is then dissected. A value of linePoints.Count-1 means the original end of the polyline, a value of linePoints.Count-1.5 means the end is halfway between the next-to-last and the last point of the original polyline and so on. </param>
		/// <param name="dashPattern">The dash pattern used to dissect the polyline.</param>
		/// <param name="dashPatternOffset">The dash pattern offset (relative units, i.e. same units as dashPattern itself).</param>
		/// <param name="dashPatternScale">Length of one unit of the dash pattern..</param>
		/// <param name="dashPatternStartAbsolute">An absolute length. This parameter is similar to <paramref name="dashPatternOffset"/>, but in absolute units.</param>
		/// <param name="startCapForwardAndPositionProvided">If true, the start cap position and forward vector are already known and are not need to be calculated in this procedure. But west and north vector are still calculated here.</param>
		/// <param name="startCapNeedsJoiningSegment">If true, the start cap needs a joining segment to merge seamlessly with the first segment. This joining segment is provided by the enumeration. It is a segment of length zero, i.e. has the same position as the next point, but with an orientation as the startcap has.</param>
		/// <param name="startCapCOS">Data to be filled out be the procedure that describes position and orientation of the start cap.</param>
		/// <param name="endCapForwardAndPositionProvided">If true, the end cap position and forward vector are already known and are not need to be calculated in this procedure. But west and north vector are still calculated here.</param>
		/// <param name="endCapNeedsJoiningSegment">If true, the end cap needs a joining segment to merge seamlessly with the first segment. This joining segment is provided by the enumeration. It is a segment of length zero, i.e. has the same position as the previous point, but with an orientation as the end cap has.</param>
		/// <param name="endCapCOS">Data to be filled out be the procedure that describes position and orientation of the end cap.</param>
		/// <returns>Enumeration of polylines. The first item of the returned tuples is the list with the polyline points, the second item is the west vector for the first polyline point, and the third item is the north vector for the first polyline point.</returns>
		/// <exception cref="System.ArgumentOutOfRangeException">
		/// </exception>
		/// <exception cref="System.ArgumentException"></exception>
		public static IEnumerable<List<PolylinePointD3D>> DissectPolylineWithDashPattern(
			IEnumerable<PointD3D> linePoints,
			double startIndex, double endIndex,
			IList<double> dashPattern,
			double dashPatternOffset,
			double dashPatternScale,
			double dashPatternStartAbsolute,
			bool startCapForwardAndPositionProvided,
			bool startCapNeedsJoiningSegment,
			PolylinePointD3DAsClass startCapCOS,
			bool endCapForwardAndPositionProvided,
			bool endCapNeedsJoiningSegment,
			PolylinePointD3DAsClass endCapCOS)
		{
			if (null == dashPattern || dashPattern.Count == 0)
				throw new ArgumentOutOfRangeException(nameof(dashPattern) + " is null or empty");
			if (!(dashPatternScale > 0))
				throw new ArgumentOutOfRangeException(nameof(dashPatternScale) + " should be > 0");

			int dashIndex = 0;
			int dashCount = dashPattern.Count;

			// Fast forward in dash
			double remainingOffset = dashPatternOffset;
			double currDash = dashPattern[dashIndex];

			while (remainingOffset > 0)
			{
				if ((remainingOffset - currDash) >= 0)
				{
					dashIndex = (dashIndex + 1) % dashCount;
					remainingOffset = remainingOffset - currDash;
					currDash = dashPattern[dashIndex];
				}
				else
				{
					currDash -= remainingOffset;
					remainingOffset = 0;
				}
			}

			// now move forward to dashPatternStartAbsolute
			double remainingOffsetAbsolute = dashPatternStartAbsolute;
			while (remainingOffsetAbsolute > 0)
			{
				var diff = remainingOffsetAbsolute - currDash * dashPatternScale;
				if (diff >= 0)
				{
					dashIndex = (dashIndex + 1) % dashCount;
					remainingOffsetAbsolute = diff;
					currDash = dashPattern[dashIndex];
				}
				else
				{
					currDash -= remainingOffsetAbsolute / dashPatternScale;
					remainingOffsetAbsolute = 0;
				}
			}

			var westNorth = GetWestNorthVectorAtStart(linePoints);

			var en = GetPolylineWithFractionalStartAndEndIndex(
				linePoints,
				westNorth.WestVector, westNorth.NorthVector, westNorth.ForwardVector,
				startIndex, endIndex,
				startCapForwardAndPositionProvided, startCapNeedsJoiningSegment, startCapCOS,
				endCapForwardAndPositionProvided, endCapNeedsJoiningSegment, endCapCOS).GetEnumerator();

			if (false == en.MoveNext())
				throw new ArgumentException(nameof(linePoints) + " seems not to contain line points");

			var previousPoint = en.Current;

			double patternRemainingDistance = currDash * dashPatternScale; // remaining distance for the current pattern feature

			var outputPoints = new List<PolylinePointD3D>();

			if (0 == dashIndex % 2)
				outputPoints.Add(previousPoint);

			while (true == en.MoveNext())
			{
				bool isLineDissected = false; // false as long as the current line (from prev.Item1 to curr.Item1) is not entirely dissected
				while (!isLineDissected)
				{
					var currentPoint = en.Current;
					var vec = currentPoint.Position - previousPoint.Position;
					var currentDistance = vec.Length;

					if (patternRemainingDistance >= currentDistance) // if the remaining distance of this dash is greater than the current distance, we take this point completely.
					{
						if (0 == dashIndex % 2)
							outputPoints.Add(currentPoint);
						patternRemainingDistance -= currentDistance;
						isLineDissected = true;
					}
					else // if (patternRemainingDistance < currentDistance) // if the remaining distance in this dash is smaller than the current distance, we calculate the intermediate point on this line
					{
						var rel = patternRemainingDistance / currentDistance;
						var p = PointD3D.Interpolate(previousPoint.Position, currentPoint.Position, rel);
						if (0 == dashIndex % 2)
							outputPoints.Add(new PolylinePointD3D(currentPoint.ForwardVector, currentPoint.WestVector, currentPoint.NorthVector, p));

						// now output the list
						if (outputPoints.Count >= 2)
						{
							yield return outputPoints;
							outputPoints = new List<PolylinePointD3D>(); // don't recycle the list
						}

						currentPoint = new PolylinePointD3D(currentPoint.ForwardVector, currentPoint.WestVector, currentPoint.NorthVector, p);
						patternRemainingDistance = 0;
					}

					// now increment pattern pointer if the remaining pattern distance is zero
					if (patternRemainingDistance <= 0)
					{
						// increment pattern pointer
						++dashIndex;
						if (dashIndex >= dashCount)
						{
							dashIndex = 0;
						}
						patternRemainingDistance = dashPattern[dashIndex] * dashPatternScale;

						// if now the pattern is the start of a dash, store the starting west and north vector
						if (0 == dashIndex % 2)
						{
							outputPoints.Add(currentPoint);
						}
					}

					previousPoint = currentPoint;
				}
			}

			if (outputPoints.Count >= 2)
			{
				yield return outputPoints;
			}
		}
Esempio n. 2
0
		/// <summary>
		/// Gets a part of an polyline by providing start end end indices. The indices are allowed to be real values (for instance the start index 0.5 means that the
		/// start point of the returned polyline is excactly in the middle between the first point (index 0) and the second point (index 1) of the original polyline.
		/// </summary>
		/// <param name="originalPolylineEnumerator">The enumerator for the points of the original polyline.</param>
		/// <param name="startIndex">The start index. Must be greater than or equal to zero.</param>
		/// <param name="endIndex">The end index Must be greater then the start index and smaller than or equal to originalPolyline.Count-1.</param>
		/// <param name="startCapForwardAndPositionProvided">If <c>true</c>, position and forward vector of the start cap were already calculated (but not west and north vector).</param>
		/// <param name="startCapNeedsJoiningSegment">If set to <c>true</c>, there is need for a joining segment between start cap and the rest of the polyline. This joining segment is returned in the enumeration. It has the same position as the start of the returned polyline, but the forward, west and north vectors of the start cap.</param>
		/// <param name="startCapCOS">Position, forward, west and north vectors of the start cap. This information will be filled in during the enumeration.</param>
		/// <param name="endCapForwardAndPositionProvided">If <c>true</c>, position and forward vector of the start cap were already calculated (but not west and north vector).</param>
		/// <param name="endCapNeedsJoiningSegment">If set to <c>true</c>, there is need for a joining segment between the end of the polyline and the end cap. This joining segment is returned in the enumeration. It has the same position as the end of the polyline, but the forward, west and north vectors of the end cap.</param>
		/// <param name="endCapCOS">Position, forward, west and north vectors of the end cap. This information will be filled in during the enumeration, and will be valid only after the enumeration has finished.</param>
		/// <returns>Enumeration of that part of the original polyline, that is described by the start and end index.</returns>
		public static IEnumerable<PolylinePointD3D> GetPolylineWithFractionalStartAndEndIndex(
			IEnumerator<PolylinePointD3D> originalPolylineEnumerator,
			double startIndex, double endIndex,
			bool startCapForwardAndPositionProvided,
			bool startCapNeedsJoiningSegment,
			PolylinePointD3DAsClass startCapCOS,
			bool endCapForwardAndPositionProvided,
			bool endCapNeedsJoiningSegment,
			PolylinePointD3DAsClass endCapCOS
			)
		{
			int startIndexInt = (int)Math.Floor(startIndex);
			double startIndexFrac = startIndex - startIndexInt;
			int endIndexInt = (int)Math.Floor(endIndex);
			double endIndexFrac = endIndex - endIndexInt;

			int i;
			for (i = -1; i < startIndexInt; ++i)
			{
				if (!originalPolylineEnumerator.MoveNext()) // Fast forward to the first item
					throw new ArgumentOutOfRangeException(nameof(startIndex) + " seems to be too high (not enough points in " + nameof(originalPolylineEnumerator) + ")");
			}
			var previousItem = originalPolylineEnumerator.Current;
			if (!originalPolylineEnumerator.MoveNext())
				throw new ArgumentOutOfRangeException(nameof(startIndex) + " seems to be too high (not enough points in " + nameof(originalPolylineEnumerator) + ")");
			++i;
			var currentItem = originalPolylineEnumerator.Current;

			var firstItem = previousItem;

			if (startIndexFrac != 0)
			{
				var newPoint = PointD3D.Interpolate(previousItem.Position, currentItem.Position, startIndexFrac);
				firstItem = new PolylinePointD3D(currentItem.ForwardVector, currentItem.WestVector, currentItem.NorthVector, newPoint); // return interpolated start point
			}

			if (startCapForwardAndPositionProvided)
			{
				// per convention the very first and the next returned item should have the same forward, west and north vectors
				// thus we change the first item now and also take it for the start cap
				GetWestAndNorthVectorsForPreviousSegment(startCapCOS.ForwardVector, firstItem.ForwardVector, ref firstItem.WestVector, ref firstItem.NorthVector);
				firstItem.ForwardVector = startCapCOS.ForwardVector;
				startCapCOS.WestVector = firstItem.WestVector;
				startCapCOS.NorthVector = firstItem.NorthVector;
				if (startCapNeedsJoiningSegment)
				{
					yield return firstItem; // yield return exactly the same as the first item -> but this will lead to the drawing of a joining segment between start cap and the rest of the polyline
				}
			}
			else
			{
				startCapCOS.Position = firstItem.Position;
				startCapCOS.WestVector = firstItem.WestVector;
				startCapCOS.NorthVector = firstItem.NorthVector;
				startCapCOS.ForwardVector = firstItem.ForwardVector;
			}

			yield return firstItem; // now yield return the first item

			if (endIndexInt > startIndexInt)
				yield return currentItem; // if start and end do not lie in the same segment, we can now yield return the second point

			// now for all other points before the end point
			for (; i < endIndexInt; ++i)
			{
				if (!originalPolylineEnumerator.MoveNext())
					throw new ArgumentOutOfRangeException(nameof(endIndex) + " seems to be too high (not enough points in " + nameof(originalPolylineEnumerator) + ")");

				yield return originalPolylineEnumerator.Current; // return intermediate points, maybe here the end point too (if endIndexFrac was zero)
			}

			// now the last item

			var lastItem = originalPolylineEnumerator.Current; // note: if endIndexFrac is 0, the last item was yield returned in the loop above

			if (endIndexFrac != 0)
			{
				if (endIndexInt != startIndexInt)
				{
					previousItem = originalPolylineEnumerator.Current;
					if (!originalPolylineEnumerator.MoveNext())
						throw new ArgumentOutOfRangeException(nameof(endIndex) + " seems to be too high (not enough points in " + nameof(originalPolylineEnumerator) + ")");
					currentItem = originalPolylineEnumerator.Current;
				}

				var newPoint = PointD3D.Interpolate(previousItem.Position, currentItem.Position, endIndexFrac);
				lastItem = new PolylinePointD3D(currentItem.ForwardVector, currentItem.WestVector, currentItem.NorthVector, newPoint);
				yield return lastItem; // here the last item was not yield returned, thus we do it now
			}

			if (endCapForwardAndPositionProvided)
			{
				endCapCOS.WestVector = lastItem.WestVector;
				endCapCOS.NorthVector = lastItem.NorthVector;
				GetWestAndNorthVectorsForNextSegment(endCapCOS.ForwardVector, lastItem.ForwardVector, ref endCapCOS.WestVector, ref endCapCOS.NorthVector);
				if (endCapNeedsJoiningSegment)
				{
					yield return new PolylinePointD3D(endCapCOS.ForwardVector, endCapCOS.WestVector, endCapCOS.NorthVector, lastItem.Position); // and maybe return a very last point which is the joining segment. Is has the same location as the previous point, and thus the forward vector must be used here!
				}
			}
			else
			{
				endCapCOS.Position = lastItem.Position;
				endCapCOS.WestVector = lastItem.WestVector;
				endCapCOS.NorthVector = lastItem.NorthVector;
				endCapCOS.ForwardVector = lastItem.ForwardVector;
			}
		}