Ejemplo n.º 1
0
    public void AddWithNormals(
    Action<PointD3D, VectorD3D> AddPositionAndNormal,
    Action<int, int, int, bool> AddIndices,
    ref int vertexIndexOffset,
    PenX3D pen,
    IList<PointD3D> polylinePoints
    )
    {
      if (pen.DashPattern is DashPatterns.Solid)
      {
        // draw without a dash pattern - we consider the whole line as one dash segment, but instead of dash caps, with line caps
        _dashSegment.Initialize(pen.CrossSection, pen.Thickness1, pen.Thickness2, pen.LineJoin, pen.MiterLimit, pen.LineStartCap, pen.LineEndCap);
        var westNorth = PolylineMath3D.GetWestNorthVectorAtStart(polylinePoints);
        _dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, polylinePoints, westNorth.WestVector, westNorth.NorthVector, westNorth.ForwardVector, null, null);
      }
      else
      {
        // draw with a dash pattern
        _dashSegment.Initialize(pen);

        double dashOffset = 0;

        bool startCapForwardAndPositionProvided = false;
        bool startCapNeedsJoinSegment = false;
        var startCapCOS = new PolylinePointD3DAsClass();
        bool endCapForwardAndPositionProvided = false;
        bool endCapNeedsJoinSegment = false;
        var endCapCOS = new PolylinePointD3DAsClass();

        double startIndex = 0;
        double endIndex = polylinePoints.Count - 1;

        // calculate the real start and end of the line, taking the line start and end cap length into account
        if (null != pen.LineStartCap)
        {
          var v = pen.LineStartCap.GetAbsoluteBaseInset(pen.Thickness1, pen.Thickness2);

          if (v < 0)
          {
            dashOffset = -v;

            startIndex = PolylineMath3D.GetFractionalStartIndexOfPolylineWithCapInsetAbsolute(
              polylinePoints,
              -v,
              out startCapForwardAndPositionProvided,
              out startCapNeedsJoinSegment,
              startCapCOS);
          }
        }

        if (null != pen.LineEndCap)
        {
          var v = pen.LineEndCap.GetAbsoluteBaseInset(pen.Thickness1, pen.Thickness2);
          if (v < 0)
          {
            endIndex = PolylineMath3D.GetFractionalEndIndexOfPolylineWithCapInsetAbsolute(
              polylinePoints,
              -v,
              out endCapForwardAndPositionProvided,
              out endCapNeedsJoinSegment,
              endCapCOS);
          }
        }

        // now draw the individual dash segments

        bool wasLineStartCapDrawn = false;
        bool wasLineEndCapDrawn = false;

        var en = PolylineMath3D.DissectPolylineWithDashPattern(
              polylinePoints,
              startIndex, endIndex,
              pen.DashPattern,
              pen.DashPattern.DashOffset,
              Math.Max(pen.Thickness1, pen.Thickness2),
              dashOffset,
              startCapForwardAndPositionProvided,
              startCapNeedsJoinSegment,
              startCapCOS,
              endCapForwardAndPositionProvided,
              endCapNeedsJoinSegment,
              endCapCOS
              ).GetEnumerator();

        if (!en.MoveNext())
        {
          // there is no segment at all in the list, but maybe we can draw the start and end line caps
        }
        else
        {
          var previousPointList = en.Current;
          var currentPointList = en.Current;

          if (en.MoveNext())
            currentPointList = en.Current;
          else
            currentPointList = null;

          // if current point list is null, then there is only one segment, namely previousPointList, we have to draw it with start line cap and end line cap.
          if (currentPointList == null)
          {
            // note start line cap and end line cap will be overridden for this segment, but only then if the seamless merge with the dash segment
            bool overrideLineStartCap = startCapForwardAndPositionProvided && previousPointList[0].Position == startCapCOS.Position;
            bool overrideLineEndCap = endCapForwardAndPositionProvided && previousPointList[previousPointList.Count - 1].Position == endCapCOS.Position;
            _dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, previousPointList, overrideLineStartCap ? pen.LineStartCap : null, overrideLineEndCap ? pen.LineEndCap : null);
            wasLineStartCapDrawn = overrideLineStartCap;
            wasLineEndCapDrawn = overrideLineEndCap;
          }
          else // there are at least two segments
          {
            // this is the start of the line, thus we must use the lineStartCap instead of the dashStartCap

            // note start line cap will be overridden for this first segment, but only then if it seamlessly merge with the start of the dash segment
            bool overrideLineStartCap = startCapForwardAndPositionProvided && previousPointList[0].Position == startCapCOS.Position;
            _dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, previousPointList, overrideLineStartCap ? pen.LineStartCap : null, null);
            wasLineStartCapDrawn = overrideLineStartCap;

            previousPointList = currentPointList;
            while (en.MoveNext())
            {
              var currentList = en.Current;

              // draw the previous list as a normal dashSegment, thus we can use dashStartCap and dashEndCap
              _dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, previousPointList, null, null);
              previousPointList = currentList;
            }

            // now currentList is the last list, we can draw an endcap to this
            bool overrideLineEndCap = endCapForwardAndPositionProvided && previousPointList[previousPointList.Count - 1].Position == endCapCOS.Position;
            _dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, previousPointList, null, overrideLineEndCap ? pen.LineEndCap : null);
            wasLineEndCapDrawn = overrideLineEndCap;
          }

          object temporaryStorageSpace = null;

          // if the start cap was not drawn before, it must be drawn now
          if (!wasLineStartCapDrawn && null != pen.LineStartCap)
          {
            pen.LineStartCap.AddGeometry(
              AddPositionAndNormal,
              AddIndices,
              ref vertexIndexOffset,
              true,
              startCapCOS.Position,
              startCapCOS.WestVector,
              startCapCOS.NorthVector,
              startCapCOS.ForwardVector,
              pen.CrossSection,
              null,
              null,
              ref temporaryStorageSpace);
          }

          // if the end cap was not drawn before, it must be drawn now
          if (!wasLineEndCapDrawn && null != pen.LineEndCap)
          {
            pen.LineEndCap.AddGeometry(
              AddPositionAndNormal,
              AddIndices,
              ref vertexIndexOffset,
              false,
              endCapCOS.Position,
              endCapCOS.WestVector,
              endCapCOS.NorthVector,
              endCapCOS.ForwardVector,
              pen.CrossSection,
              null,
              null,
              ref temporaryStorageSpace);
          }
        }
      }
    }
