/// <summary> /// Initialization that is needed only once per straigth line (not once per dash). /// </summary> /// <param name="pen">The pen that is used to draw the line.</param> /// <param name="west">The west vector.</param> /// <param name="north">The north vector.</param> /// <param name="line">The global line to draw. This argument is needed to extract the line vector, which for a straight line is also the line vector for each individual dash segment.</param> public void Initialize( PenX3D pen, VectorD3D west, VectorD3D north, LineD3D line) { Initialize( pen.CrossSection, pen.Thickness1, pen.Thickness2, pen.DashStartCap, pen.DashEndCap, west, north, line ); }
/// <summary> /// Dissects a straight line into individual line segments, using a dash pattern. /// </summary> /// <param name="line">The line to dissect.</param> /// <param name="dashPattern">The dash pattern.</param> /// <param name="dashPatternOffset">The dash pattern offset (relative units, i.e. same units as dashPattern itself).</param> /// <param name="dashPatternScale">The dash pattern scale.</param> /// <param name="dashPatternStartAbsolute">An absolute length. This parameter is similar to <paramref name="dashPatternOffset"/>, but in absolute units.</param> /// <returns></returns> public static IEnumerable <LineD3D> DissectStraightLineWithDashPattern(LineD3D line, IReadOnlyList <double> dashPattern, double dashPatternOffset, double dashPatternScale, double dashPatternStartAbsolute) { 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; } } // now we are ready to start double lineLength = line.Length; double sumPrev = 0; double lengthPrev = 0; for (; lengthPrev < lineLength;) { double sumCurr = sumPrev + currDash; double lengthCurr = sumCurr * dashPatternScale; if (lengthCurr >= lineLength) { lengthCurr = lineLength; } if ((0 == dashIndex % 2) && (lengthCurr > lengthPrev)) { yield return(new LineD3D( line.GetPointAtLineFromRelativeValue(lengthPrev / lineLength), line.GetPointAtLineFromRelativeValue(lengthCurr / lineLength) )); } sumPrev = sumCurr; lengthPrev = lengthCurr; dashIndex = (dashIndex + 1) % dashCount; currDash = dashPattern[dashIndex]; } }
/// <summary> /// Gets the west and north vector for a single straight line. /// </summary> /// <param name="line">The line.</param> /// <returns>The west and the north vector (Item1=west vector, Item2 = north vector).</returns> public static Tuple<VectorD3D, VectorD3D> GetWestNorthVectors(LineD3D line) { return GetWestNorthVectors(line.Vector); }
public void AddGeometry( Action<PointD3D, VectorD3D> AddPositionAndNormal, Action<int, int, int, bool> AddIndices, ref int vertexIndexOffset, PenX3D pen, LineD3D line ) { var westnorth = PolylineMath3D.GetWestNorthVectors(line); var westVector = westnorth.Item1; var northVector = westnorth.Item2; 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.LineStartCap, pen.LineEndCap, westVector, northVector, line); _dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, line, null, null); } else { // draw with a dash pattern _dashSegment.Initialize(pen, westVector, northVector, line); double dashOffset = 0; PointD3D lineStart = line.P0; PointD3D lineEnd = line.P1; var lineVector = line.LineVector; double lineLength = lineVector.Length; var lineVectorNormalized = lineVector / lineLength; // 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; lineStart += -v * lineVectorNormalized; lineLength += v; } } if (null != pen.LineEndCap) { var v = pen.LineEndCap.GetAbsoluteBaseInset(pen.Thickness1, pen.Thickness2); if (v < 0) { lineEnd += v * lineVectorNormalized; lineLength += v; } } // now draw the individual dash segments bool wasLineStartCapDrawn = false; bool wasLineEndCapDrawn = false; if (lineLength > 0) { foreach (var seg in Math3D.DissectStraightLineWithDashPattern(new LineD3D(lineStart, lineEnd), pen.DashPattern, pen.DashPattern.DashOffset, Math.Max(pen.Thickness1, pen.Thickness2), dashOffset)) { if (seg.P0 == lineStart) // this is the start of the line, thus we must use the lineStartCap instead of the dashStartCap { _dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, seg, pen.LineStartCap, null); wasLineStartCapDrawn = true; } else if (seg.P1 == lineEnd) // this is the end of the line, thus we must use the lineEndCap instead of the dashEndCap { _dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, seg, null, pen.LineEndCap); wasLineEndCapDrawn = true; } else // this is a normal dashSegment, thus we can use dashStartCap and dashEndCap { _dashSegment.AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, seg, null, null); } } } 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, lineStart, westVector, northVector, lineVectorNormalized, 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, lineEnd, westVector, northVector, lineVectorNormalized, pen.CrossSection, null, null, ref temporaryStorageSpace); } } }
/// <summary> /// Dissects a straight line into individual line segments, using a dash pattern. /// </summary> /// <param name="line">The line to dissect.</param> /// <param name="dashPattern">The dash pattern.</param> /// <param name="dashPatternOffset">The dash pattern offset (relative units, i.e. same units as dashPattern itself).</param> /// <param name="dashPatternScale">The dash pattern scale.</param> /// <param name="dashPatternStartAbsolute">An absolute length. This parameter is similar to <paramref name="dashPatternOffset"/>, but in absolute units.</param> /// <returns></returns> public static IEnumerable<LineD3D> DissectStraightLineWithDashPattern(LineD3D line, IList<double> dashPattern, double dashPatternOffset, double dashPatternScale, double dashPatternStartAbsolute) { 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; } } // now we are ready to start double lineLength = line.Length; double sumPrev = 0; double lengthPrev = 0; for (; lengthPrev < lineLength;) { double sumCurr = sumPrev + currDash; double lengthCurr = sumCurr * dashPatternScale; if (lengthCurr >= lineLength) { lengthCurr = lineLength; } if ((0 == dashIndex % 2) && (lengthCurr > lengthPrev)) { yield return new LineD3D( line.GetPointAtLineFromRelativeValue(lengthPrev / lineLength), line.GetPointAtLineFromRelativeValue(lengthCurr / lineLength) ); } sumPrev = sumCurr; lengthPrev = lengthCurr; dashIndex = (dashIndex + 1) % dashCount; currDash = dashPattern[dashIndex]; } }
public bool IsHit(LineD3D line, double thickness1, double thickness2) { if (!(line.Length > 0)) return false; var eastnorth = PolylineMath3D.GetWestNorthVectors(line); var e = eastnorth.Item1; // east vector var n = eastnorth.Item2; // north vector double thickness1By2 = thickness1 / 2; double thickness2By2 = thickness2 / 2; PointD3D[] pts = new PointD3D[8]; pts[0] = _hitTransformation.Transform(line.P0 - thickness1By2 * e - thickness2By2 * n); pts[1] = _hitTransformation.Transform(line.P1 - thickness1By2 * e - thickness2By2 * n); pts[2] = _hitTransformation.Transform(line.P0 + thickness1By2 * e - thickness2By2 * n); pts[3] = _hitTransformation.Transform(line.P1 + thickness1By2 * e - thickness2By2 * n); pts[4] = _hitTransformation.Transform(line.P0 - thickness1By2 * e + thickness2By2 * n); pts[5] = _hitTransformation.Transform(line.P1 - thickness1By2 * e + thickness2By2 * n); pts[6] = _hitTransformation.Transform(line.P0 + thickness1By2 * e + thickness2By2 * n); pts[7] = _hitTransformation.Transform(line.P1 + thickness1By2 * e + thickness2By2 * n); double z; foreach (var ti in RectangleD3D.GetTriangleIndices()) { if (HitTestWithAlreadyTransformedPoints(pts[ti.Item1], pts[ti.Item2], pts[ti.Item3], out z) && z >= 0) return true; } z = double.NaN; return false; }
public void Initialize( ICrossSectionOfLine crossSection, double thickness1, double thickness2, ILineCap startCap, ILineCap endCap, VectorD3D westVector, VectorD3D northVector, LineD3D line) { this._crossSection = crossSection; this._crossSectionVertexCount = crossSection.NumberOfVertices; this._crossSectionNormalCount = crossSection.NumberOfNormals; this._dashStartCap = startCap; this._dashStartCapBaseInsetAbsolute = null == _dashStartCap ? 0 : _dashStartCap.GetAbsoluteBaseInset(thickness1, thickness2); this._dashEndCap = endCap; this._dashEndCapBaseInsetAbsolute = null == _dashEndCap ? 0 : _dashEndCap.GetAbsoluteBaseInset(thickness1, thickness2); this._westVector = westVector; this._northVector = northVector; this._forwardVector = line.LineVectorNormalized; this._lastNormalsTransformed = new VectorD3D[_crossSectionNormalCount]; this._lastPositionsTransformedStart = new PointD3D[_crossSectionVertexCount]; this._lastPositionsTransformedEnd = new PointD3D[_crossSectionVertexCount]; // Get the matrix for the start plane var matrix = Math3D.Get2DProjectionToPlane(westVector, northVector, PointD3D.Empty); // note: for a single line segment, the normals need to be calculated only once for (int i = 0; i < _lastNormalsTransformed.Length; ++i) { _lastNormalsTransformed[i] = matrix.Transform(crossSection.Normals(i)); } }
public void AddGeometry( Action<PointD3D, VectorD3D> AddPositionAndNormal, Action<int, int, int, bool> AddIndices, ref int vertexIndexOffset, LineD3D dashSegment, ILineCap overrideStartCap, ILineCap overrideEndCap) { if (null == _lastNormalsTransformed) throw new InvalidProgramException("The structure is not initialized yet. Call Initialize before using it!"); PointD3D lineStart = dashSegment.P0; PointD3D lineEnd = dashSegment.P1; var lineVector = dashSegment.LineVector; double lineLength = lineVector.Length; if (null != _dashStartCap && null == overrideStartCap) { if (_dashStartCapBaseInsetAbsolute < 0) { lineStart += -_dashStartCapBaseInsetAbsolute * _forwardVector; lineLength += _dashStartCapBaseInsetAbsolute; } } if (null != _dashEndCap && null == overrideEndCap) { if (_dashEndCapBaseInsetAbsolute < 0) { lineEnd += _dashEndCapBaseInsetAbsolute * _forwardVector; lineLength += _dashEndCapBaseInsetAbsolute; } } AddGeometry(AddPositionAndNormal, AddIndices, ref vertexIndexOffset, lineStart, lineEnd, lineLength > 0, overrideStartCap, overrideEndCap); }