/// <summary>
        /// Add extruded point(s) based on two neighbouring line segments.
        /// </summary>
        /// <param name="linePointAndForwardSegment">A line point and its corresponding segment.</param>
        /// <param name="previousLinePointAndForwardSegment">The previous line point and its corresponding segment.</param>
        /// <param name="nextLinePoint">The next line point (since its parameter, uv etc will be needed).</param>
        /// <param name="lineSegmentIndexToAssign">Line segment index to assign the extruded point coming from.</param>
        /// <param name="extrusionAmount">The extrusion amount.</param>
        /// <param name="extrudedLinePoints">The list of all extruded line points.</param>
        /// <param name="findParameter">Delegate for determining altered point parameter.</param>
        private static void AddPointExtrusionFromPreviousNextSegments(LinePointUVAndSegment linePointAndForwardSegment, LinePointUVAndSegment previousLinePointAndForwardSegment, LinePointUV nextLinePoint, int lineSegmentIndexToAssign, float extrusionAmount, List <ExtrudedPointUV> extrudedLinePoints, GetShiftedScaledParameter findParameter)
        {
            double extrusionMagnitudeFractionFirstTangent;
            bool   segmentIntersection = LineSegmentUtil.DoNeighbouringSegmentTangentsFromCommonPointIntersect(previousLinePointAndForwardSegment.SegmentTangent, linePointAndForwardSegment.SegmentTangent, Mathf.Sign(extrusionAmount), out extrusionMagnitudeFractionFirstTangent);

            if (segmentIntersection)
            {
                //Concave section; expect intersection of neighbouring line segments around this point
                var  extrusionDistanceAlongTangents = Mathf.Abs((float)extrusionMagnitudeFractionFirstTangent * extrusionAmount);
                bool intersectionOccursOutOfSegment = extrusionDistanceAlongTangents >= Mathf.Min(previousLinePointAndForwardSegment.SegmentLength, linePointAndForwardSegment.SegmentLength);
                if (intersectionOccursOutOfSegment)
                {
                    AttemptAddNaiveExtrudedPointsWhenTheirExtrudedSegmentsDoNotIntersect(linePointAndForwardSegment, previousLinePointAndForwardSegment, lineSegmentIndexToAssign, extrusionAmount, extrudedLinePoints, findParameter, extrusionDistanceAlongTangents);
                }
                else
                {
                    var intersection = LineSegmentUtil.GetExtrudedSegmentNeighbouringIntersectionPoint(linePointAndForwardSegment.Point, previousLinePointAndForwardSegment.SegmentTangent, extrusionMagnitudeFractionFirstTangent, extrusionAmount);
                    var linePointOfThisIntersection = new LinePointUV(findParameter(linePointAndForwardSegment.Parameter), intersection, linePointAndForwardSegment.UV);
                    extrudedLinePoints.Add(new ExtrudedPointUV(linePointOfThisIntersection, lineSegmentIndexToAssign, lineSegmentIndexToAssign - 1));
                }
            }
            else
            {
                //Convex section, expect a circular fan of points around this point.
                RangeF rangeFromPreviousPoints = new RangeF(findParameter(previousLinePointAndForwardSegment.Parameter), findParameter(linePointAndForwardSegment.Parameter));
                RangeF rangeFromNextPoints     = new RangeF(findParameter(linePointAndForwardSegment.Parameter), findParameter(nextLinePoint.Parameter));
                RangeF parameterRangeToUse     = new RangeF(rangeFromPreviousPoints.FromFractionUnclamped(0.6f), rangeFromNextPoints.FromFractionUnclamped(0.4f));

                var   extrudedFromPreviousNormal     = linePointAndForwardSegment.Point + extrusionAmount * previousLinePointAndForwardSegment.SegmentNormal;
                var   extrudedFromNextNormal         = linePointAndForwardSegment.Point + extrusionAmount * linePointAndForwardSegment.SegmentNormal;
                var   diffBetweenExtrusion           = (extrudedFromNextNormal - extrudedFromPreviousNormal).magnitude;
                var   minNeighbouringSegmentDistance = Mathf.Min(previousLinePointAndForwardSegment.SegmentLength, linePointAndForwardSegment.SegmentLength);
                float absExtrusionAmount             = Mathf.Abs(extrusionAmount);
                var   comparisonDistance             = Mathf.Min(minNeighbouringSegmentDistance, absExtrusionAmount);

                var angleBetweenSegmentNormals = Mathf.Acos(Mathf.Clamp(Vector3.Dot(previousLinePointAndForwardSegment.SegmentNormal, linePointAndForwardSegment.SegmentNormal), -1f, 1f));

                int numPointsFromDistance = Mathf.CeilToInt(diffBetweenExtrusion / comparisonDistance);
                if (numPointsFromDistance <= 2)
                {
                    numPointsFromDistance++;
                }                                                           //NB Want at least the end-points, and if distance is large enough to need 2 naturally, 3 seems to looks better.
                int numPointsFromAngle = Mathf.CeilToInt(angleBetweenSegmentNormals / _convexExtrusionMinimumAngleRadians);
                int numPoints          = Mathf.Max(numPointsFromDistance, numPointsFromAngle);

                for (int i = 0; i < numPoints; i++)
                {
                    float fraction       = numPoints == 1 ? 0.5f : i / (numPoints - 1.0f);
                    var   averageTangent = previousLinePointAndForwardSegment.SegmentTangent.AverageDirectionVector(linePointAndForwardSegment.SegmentTangent, fraction);
                    var   averageNormal  = NormalUtil.NormalFromTangent(averageTangent);
                    var   extrudedVector = linePointAndForwardSegment.Point + averageNormal * extrusionAmount;
                    var   parameter      = parameterRangeToUse.FromFractionUnclamped(fraction);

                    Vector2 newUV = GetUVParameterOfFanPoint(linePointAndForwardSegment, previousLinePointAndForwardSegment, nextLinePoint, absExtrusionAmount, angleBetweenSegmentNormals, fraction);

                    var newLinePointInFan = new LinePointUV(parameter, extrudedVector, newUV);
                    extrudedLinePoints.Add(new ExtrudedPointUV(newLinePointInFan, lineSegmentIndexToAssign, lineSegmentIndexToAssign - 1));
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Average extruded points of a segment to an intersection point based on fraction of how far along the segment the intersection happened.
        /// Averages UVs directly.
        /// </summary>
        /// <param name="first">First point</param>
        /// <param name="second">Second point</param>
        /// <param name="fractionOfSecond">From of the section point to be used in the average</param>
        /// <param name="otherOriginalLineIndex1">Additional original line index to be referred to by the intersection point.</param>
        /// <param name="otherOriginalLineIndex2">Additional original line index to be referred to by the intersection point.</param>
        private static IntersectionPoint AverageExtrudedPointsToIntersection(ExtrudedPointUV first, ExtrudedPointUV second, float fractionOfSecond, int otherOriginalLineIndex1, int otherOriginalLineIndex2)
        {
            var averagedLinePoint         = new LinePointUV(first).AverageWith(new LinePointUV(second), fractionOfSecond);
            var extrudedPoint             = new ExtrudedPointUV(averagedLinePoint, first.LinePointSegmentIndex, second.LinePointSegmentIndex2);
            var extrudedPointOtherIndices = extrudedPoint;

            extrudedPointOtherIndices.LinePointSegmentIndex  = otherOriginalLineIndex1;
            extrudedPointOtherIndices.LinePointSegmentIndex2 = otherOriginalLineIndex2;
            return(new IntersectionPoint(extrudedPoint, extrudedPoint, extrudedPointOtherIndices));
        }
        /// <summary>
        /// Gets an extruded semicircular line cap at the start of a set of line points.
        /// </summary>
        /// <param name="linePointsAndSegments">List of line points with segments connecting to the next point in the line.</param>
        /// <param name="extrusionAmount">The extrusion amount.</param>
        /// <param name="parameterShift">The amount to shift the original line point parameters by when constructing the extruded points.</param>
        /// <param name="lineIsReversed">Whether the line segments have been reversed.</param>
        private static List <ExtrudedPointUV> GetExtrudedLineCap(List <LinePointUVAndSegment> linePointsAndSegments, float extrusionAmount, float parameterShift, bool lineIsReversed)
        {
            List <ExtrudedPointUV> lineCapPoints = new List <ExtrudedPointUV>();
            var startSegment = linePointsAndSegments[0];
            var normal       = startSegment.SegmentNormal;
            var minusTangent = -startSegment.SegmentTangent;

            var normalDeltaUV  = Vector2.up * (lineIsReversed ? -1f : 1f);
            var tangentDeltaUV = -Vector2.right * (lineIsReversed ? -1f : 1f);

            var comparisonDistance = startSegment.SegmentLength;
            var halfCircleDistance = Mathf.PI * extrusionAmount;
            int numPoints          = Mathf.CeilToInt(halfCircleDistance / comparisonDistance);
            int extrudedPointIndex = lineIsReversed ? linePointsAndSegments.Count : 0;

            if (numPoints < 5)
            {
                numPoints = 5;
            }
            else if (numPoints % 2 == 0)
            {
                //Make a more symmetrical cap.
                numPoints++;
            }

            for (int i = 0; i < numPoints; i++)
            {
                float fractionOrig      = (i + 1) / (float)(numPoints + 1);
                float fraction          = 1f - fractionOrig;
                var   avgNormal         = fraction <= 0.5f ? normal.AverageDirectionVector(minusTangent, fraction * 2f) : minusTangent.AverageDirectionVector(-normal, (fraction - 0.5f) * 2f);
                var   avgDeltaUVDir     = fraction <= 0.5f ? normalDeltaUV.AverageDirectionVector(tangentDeltaUV, fraction * 2f) : tangentDeltaUV.AverageDirectionVector(-normalDeltaUV, (fraction - 0.5f) * 2f);
                var   deltaUV           = new Vector2(avgDeltaUVDir.x * extrusionAmount, avgDeltaUVDir.y * 0.5f);
                var   extrudedVector    = startSegment.Point + avgNormal * extrusionAmount;
                var   newLinePointInFan = new LinePointUV(fractionOrig + parameterShift, extrudedVector, startSegment.UV + deltaUV);
                lineCapPoints.Add(new ExtrudedPointUV(newLinePointInFan, extrudedPointIndex, extrudedPointIndex));
            }
            return(lineCapPoints);
        }
        /// <summary>
        /// Converts a line point and uv in world coordinates into a Vector4 with the local point and uv packed into four components.
        /// </summary>
        /// <param name="linePointUV">The list of line points.</param>
        protected Vector4 LinePointUVToLocalVector4(LinePointUV linePointUV)
        {
            var localPoint = transform.InverseTransformPoint(linePointUV.Point);

            return(new Vector4(localPoint.x, localPoint.y, linePointUV.UV.x, linePointUV.UV.y));
        }
        /// <summary>
        /// Gets the extruded subspace of points with v parameter set appropriately to direction of hte line compared to extrusion.
        /// </summary>
        /// <param name="originalLineSegments">List of line points with segments connecting to the next point in the line.</param>
        /// <param name="lastLinePoint">The final line point.</param>
        /// <param name="extrusionAmount">The extrusion amount.</param>
        /// <param name="parameterShift">The amount to shift the original line point parameters by when constructing the extruded points.</param>
        /// <param name="isReversed">Whether line point indices associated with extruded points should be reversed.</param>
        private static List <ExtrudedPointUV> GetExtrudedSubspaceAndUvFromSegmentIntersections(List <LinePointUVAndSegment> originalLineSegments, LinePointUV lastLinePoint, float extrusionAmount, float parameterShift, bool isReversed)
        {
            var   extrudedSubspace = GetExtrudedSubspace_Points_SegmentIntersections(originalLineSegments, lastLinePoint, extrusionAmount, parameterShift, isReversed, includeLast: true);
            float uv_vparameter    = isReversed ? 0f : 1f;

            for (int i = 0; i < extrudedSubspace.Count; i++)
            {
                ExtrudedPointUV extrudedPointUV = extrudedSubspace[i];
                extrudedPointUV.UV  = new Vector2(extrudedSubspace[i].UV.x, uv_vparameter);
                extrudedSubspace[i] = extrudedPointUV;
            }
            return(extrudedSubspace);
        }
        /// <summary>
        /// Gets a set of line point and segments in reverse direction, with reversed parameters.
        /// </summary>
        /// <param name="originalLineSegments">List of line points with segments connecting to the next point in the line.</param>
        /// <param name="lastLinePoint">The final line point.</param>
        private static List <LinePointUVAndSegment> GetReversedSegmentsAndReversedParameters(List <LinePointUVAndSegment> originalLineSegments, LinePointUV lastLinePoint)
        {
            List <LinePointUVAndSegment> reversedSegments   = new List <LinePointUVAndSegment>();
            RangeF originalSegmentsParameterRange           = new RangeF(originalLineSegments[0].Parameter, lastLinePoint.Parameter);
            var    originalSegmentsParameterRangeMaxPlusMin = originalSegmentsParameterRange.Min + originalSegmentsParameterRange.Max;

            var endIndex = originalLineSegments.Count - 1;

            for (int i = endIndex; i >= 0; i--)
            {
                var originPoint           = i == endIndex ? lastLinePoint : originalLineSegments[i + 1].LinePoint;
                var currentSegmentForward = originalLineSegments[i];
                originPoint.Parameter = originalSegmentsParameterRangeMaxPlusMin - originPoint.Parameter;
                var segment = new LinePointUVAndSegment(originPoint, currentSegmentForward.Point, -currentSegmentForward.SegmentTangent, -currentSegmentForward.SegmentNormal, currentSegmentForward.SegmentLength);
                reversedSegments.Add(segment);
            }
            return(reversedSegments);
        }
        /// <summary>
        /// Determine the UV parameter of a fan point.
        /// </summary>
        /// <param name="linePointAndForwardSegment">A line point and its corresponding segment.</param>
        /// <param name="previousLinePointAndForwardSegment">The previous line point and its corresponding segment.</param>
        /// <param name="nextLinePoint">The next line point (since its parameter, uv etc will be needed).</param>
        /// <param name="absExtrusionAmount">Absolute value of the extrusion amount.</param>
        /// <param name="angleBetweenSegmentNormals">Angle between segment normals of <paramref name="previousLinePointAndForwardSegment"/> and <paramref name="linePointAndForwardSegment"/>.</param>
        /// <param name="fraction">Fraction along the fan.</param>
        private static Vector2 GetUVParameterOfFanPoint(LinePointUVAndSegment linePointAndForwardSegment, LinePointUVAndSegment previousLinePointAndForwardSegment, LinePointUV nextLinePoint, float absExtrusionAmount, float angleBetweenSegmentNormals, float fraction)
        {
            var  currentPointUv        = linePointAndForwardSegment.UV;
            bool isInPreviousHalfOfArc = fraction < 0.5f;
            var  fractionFromNeighbour = isInPreviousHalfOfArc ? fraction : 1f - fraction;
            var  fanArcLength          = angleBetweenSegmentNormals * absExtrusionAmount;

            var neighbouringSegmentHalfLength = 0.5f * (isInPreviousHalfOfArc ? previousLinePointAndForwardSegment.SegmentLength : linePointAndForwardSegment.SegmentLength);

            var distance          = neighbouringSegmentHalfLength + fractionFromNeighbour * fanArcLength;
            var distanceToHalfFan = neighbouringSegmentHalfLength + 0.5f * fanArcLength;
            var uvFractionFromNeighbouringHalfPoint = distance / distanceToHalfFan;

            var neighbouringPoint = isInPreviousHalfOfArc ? previousLinePointAndForwardSegment.LinePoint : nextLinePoint;
            var averageNeighbourAndCurrentPoint   = neighbouringPoint.AverageWith(linePointAndForwardSegment.LinePoint, 0.5f);
            var averageNeighbourAndCurrentPointUv = averageNeighbourAndCurrentPoint.UV;
            var newUV = averageNeighbourAndCurrentPointUv * (1f - uvFractionFromNeighbouringHalfPoint) + currentPointUv * uvFractionFromNeighbouringHalfPoint;

            return(newUV);
        }
        /// <summary>
        /// Gets the points corresponding to the extruded subspace due to extruded segment-by-segment intersection.
        /// </summary>
        /// <param name="linePointsAndForwardSegments">List of line points with segments connecting to the next point in the line.</param>
        /// <param name="lastLinePoint">The final line point.</param>
        /// <param name="extrusionAmount">The extrusion amount.</param>
        /// <param name="parameterShift">The amount to shift the original line point parameters by when constructing the extruded points.</param>
        /// <param name="reverseIndices">Whether line point indices associated with extruded points should be reversed.</param>
        /// <param name="includeLast">If final segment should be included in the extrusion.</param>
        private static List <ExtrudedPointUV> GetExtrudedSubspace_Points_SegmentIntersections(List <LinePointUVAndSegment> linePointsAndForwardSegments, LinePointUV lastLinePoint, float extrusionAmount, float parameterShift, bool reverseIndices, bool includeLast)
        {
            var extrudedLinePoints = new List <ExtrudedPointUV>();

            GetShiftedScaledParameter findParameter = (originalPointParameter) => parameterShift + originalPointParameter;

            var numSegments = linePointsAndForwardSegments.Count;

            if (numSegments >= 2)
            {
                int endCount = includeLast ? numSegments : numSegments - 1;
                for (int i = 0; i <= endCount; i++)
                {
                    if (i == 0)
                    {
                        int lineSegmentIndex    = 0;
                        var linePointAndSegment = linePointsAndForwardSegments[lineSegmentIndex];
                        int extrudedPointIndexOnOriginalLine = reverseIndices ? numSegments : 0;

                        var extrudedStartPoint = new ExtrudedPointUV(linePointAndSegment.LinePoint, findParameter(linePointAndSegment.Parameter), linePointAndSegment.SegmentNormal, extrusionAmount, extrudedPointIndexOnOriginalLine);
                        extrudedLinePoints.Add(extrudedStartPoint);
                    }
                    else if (i == numSegments)
                    {
                        int lineSegmentIndex                 = numSegments - 1;
                        var lastLinePointAndSegment          = linePointsAndForwardSegments[lineSegmentIndex];
                        int extrudedPointIndexOnOriginalLine = reverseIndices ? 0 : numSegments;

                        var extrudedEndPoint = new ExtrudedPointUV(lastLinePoint, findParameter(lastLinePoint.Parameter), lastLinePointAndSegment.SegmentNormal, extrusionAmount, extrudedPointIndexOnOriginalLine);
                        extrudedLinePoints.Add(extrudedEndPoint);
                    }
                    else
                    {
                        var indexToAssign       = reverseIndices ? numSegments - i : i;
                        var linePointAndSegment = linePointsAndForwardSegments[i];
                        var nextLinePoint       = i + 1 < numSegments ? linePointsAndForwardSegments[i + 1].LinePoint : lastLinePoint;
                        AddPointExtrusionFromPreviousNextSegments(linePointAndSegment, linePointsAndForwardSegments[i - 1], nextLinePoint, indexToAssign, extrusionAmount, extrudedLinePoints, findParameter);
                    }
                }
            }
            return(extrudedLinePoints);
        }