Ejemplo n.º 2
0
		public void AddWithNormals(
		Action<PointD3D, VectorD3D> AddPositionAndNormal,
		Action<int, int, int, bool> AddIndices,
		ref int vertexIndexOffset,
		PenX3D pen,
		IList<PointD3D> polylinePoints
		)
		{
			if (pen.DashPattern is DashPatterns.Solid)
			{
				// draw without a dash pattern - we consider the whole line as one dash segment, but instead of dash caps, with line caps
				_dashSegment.Initialize(pen.CrossSection, pen.Thickness1, pen.Thickness2, pen.LineJoin, pen.MiterLimit, pen.LineStartCap, pen.LineEndCap);
				var westNorth = PolylineMath3D.GetWestNorthVectorAtStart(polylinePoints);
				_dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, polylinePoints, westNorth.WestVector, westNorth.NorthVector, westNorth.ForwardVector, null, null);
			}
			else
			{
				// draw with a dash pattern
				_dashSegment.Initialize(pen);

				double dashOffset = 0;

				bool startCapForwardAndPositionProvided = false;
				bool startCapNeedsJoinSegment = false;
				PolylinePointD3DAsClass startCapCOS = new PolylinePointD3DAsClass();
				bool endCapForwardAndPositionProvided = false;
				bool endCapNeedsJoinSegment = false;
				PolylinePointD3DAsClass endCapCOS = new PolylinePointD3DAsClass();

				double startIndex = 0;
				double endIndex = polylinePoints.Count - 1;

				// calculate the real start and end of the line, taking the line start and end cap length into account
				if (null != pen.LineStartCap)
				{
					var v = pen.LineStartCap.GetAbsoluteBaseInset(pen.Thickness1, pen.Thickness2);

					if (v < 0)
					{
						dashOffset = -v;

						startIndex = PolylineMath3D.GetFractionalStartIndexOfPolylineWithCapInsetAbsolute(
							polylinePoints,
							-v,
							out startCapForwardAndPositionProvided,
							out startCapNeedsJoinSegment,
							startCapCOS);
					}
				}

				if (null != pen.LineEndCap)
				{
					var v = pen.LineEndCap.GetAbsoluteBaseInset(pen.Thickness1, pen.Thickness2);
					if (v < 0)
					{
						endIndex = PolylineMath3D.GetFractionalEndIndexOfPolylineWithCapInsetAbsolute(
							polylinePoints,
							-v,
							out endCapForwardAndPositionProvided,
							out endCapNeedsJoinSegment,
							endCapCOS);
					}
				}

				// now draw the individual dash segments

				bool wasLineStartCapDrawn = false;
				bool wasLineEndCapDrawn = false;

				var en = PolylineMath3D.DissectPolylineWithDashPattern(
							polylinePoints,
							startIndex, endIndex,
							pen.DashPattern,
							pen.DashPattern.DashOffset,
							Math.Max(pen.Thickness1, pen.Thickness2),
							dashOffset,
							startCapForwardAndPositionProvided,
							startCapNeedsJoinSegment,
							startCapCOS,
							endCapForwardAndPositionProvided,
							endCapNeedsJoinSegment,
							endCapCOS
							).GetEnumerator();

				if (!en.MoveNext())
				{
					// there is no segment at all in the list, but maybe we can draw the start and end line caps
				}
				else
				{
					var previousPointList = en.Current;
					var currentPointList = en.Current;

					if (en.MoveNext())
						currentPointList = en.Current;
					else
						currentPointList = null;

					// if current point list is null, then there is only one segment, namely previousPointList, we have to draw it with start line cap and end line cap.
					if (currentPointList == null)
					{
						// note start line cap and end line cap will be overridden for this segment, but only then if the seamless merge with the dash segment
						bool overrideLineStartCap = startCapForwardAndPositionProvided && previousPointList[0].Position == startCapCOS.Position;
						bool overrideLineEndCap = endCapForwardAndPositionProvided && previousPointList[previousPointList.Count - 1].Position == endCapCOS.Position;
						_dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, previousPointList, overrideLineStartCap ? pen.LineStartCap : null, overrideLineEndCap ? pen.LineEndCap : null);
						wasLineStartCapDrawn = overrideLineStartCap;
						wasLineEndCapDrawn = overrideLineEndCap;
					}
					else // there are at least two segments
					{
						// this is the start of the line, thus we must use the lineStartCap instead of the dashStartCap

						// note start line cap will be overridden for this first segment, but only then if it seamlessly merge with the start of the dash segment
						bool overrideLineStartCap = startCapForwardAndPositionProvided && previousPointList[0].Position == startCapCOS.Position;
						_dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, previousPointList, overrideLineStartCap ? pen.LineStartCap : null, null);
						wasLineStartCapDrawn = overrideLineStartCap;

						previousPointList = currentPointList;
						while (en.MoveNext())
						{
							var currentList = en.Current;

							// draw the previous list as a normal dashSegment, thus we can use dashStartCap and dashEndCap
							_dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, previousPointList, null, null);
							previousPointList = currentList;
						}

						// now currentList is the last list, we can draw an endcap to this
						bool overrideLineEndCap = endCapForwardAndPositionProvided && previousPointList[previousPointList.Count - 1].Position == endCapCOS.Position;
						_dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, previousPointList, null, overrideLineEndCap ? pen.LineEndCap : null);
						wasLineEndCapDrawn = overrideLineEndCap;
					}

					object temporaryStorageSpace = null;

					// if the start cap was not drawn before, it must be drawn now
					if (!wasLineStartCapDrawn && null != pen.LineStartCap)
					{
						pen.LineStartCap.AddGeometry(
							AddPositionAndNormal,
							AddIndices,
							ref vertexIndexOffset,
							true,
							startCapCOS.Position,
							startCapCOS.WestVector,
							startCapCOS.NorthVector,
							startCapCOS.ForwardVector,
							pen.CrossSection,
							null,
							null,
							ref temporaryStorageSpace);
					}

					// if the end cap was not drawn before, it must be drawn now
					if (!wasLineEndCapDrawn && null != pen.LineEndCap)
					{
						pen.LineEndCap.AddGeometry(
							AddPositionAndNormal,
							AddIndices,
							ref vertexIndexOffset,
							false,
							endCapCOS.Position,
							endCapCOS.WestVector,
							endCapCOS.NorthVector,
							endCapCOS.ForwardVector,
							pen.CrossSection,
							null,
							null,
							ref temporaryStorageSpace);
					}
				}
			}
		}
