static void SkipRange( float distance, PathDistanceForwardIterator pathIt, PathPatternIterator patternIt, PathProperties pathProps, JoiningInfo[] joiningInfo, float[] segmentLengths) { float unitsRemaining = distance; while (unitsRemaining > Epsilon) { var result = pathIt.AdvanceBy(unitsRemaining, out unitsRemaining); switch (result) { case PathDistanceForwardIterator.Result.Ended: return; case PathDistanceForwardIterator.Result.Stepped: if (unitsRemaining < Epsilon) { return; } break; case PathDistanceForwardIterator.Result.NewSegment: HandleNewSegmentJoining(pathIt, patternIt, joiningInfo, pathProps.Stroke.HalfThickness, segmentLengths); break; } } }
private static void TessellatePath(BezierContour contour, PathProperties pathProps, List <Geometry> geoms, TessellationOptions tessellationOptions) { UnityEngine.Profiling.Profiler.BeginSample("TessellatePath"); if (pathProps.Stroke != null) { Vector2[] vertices; UInt16[] indices; VectorUtils.TessellatePath(contour, pathProps, tessellationOptions, out vertices, out indices); var color = pathProps.Stroke.Color; if (indices.Length > 0) { geoms.Add(new Geometry() { Vertices = vertices, Indices = indices, Color = color }); } } UnityEngine.Profiling.Profiler.EndSample(); }
/// <summary> /// Tessellates a path. /// </summary> /// <param name="contour">The path to tessellate</param> /// <param name="pathProps">The path properties</param> /// <param name="tessellateOptions">The tessellation options</param> /// <param name="vertices">The resulting vertices</param> /// <param name="indices">The resulting triangles</param> /// <remarks> /// The individual line segments generated during tessellation are made out of a set of ordered vertices. It is important /// to honor this ordering so joining and and capping connect properly with the existing vertices without generating dupes. /// The ordering assumed is as follows: /// The last two vertices of a piece must be such that the first is generated at the end with a positive half-thickness /// while the second vertex is at the end too but at a negative half-thickness. /// No assumptions are enforced for other vertices before the two last vertices. /// </remarks> public static void TessellatePath(BezierContour contour, PathProperties pathProps, TessellationOptions tessellateOptions, out Vector2[] vertices, out UInt16[] indices) { if (tessellateOptions.StepDistance < Epsilon) { throw new Exception("stepDistance too small"); } if (contour.Segments.Length < 2) { vertices = new Vector2[0]; indices = new UInt16[0]; return; } UnityEngine.Profiling.Profiler.BeginSample("TessellatePath"); float[] segmentLengths = VectorUtils.SegmentsLengths(contour.Segments, contour.Closed); // Approximate the number of vertices/indices we need to store the results so we reduce memory reallocations during work float approxTotalLength = 0.0f; foreach (var s in segmentLengths) { approxTotalLength += s; } int approxStepCount = Math.Max((int)(approxTotalLength / tessellateOptions.StepDistance + 0.5f), 2); if (pathProps.Stroke.Pattern != null) { approxStepCount += pathProps.Stroke.Pattern.Length * 2; } List <Vector2> verts = new List <Vector2>(approxStepCount * 2 + 32); // A little bit possibly for the endings List <UInt16> inds = new List <UInt16>((int)(verts.Capacity * 1.5f)); // Usually every 4 verts represent a quad that uses 6 indices var patternIt = new PathPatternIterator(pathProps.Stroke.Pattern, pathProps.Stroke.PatternOffset); var pathIt = new PathDistanceForwardIterator(contour.Segments, contour.Closed, tessellateOptions.MaxCordDeviationSquared, tessellateOptions.MaxTanAngleDeviationCosine, tessellateOptions.SamplingStepSize); JoiningInfo[] joiningInfo = new JoiningInfo[2]; HandleNewSegmentJoining(pathIt, patternIt, joiningInfo, pathProps.Stroke.HalfThickness, segmentLengths); int rangeIndex = 0; while (!pathIt.Ended) { if (patternIt.IsSolid) { TessellateRange(patternIt.SegmentLength, pathIt, patternIt, pathProps, tessellateOptions, joiningInfo, segmentLengths, approxTotalLength, rangeIndex++, verts, inds); } else { SkipRange(patternIt.SegmentLength, pathIt, patternIt, pathProps, joiningInfo, segmentLengths); } patternIt.Advance(); } vertices = verts.ToArray(); indices = inds.ToArray(); UnityEngine.Profiling.Profiler.EndSample(); }
static void TessellateRange( float distance, PathDistanceForwardIterator pathIt, PathPatternIterator patternIt, PathProperties pathProps, TessellationOptions tessellateOptions, JoiningInfo[] joiningInfo, float[] segmentLengths, float totalLength, int rangeIndex, List <Vector2> verts, List <UInt16> inds) { bool startOfLoop = pathIt.Closed && (pathIt.CurrentSegment == 0) && (pathIt.CurrentT == 0.0f); if (startOfLoop && (joiningInfo[0] != null)) { GenerateJoining(joiningInfo[0], pathProps.Corners, pathProps.Stroke.HalfThickness, pathProps.Stroke.TippedCornerLimit, tessellateOptions, verts, inds); } else { var pathEnding = pathProps.Head; // If pattern at the end will overlap with beginning, use a chopped ending to allow merging if (pathIt.Closed && rangeIndex == 0 && patternIt.IsSolidAt(pathIt.CurrentT) && patternIt.IsSolidAt(totalLength)) { pathEnding = PathEnding.Chop; } GenerateTip(VectorUtils.PathSegmentAtIndex(pathIt.Segments, pathIt.CurrentSegment), true, pathIt.CurrentT, pathEnding, pathProps.Stroke.HalfThickness, tessellateOptions, verts, inds); } float startingLength = pathIt.LengthSoFar; float unitsRemaining = Mathf.Min(tessellateOptions.StepDistance, distance); bool endedEntirePath = false; for (;;) { var result = pathIt.AdvanceBy(unitsRemaining, out unitsRemaining); if (result == PathDistanceForwardIterator.Result.Ended) { endedEntirePath = true; break; } else if (result == PathDistanceForwardIterator.Result.NewSegment) { if (joiningInfo[1] != null) { GenerateJoining(joiningInfo[1], pathProps.Corners, pathProps.Stroke.HalfThickness, pathProps.Stroke.TippedCornerLimit, tessellateOptions, verts, inds); } else { AddSegment(VectorUtils.PathSegmentAtIndex(pathIt.Segments, pathIt.CurrentSegment), pathIt.CurrentT, pathProps.Stroke.HalfThickness, null, pathIt.SegmentLengthSoFar, verts, inds); } HandleNewSegmentJoining(pathIt, patternIt, joiningInfo, pathProps.Stroke.HalfThickness, segmentLengths); } if ((unitsRemaining <= Epsilon) && !TryGetMoreRemainingUnits(ref unitsRemaining, pathIt, startingLength, distance, tessellateOptions.StepDistance)) { break; } if (result == PathDistanceForwardIterator.Result.Stepped) { AddSegment(VectorUtils.PathSegmentAtIndex(pathIt.Segments, pathIt.CurrentSegment), pathIt.CurrentT, pathProps.Stroke.HalfThickness, joiningInfo, pathIt.SegmentLengthSoFar, verts, inds); } } // Ending if (endedEntirePath && pathIt.Closed) { // No joining needed, the start and end of the path should just connect inds.Add(0); inds.Add(1); inds.Add((UInt16)(verts.Count - 2)); inds.Add((UInt16)(verts.Count - 1)); inds.Add((UInt16)(verts.Count - 2)); inds.Add(1); } else { AddSegment(VectorUtils.PathSegmentAtIndex(pathIt.Segments, pathIt.CurrentSegment), pathIt.CurrentT, pathProps.Stroke.HalfThickness, joiningInfo, pathIt.SegmentLengthSoFar, verts, inds); GenerateTip(VectorUtils.PathSegmentAtIndex(pathIt.Segments, pathIt.CurrentSegment), false, pathIt.CurrentT, pathProps.Tail, pathProps.Stroke.HalfThickness, tessellateOptions, verts, inds); } }