void CalculateVertices() { if (!_areVertsDirty || _points.Length < 2) { return; } _areVertsDirty = false; _indices.Reset(); _vertices.Reset(); var maxX = float.MinValue; var minX = float.MaxValue; var maxY = float.MinValue; var minY = float.MaxValue; if (_useStartEndWidths) { _maxWidth = System.Math.Max(_startWidth, _endWidth); } // calculate line length first and simulataneously get our min/max points for the bounds var lineLength = 0f; var halfMaxWidth = _maxWidth * 0.5f; _points.Buffer[0].LengthFromPreviousPoint = 0; for (var i = 0; i < _points.Length - 1; i++) { var distance = Vector2.Distance(_points.Buffer[i].Position, _points.Buffer[i + 1].Position); _points.Buffer[i + 1].LengthFromPreviousPoint = distance; lineLength += distance; maxX = Mathf.MaxOf(maxX, _points.Buffer[i].Position.X + halfMaxWidth, _points.Buffer[i + 1].Position.X + halfMaxWidth); minX = Mathf.MinOf(minX, _points.Buffer[i].Position.X - halfMaxWidth, _points.Buffer[i + 1].Position.X - halfMaxWidth); maxY = Mathf.MaxOf(maxY, _points.Buffer[i].Position.Y + halfMaxWidth, _points.Buffer[i + 1].Position.Y + halfMaxWidth); minY = Mathf.MinOf(minY, _points.Buffer[i].Position.Y - halfMaxWidth, _points.Buffer[i + 1].Position.Y - halfMaxWidth); } _bounds.X = minX; _bounds.Y = minY; _bounds.Width = maxX - minX; _bounds.Height = maxY - minY; // special case: single segment if (_points.Length == 2) { if (_useStartEndWidths) { _points.Buffer[0].Width = _startWidth; _points.Buffer[1].Width = _endWidth; } if (_useStartEndColors) { _points.Buffer[0].Color = _startColor; _points.Buffer[1].Color = _endColor; } _firstSegment.SetPoints(ref _points.Buffer[0], ref _points.Buffer[1]); AddSingleSegmentLine(ref _firstSegment, _points.Buffer[1].Color); return; } var distanceSoFar = 0f; var fusedPoint = Vector2.Zero; var vertIndex = 0; var thirdPoint = new SegmentPoint(); for (var i = 0; i < _points.Length - 1; i++) { var firstPoint = _points.Buffer[i]; var secondPoint = _points.Buffer[i + 1]; var hasThirdPoint = _points.Length > i + 2; if (hasThirdPoint) { thirdPoint = _points.Buffer[i + 2]; } // we need the distance along the line of both the first and second points. distanceSoFar will always be for the furthest point // which is the previous point before adding the current segment distance. var firstPointDistance = distanceSoFar; distanceSoFar += secondPoint.LengthFromPreviousPoint; var firstPointRatio = firstPointDistance / lineLength; var secondPointRatio = distanceSoFar / lineLength; var thirdPointRatio = 0f; if (hasThirdPoint) { thirdPointRatio = (distanceSoFar + thirdPoint.LengthFromPreviousPoint) / lineLength; } if (_useStartEndColors) { ColorExt.Lerp(ref _startColor, ref _endColor, out firstPoint.Color, firstPointRatio); ColorExt.Lerp(ref _startColor, ref _endColor, out secondPoint.Color, secondPointRatio); if (hasThirdPoint) { ColorExt.Lerp(ref _startColor, ref _endColor, out thirdPoint.Color, thirdPointRatio); } } if (_useStartEndWidths) { firstPoint.Width = Mathf.Lerp(_startWidth, _endWidth, firstPointRatio); secondPoint.Width = Mathf.Lerp(_startWidth, _endWidth, secondPointRatio); if (hasThirdPoint) { thirdPoint.Width = Mathf.Lerp(_startWidth, _endWidth, thirdPointRatio); } } if (i == 0) { _firstSegment.SetPoints(ref firstPoint, ref secondPoint); _secondSegment.SetPoints(ref secondPoint, ref thirdPoint); } else { Utils.Swap(ref _firstSegment, ref _secondSegment); if (hasThirdPoint) { _secondSegment.SetPoints(ref secondPoint, ref thirdPoint); } } // dont recalculate the fusedPoint for the last segment since there will be no third point to work with if (hasThirdPoint) { var shouldFuseBottom = Vector2Ext.IsTriangleCCW(firstPoint.Position, secondPoint.Position, thirdPoint.Position); _secondSegment.SetFusedData(shouldFuseBottom, ref _firstSegment); } // special care needs to be take with the first segment since it has a different vert count if (i == 0) { AddFirstSegment(ref _firstSegment, ref _secondSegment, ref vertIndex); } else { AddSegment(ref _firstSegment, ref vertIndex); } _lastSegment.CloneFrom(ref _firstSegment); } }
/// <summary> /// Computes a triangle list that fully covers the area enclosed by the given set of points. If points are not CCW, pass false for /// the arePointsCCW parameter /// </summary> /// <param name="points">A list of points that defines an enclosing path.</param> /// <param name="count">The number of points in the path.</param> public void Triangulate(Vector2[] points, bool arePointsCCW = true) { var count = points.Length; // set up previous and next links to effectively from a double-linked vert list Initialize(count); // loop breaker for polys that are not triangulatable var iterations = 0; // start at vert 0 var index = 0; // keep removing verts until just a triangle is left while (count > 3 && iterations < 500) { iterations++; // test if current vert is an ear var isEar = true; var a = points[_triPrev[index]]; var b = points[index]; var c = points[_triNext[index]]; // an ear must be convex (here counterclockwise) if (Vector2Ext.IsTriangleCCW(a, b, c)) { // loop over all verts not part of the tentative ear var k = _triNext[_triNext[index]]; do { // if vert k is inside the ear triangle, then this is not an ear if (TestPointTriangle(points[k], a, b, c)) { isEar = false; break; } k = _triNext[k]; } while (k != _triPrev[index]); } else { // the ear triangle is clockwise so points[i] is not an ear isEar = false; } // if current vert is an ear, delete it and visit the previous vert if (isEar) { // triangle is an ear TriangleIndices.Add(_triPrev[index]); TriangleIndices.Add(index); TriangleIndices.Add(_triNext[index]); // delete vert by redirecting next and previous links of neighboring verts past it // decrement vertext count _triNext[_triPrev[index]] = _triNext[index]; _triPrev[_triNext[index]] = _triPrev[index]; count--; // visit the previous vert next index = _triPrev[index]; } else { // current vert is not an ear. visit the next vert index = _triNext[index]; } } // output the final triangle TriangleIndices.Add(_triPrev[index]); TriangleIndices.Add(index); TriangleIndices.Add(_triNext[index]); if (!arePointsCCW) { TriangleIndices.Reverse(); } }