Ejemplo n.º 3
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;
			}
		}
Ejemplo n.º 4
0
		/// <summary>
		/// Initialization that is needed only once per straigth line (not once per dash).
		/// </summary>
		/// <param name="crossSection">The cross section of the pen that is used to draw the line.</param>
		/// <param name="thickness1">Thickness1 of the pen.</param>
		/// <param name="thickness2">Thickness2 of the pen.</param>
		/// <param name="lineJoin">The LineJoin property of the pen.</param>
		/// <param name="miterLimit">The MiterLimit property of the pen.</param>
		/// <param name="startCap">The start cap to be used for this polyline segment.</param>
		/// <param name="endCap">The end cap to be used for this polyline segment.</param>
		public void Initialize(
		ICrossSectionOfLine crossSection,
		double thickness1,
		double thickness2,
		PenLineJoin lineJoin,
		double miterLimit,
		ILineCap startCap,
		ILineCap endCap)
		{
			this._crossSection = crossSection;
			this._crossSectionVertexCount = crossSection.NumberOfVertices;
			this._crossSectionNormalCount = crossSection.NumberOfNormals;
			this._crossSectionMaximalDistanceFromCenter = _crossSection.GetMaximalDistanceFromCenter();
			this._crossSectionPartsTriangleIndices = new Dictionary<Tuple<int, int>, int[]>();
			this._lineJoin = lineJoin;
			this._miterLimit = miterLimit;
			this._miterLimitDotThreshold = Math.Cos(Math.PI - 2 * Math.Asin(1 / miterLimit));

			this._dashStartCap = startCap;
			this._dashStartCapBaseInsetAbsolute = null == _dashStartCap ? 0 : _dashStartCap.GetAbsoluteBaseInset(thickness1, thickness2);
			this._dashEndCap = endCap;
			this._dashEndCapBaseInsetAbsolute = null == _dashEndCap ? 0 : _dashEndCap.GetAbsoluteBaseInset(thickness1, thickness2);

			_positionsTransformedStartCurrent = new PointD3D[_crossSectionVertexCount];
			_positionsTransformedEndCurrent = new PointD3D[_crossSectionVertexCount];
			_positionsTransformedStartNext = new PointD3D[_crossSectionVertexCount];
			_normalsTransformedCurrent = new VectorD3D[_crossSectionNormalCount];
			_normalsTransformedNext = new VectorD3D[_crossSectionNormalCount];
			_crossSectionRotatedVertices = new VectorD2D[_crossSectionVertexCount];
			_startCapCOS = new PolylinePointD3DAsClass();
			_endCapCOS = new PolylinePointD3DAsClass();
		}
Ejemplo n.º 5
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;
			}
		}
Ejemplo n.º 6
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="originalPolyline">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(
			IEnumerable<PolylinePointD3D> originalPolyline,
			double startIndex, double endIndex,
			bool startCapForwardAndPositionProvided,
			bool startCapNeedsJoiningSegment,
			PolylinePointD3DAsClass startCapCOS,
			bool endCapForwardAndPositionProvided,
			bool endCapNeedsJoiningSegment,
			PolylinePointD3DAsClass endCapCOS
			)
		{
			return GetPolylineWithFractionalStartAndEndIndex(
			originalPolyline.GetEnumerator(),
			startIndex, endIndex,
			startCapForwardAndPositionProvided,
			startCapNeedsJoiningSegment,
			startCapCOS,
			endCapForwardAndPositionProvided,
			endCapNeedsJoiningSegment,
			endCapCOS);
		}
Ejemplo n.º 7
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="originalPolyline">The original polyline.</param>
		/// <param name="westVector">The west vector at start of the original polyline.</param>
		/// <param name="northVector">The north vector at the start of the original polyline.</param>
		/// <param name="forwardVector">The forward vector at the start 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(
			IEnumerable<PointD3D> originalPolyline,
			VectorD3D westVector,
			VectorD3D northVector,
			VectorD3D forwardVector,
			double startIndex, double endIndex,
			bool startCapForwardAndPositionProvided,
			bool startCapNeedsJoiningSegment,
			PolylinePointD3DAsClass startCapCOS,
			bool endCapForwardAndPositionProvided,
			bool endCapNeedsJoiningSegment,
			PolylinePointD3DAsClass endCapCOS
			)
		{
			var originalPolylineEnumerator = GetPolylinePointsWithWestAndNorth(originalPolyline, westVector, northVector, forwardVector).GetEnumerator();

			return GetPolylineWithFractionalStartAndEndIndex(
			originalPolylineEnumerator,
			startIndex, endIndex,
			startCapForwardAndPositionProvided,
			startCapNeedsJoiningSegment,
			startCapCOS,
			endCapForwardAndPositionProvided,
			endCapNeedsJoiningSegment,
			endCapCOS);
		}
Ejemplo n.º 8
0
		/// <summary>
		/// Gets the fractional end index of a polyline with an end cap when given the absolute inset of the end cap.
		/// </summary>
		/// <param name="polylinePoints">The polyline points.</param>
		/// <param name="capInsetAbsolute">The absolute cap inset. Must be a value &gt;= 0.</param>
		/// <param name="endCapForwardAndPositionProvided">On return, if this value is true, the position and forward vector of the end cap was calculated and is returned in <paramref name="endCapCOS"/> (but not the west and north vectors).</param>
		/// <param name="endCapNeedsJoiningSegment">On return, if this value is true, the end cap needs a joining segment with the same direction as the start cap to seamlessly merge with the last line segment.</param>
		/// <param name="endCapCOS">On return, if <paramref name="endCapForwardAndPositionProvided"/> is true, this value holds the position and forward vector of the end cap (but not the west and north vectors).</param>
		/// <returns>The fractional end index. A value of polylinePoints.Count-1 indicate that the new polyline should end at the original polyline end, a value of e.g. polylinePoints.Count-1.5 indicate that it should end in the middle between the next-to-last and last point, and so on.</returns>
		public static double GetFractionalEndIndexOfPolylineWithCapInsetAbsolute(
			IList<PointD3D> polylinePoints,
			double capInsetAbsolute,
			out bool endCapForwardAndPositionProvided,
			out bool endCapNeedsJoiningSegment,
			PolylinePointD3DAsClass endCapCOS)
		{
			var lineEnd = polylinePoints[polylinePoints.Count - 1];
			for (int i = polylinePoints.Count - 2; i >= 0; --i)
			{
				var curr = polylinePoints[i];

				double diff = (curr - lineEnd).Length - capInsetAbsolute;

				if (diff == 0 && i > 0) // OK, exactely here
				{
					endCapCOS.Position = curr;
					endCapCOS.ForwardVector = (lineEnd - endCapCOS.Position).Normalized;
					endCapNeedsJoiningSegment = VectorD3D.DotProduct(endCapCOS.ForwardVector, (polylinePoints[i - 1] - curr).Normalized) < Cos01Degree;
					endCapForwardAndPositionProvided = true;
					return i;
				}
				else if (diff > 0) // OK, sowewhere between previous point and here.
				{
					int baseIndex = i + 1;
					var prev = polylinePoints[baseIndex];

					var relIndex = Math3D.GetFractionalIndexOfPointOnLineInGivenDistanceToAnotherPoint(prev, curr, lineEnd, capInsetAbsolute);

					endCapCOS.Position = PointD3D.Interpolate(prev, curr, relIndex);
					endCapCOS.ForwardVector = (lineEnd - endCapCOS.Position).Normalized;
					endCapNeedsJoiningSegment = VectorD3D.DotProduct(endCapCOS.ForwardVector, (prev - curr).Normalized) < Cos01Degree;
					endCapForwardAndPositionProvided = true;
					return baseIndex - relIndex;
				}
			}

			// the cap is too big, all the line is inside the cap
			// now search at least for a point which can serve as cap direction
			for (int i = 0; i < polylinePoints.Count; ++i)
			{
				if (polylinePoints[i] != lineEnd)
				{
					endCapCOS.ForwardVector = (lineEnd - polylinePoints[i]).Normalized;
					endCapCOS.Position = lineEnd + endCapCOS.ForwardVector * capInsetAbsolute;
					endCapNeedsJoiningSegment = false;
					endCapForwardAndPositionProvided = true;
					return double.NaN;
				}
			}

			endCapCOS.ForwardVector = VectorD3D.Empty;
			endCapCOS.Position = lineEnd;
			endCapNeedsJoiningSegment = false;
			endCapForwardAndPositionProvided = false;
			return polylinePoints.Count - 1;
		}
Ejemplo n.º 9
0
		/// <summary>
		/// Gets the fractional start index of a polyline with a start cap when given the absolute inset of the start cap.
		/// </summary>
		/// <param name="polylinePoints">The polyline points.</param>
		/// <param name="capInsetAbsolute">The absolute cap inset. Must be a value &gt;= 0.</param>
		/// <param name="startCapForwardAndPositionProvided">On return, if this value is true, the position and forward vector of the start cap was calculated and is returned in <paramref name="startCapCOS"/> (but not the west and north vectors).</param>
		/// <param name="startCapNeedsJoiningSegment">On return, if this value is true, the start cap needs a joining segment with the same direction as the start cap to seamlessly merge with the first line segment.</param>
		/// <param name="startCapCOS">On return, if <paramref name="startCapForwardAndPositionProvided"/> is true, this value holds the position and forward vector of the start cap (but not the west and north vector).</param>
		/// <returns>The fractional start index. A value of 0 indicate that the new polyline should start at the original polyline start, a value of 0.5 indicate that is should start in the middle between the first and second point, and so on.</returns>
		public static double GetFractionalStartIndexOfPolylineWithCapInsetAbsolute(
		IList<PointD3D> polylinePoints,
		double capInsetAbsolute,
		out bool startCapForwardAndPositionProvided,
		out bool startCapNeedsJoiningSegment,
		PolylinePointD3DAsClass startCapCOS)
		{
			var lineStart = polylinePoints[0];
			int polylinePointsCount = polylinePoints.Count;

			for (int i = 1; i < polylinePointsCount; ++i)
			{
				var curr = polylinePoints[i];
				double diff = (curr - lineStart).Length - capInsetAbsolute;

				if (diff == 0 && (i + 1) < polylinePointsCount) // OK, exactly here
				{
					startCapCOS.Position = curr;
					startCapCOS.ForwardVector = (curr - lineStart).Normalized;
					startCapNeedsJoiningSegment = VectorD3D.DotProduct(startCapCOS.ForwardVector, (polylinePoints[i + 1] - curr).Normalized) < Cos01Degree;
					startCapForwardAndPositionProvided = true;
					return i;
				}
				else if (diff > 0) // OK, sowewhere between previous point and here.
				{
					int baseIndex = i - 1;
					var prev = polylinePoints[baseIndex];

					var relIndex = Math3D.GetFractionalIndexOfPointOnLineInGivenDistanceToAnotherPoint(prev, curr, lineStart, capInsetAbsolute);

					startCapCOS.Position = PointD3D.Interpolate(prev, curr, relIndex);
					startCapCOS.ForwardVector = (startCapCOS.Position - lineStart).Normalized;
					startCapNeedsJoiningSegment = VectorD3D.DotProduct(startCapCOS.ForwardVector, (curr - prev).Normalized) < Cos01Degree;
					startCapForwardAndPositionProvided = true;
					return baseIndex + relIndex;
				}
			}

			// the cap is too big, all the line is inside the cap
			// now search at least for a point which can serve as cap direction
			for (int i = polylinePoints.Count - 1; i > 0; --i)
			{
				if (polylinePoints[i] != lineStart)
				{
					startCapCOS.ForwardVector = (polylinePoints[i] - lineStart).Normalized;
					startCapCOS.Position = lineStart + startCapCOS.ForwardVector * capInsetAbsolute;
					startCapNeedsJoiningSegment = false;
					startCapForwardAndPositionProvided = true;
					return double.NaN; ;
				}
			}

			startCapCOS.ForwardVector = VectorD3D.Empty;
			startCapCOS.Position = lineStart;
			startCapNeedsJoiningSegment = false;
			startCapForwardAndPositionProvided = false;
			return 0;
		}