public static List<List<Vector2>> CreateStroke(List<Vector2> inputShapes, SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) { if(inputShapes == null || inputShapes.Count == 0 || paintable == null || paintable.strokeWidth <= 0f) return null; return CreateStroke(new List<List<Vector2>>(){inputShapes}, paintable, closePath); }
public static Mesh CreateStrokeSimple(SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) { return(CreateStrokeSimple(new List <List <Vector2> >() { new List <Vector2>(SVGGraphics.position_buffer.ToArray()) }, paintable, closePath)); }
public static Mesh StrokeMesh(List<StrokeSegment[]> segments, float thickness, Color32 color, StrokeLineJoin lineJoin, StrokeLineCap lineCap, float miterLimit = 4f, float[] dashArray = null, float dashOffset = 0f, ClosePathRule closeLine = ClosePathRule.NEVER, float roundQuality = 10f) { List<List<Vector2>> finalPoints = StrokeShape(segments, thickness, color, lineJoin, lineCap, miterLimit, dashArray, dashOffset, closeLine, roundQuality); return TessellateStroke(finalPoints, color); }
public static Mesh CreateAntialiasing(List <List <Vector2> > paths, Color color, float antialiasingWidth, bool isStroke = false, ClosePathRule closePath = ClosePathRule.NEVER) { if (SVGAssetImport.antialiasingWidth <= 0f) { return(null); } return(SVGSimplePath.CreateAntialiasing(paths, color, antialiasingWidth, closePath)); }
public static List<List<Vector2>> StrokeShape(List<StrokeSegment[]> segments, float thickness, Color32 color, StrokeLineJoin lineJoin, StrokeLineCap lineCap, float miterLimit = 4f, float[] dashArray = null, float dashOffset = 0f, ClosePathRule closeLine = ClosePathRule.NEVER, float roundQuality = 10f) { if(segments == null || segments.Count == 0) return null; float totalCurveLength = 0f; int i, j; for(i = 0; i < segments.Count; i++) { if(segments[i] == null) continue; for(j = 0; j < segments[i].Length; j++) { totalCurveLength += segments[i][j].length; } } if(totalCurveLength == 0f) return null; bool useDash; ProcessDashArray(ref dashArray, out useDash); ClosePathRule closeSegments = closeLine; List<StrokeSegment[]> finalSegments = new List<StrokeSegment[]>(); for(i = 0; i < segments.Count; i++) { if(segments[i] == null || segments[i].Length == 0) continue; if(!useDash) { finalSegments.Add(segments[i]); } else { finalSegments.AddRange(CreateDashedStroke(segments[i], dashArray, dashOffset, ref closeSegments)); } } if(finalSegments.Count > 0) { List<List<Vector2>> finalPoints = new List<List<Vector2>>(); for(i = 0; i < finalSegments.Count; i++) { List<Vector2> points = Stroke(finalSegments[i], thickness, color, lineJoin, lineCap, miterLimit, closeSegments, roundQuality); if(points == null || points.Count < 2) continue; finalPoints.Add(points); } return finalPoints; } else { return null; } }
public static Mesh CreateStrokeSimple(List <List <Vector2> > inputShapes, SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) { if (inputShapes == null || inputShapes.Count == 0 || paintable == null || paintable.strokeWidth <= 0f) { return(null); } AddInputShape(inputShapes); Color color = GetStrokeColor(paintable); float strokeWidth = paintable.strokeWidth; if (inputShapes.Count > 1) { CombineInstance[] combineInstances = new CombineInstance[inputShapes.Count]; for (int i = 0; i < inputShapes.Count; i++) { combineInstances[i] = new CombineInstance(); combineInstances[i].mesh = SVGMeshUtils.VectorLine(inputShapes[i].ToArray(), color, color, strokeWidth, 0f, closePath); } Mesh mesh = new Mesh(); mesh.CombineMeshes(combineInstances, true, false); return(mesh); } else { return(SVGMeshUtils.VectorLine(inputShapes[0].ToArray(), color, color, strokeWidth, 0f, closePath)); } }
public static Mesh CreateAntialiasing(List <List <Vector2> > inputShapes, Color colorA, float width, ClosePathRule closePath = ClosePathRule.NEVER) { if (inputShapes == null || inputShapes.Count == 0) { return(null); } Color colorB = new Color(colorA.r, colorA.g, colorA.b, 0f); if (inputShapes.Count > 1) { CombineInstance[] combineInstances = new CombineInstance[inputShapes.Count]; for (int i = 0; i < inputShapes.Count; i++) { combineInstances[i] = new CombineInstance(); combineInstances[i].mesh = SVGMeshUtils.VectorLine(inputShapes[i].ToArray(), colorA, colorB, width, width * 0.5f, closePath); } Mesh mesh = new Mesh(); mesh.CombineMeshes(combineInstances, true, false); return(mesh); } else { return(SVGMeshUtils.VectorLine(inputShapes[0].ToArray(), colorA, colorB, width, width * 0.5f, closePath)); } }
public static List <Vector2> Stroke(StrokeSegment[] segments, float thickness, Color32 color, StrokeLineJoin lineJoin, StrokeLineCap lineCap, float miterLimit = 4f, ClosePathRule closeLine = ClosePathRule.NEVER, float roundQuality = 10f) { if (segments == null || segments.Length == 0) { return(null); } if (segments.Length == 1) { closeLine = ClosePathRule.NEVER; } else if (closeLine == ClosePathRule.AUTO) { if (segments[0].startPoint == segments[segments.Length - 1].endPoint) { closeLine = ClosePathRule.ALWAYS; } else { closeLine = ClosePathRule.NEVER; } } if (segments[0].startPoint == segments[segments.Length - 1].endPoint) { List <StrokeSegment> tempSegments = new List <StrokeSegment>(segments); tempSegments.RemoveAt(tempSegments.Count - 1); segments = tempSegments.ToArray(); } List <Vector2> innerPoints = new List <Vector2>(); List <Vector2> outerPoints = new List <Vector2>(); if (closeLine == ClosePathRule.ALWAYS) { List <StrokeSegment> tempSegments = new List <StrokeSegment>(segments); tempSegments.Add(new StrokeSegment(segments[segments.Length - 1].endPoint, segments[0].startPoint)); tempSegments.Add(new StrokeSegment(segments[0].startPoint, segments[0].endPoint)); segments = tempSegments.ToArray(); } miterLimit = (miterLimit - 1f) * thickness * 2f; if (miterLimit < 1f) { miterLimit = 1f; } int i, i1, j, segmentsLength = segments.Length; float segmentsAngle, segmentsAngleRotated, halfWidth = thickness * 0.5f, angleProgress, radAngle = 0f, miterClipHalf = miterLimit * 0.5f, miterClipHalfDouble = miterClipHalf * miterClipHalf; Vector2 segmentLeftStartA, segmentLeftEndA = Vector2.zero, segmentRightStartA, segmentRightEndA = Vector2.zero, segmentLeftEndB, segmentRightEndB, intersectionLeft, intersectionRight, segmentStartCenter; Matrix4x4 rotationMatrix = Matrix4x4.TRS(Vector2.zero, Quaternion.Euler(0f, 0f, 90f), Vector2.one); if (lineCap == StrokeLineCap.butt || closeLine == ClosePathRule.ALWAYS) { innerPoints.AddRange(new Vector2[] { segments[0].startPoint - segments[0].directionNormalizedRotated * halfWidth, segments[0].startPoint + segments[0].directionNormalizedRotated * halfWidth, }); } else if (lineCap == StrokeLineCap.round) { segmentStartCenter = Vector2.Lerp(segments[0].startPoint - segments[0].directionNormalizedRotated * halfWidth, segments[0].startPoint + segments[0].directionNormalizedRotated * halfWidth, 0.5f); radAngle = Mathf.Atan2(segments[0].directionNormalizedRotated.y, segments[0].directionNormalizedRotated.x); float roundSegmentsfAlt = roundQuality * thickness; float roundSegmentsfAltMinusOne = roundSegmentsfAlt - 1; if (roundSegmentsfAltMinusOne > 0) { for (j = 0; j <= roundSegmentsfAlt; j++) { angleProgress = 1f - Mathf.Clamp01(j / roundSegmentsfAltMinusOne); innerPoints.Add(segmentStartCenter + new Vector2(Mathf.Cos(radAngle + angleProgress * Mathf.PI) * halfWidth, Mathf.Sin(radAngle + angleProgress * Mathf.PI) * halfWidth)); } } innerPoints.AddRange(new Vector2[] { segments[0].startPoint + segments[0].directionNormalizedRotated * halfWidth, }); outerPoints.AddRange(new Vector2[] { segments[0].startPoint - segments[0].directionNormalizedRotated * halfWidth, }); } else if (lineCap == StrokeLineCap.square) { innerPoints.AddRange(new Vector2[] { segments[0].startPoint - segments[0].directionNormalized * halfWidth - segments[0].directionNormalizedRotated * halfWidth, segments[0].startPoint - segments[0].directionNormalized * halfWidth + segments[0].directionNormalizedRotated * halfWidth, }); } if (segmentsLength > 1) { for (i = 1; i < segmentsLength; i++) { i1 = i - 1; /* * if(segments[i1].length == 0f || segments[i].length == 0) * { * continue; * } */ segmentsAngle = Vector2.Dot(segments[i].directionNormalized, segments[i1].directionNormalized); segmentsAngleRotated = Vector2.Dot(segments[i].directionNormalized, segments[i1].directionNormalizedRotated); float miterLength = (1f / Mathf.Sin((Mathf.PI - Mathf.Acos(segmentsAngle)) * 0.5f)) * thickness; float miterLengthHalf = miterLength * 0.5f; Vector2 miterVector = Vector2.Lerp(segments[i1].directionNormalizedRotated, segments[i].directionNormalizedRotated, 0.5f).normalized; Vector2 miterVectorLengthHalf = miterVector * miterLengthHalf; Vector2 miterVectorRotated = rotationMatrix.MultiplyVector(miterVector); segmentLeftStartA = segments[i].startPoint - segments[i].directionNormalizedRotated * halfWidth; segmentLeftEndA = segments[i].endPoint - segments[i].directionNormalizedRotated * halfWidth; segmentRightStartA = segments[i].startPoint + segments[i].directionNormalizedRotated * halfWidth; segmentRightEndA = segments[i].endPoint + segments[i].directionNormalizedRotated * halfWidth; //segmentLeftStartB = segments[i1].startPoint - segments[i1].directionNormalizedRotated * halfWidth; segmentLeftEndB = segments[i1].endPoint - segments[i1].directionNormalizedRotated * halfWidth; //segmentRightStartB = segments[i1].startPoint + segments[i1].directionNormalizedRotated * halfWidth; segmentRightEndB = segments[i1].endPoint + segments[i1].directionNormalizedRotated * halfWidth; if (lineJoin == StrokeLineJoin.miter) { if (miterLimit < miterLength) { lineJoin = StrokeLineJoin.bevel; } } if (lineJoin == StrokeLineJoin.miter || lineJoin == StrokeLineJoin.miterClip) { if (segmentsAngle == 1f || segmentsAngle == -1f) { innerPoints.AddRange(new Vector2[] { segmentRightEndB, segmentRightStartA, }); outerPoints.AddRange(new Vector2[] { segmentLeftEndB, segmentLeftStartA, }); } else { if (segmentsAngleRotated < 0) { if (miterLimit <= miterLength) { Vector2 a = segments[i1].endPoint + miterVector * miterClipHalf; Vector2 b = segments[i1].endPoint + miterVectorLengthHalf; Vector2 c = a + miterVectorRotated; SVGMath.LineLineIntersection(out intersectionLeft, b, segmentRightEndB, a, c); SVGMath.LineLineIntersection(out intersectionRight, b, segmentRightStartA, a, c); if (miterClipHalfDouble <= (Vector2.Lerp(segmentRightEndB, segmentRightStartA, 0.5f) - segments[i1].endPoint).sqrMagnitude) { intersectionLeft = segmentRightEndB; intersectionRight = segmentRightStartA; } innerPoints.AddRange(new Vector2[] { intersectionLeft, intersectionRight, }); outerPoints.AddRange(new Vector2[] { segmentLeftEndB, segmentLeftStartA, }); } else { intersectionRight = segments[i1].endPoint + miterVectorLengthHalf; innerPoints.AddRange(new Vector2[] { intersectionRight, }); outerPoints.AddRange(new Vector2[] { segmentLeftEndB, segmentLeftStartA, }); } } else { if (miterLimit <= miterLength) { Vector2 a = segments[i1].endPoint - miterVector * miterClipHalf; Vector2 b = segments[i1].endPoint - miterVectorLengthHalf; Vector2 c = a + miterVectorRotated; SVGMath.LineLineIntersection(out intersectionLeft, b, segmentLeftStartA, a, c); SVGMath.LineLineIntersection(out intersectionRight, b, segmentLeftEndB, a, c); if (miterClipHalfDouble <= (Vector2.Lerp(segmentLeftStartA, segmentLeftEndB, 0.5f) - segments[i1].endPoint).sqrMagnitude) { intersectionLeft = segmentLeftStartA; intersectionRight = segmentLeftEndB; } outerPoints.AddRange(new Vector2[] { intersectionRight, intersectionLeft, }); innerPoints.AddRange(new Vector2[] { segmentRightEndB, segmentRightStartA, }); } else { intersectionLeft = segments[i1].endPoint - miterVectorLengthHalf; outerPoints.AddRange(new Vector2[] { intersectionLeft, }); innerPoints.AddRange(new Vector2[] { segmentRightEndB, segmentRightStartA, }); } } } } else if (lineJoin == StrokeLineJoin.bevel) { innerPoints.AddRange(new Vector2[] { segmentRightEndB, segmentRightStartA, }); outerPoints.AddRange(new Vector2[] { segmentLeftEndB, segmentLeftStartA, }); } else if (lineJoin == StrokeLineJoin.round) { if (segmentsAngle == 1f) { innerPoints.AddRange(new Vector2[] { segmentRightEndB, segmentRightStartA, }); outerPoints.AddRange(new Vector2[] { segmentLeftEndB, segmentLeftStartA, }); } else { if (segmentsAngleRotated < 0) { innerPoints.AddRange(new Vector2[] { segmentRightEndB, }); outerPoints.AddRange(new Vector2[] { segmentLeftEndB, segmentLeftStartA, }); segmentStartCenter = segments[i].startPoint; Vector2 dir = segments[i1].directionNormalizedRotated; float angle = Mathf.Acos(Vector2.Dot(segments[i1].directionNormalized, segments[i].directionNormalized)); radAngle = Mathf.Atan2(dir.y, dir.x); float roundSegmentsfAlt = roundQuality * thickness * (Mathf.Acos(segmentsAngle) / Mathf.PI); if (roundSegmentsfAlt < 1) { roundSegmentsfAlt = 1f; } float roundSegmentsfAltMinusOne = roundSegmentsfAlt; if (roundSegmentsfAltMinusOne > 0) { for (j = 0; j < roundSegmentsfAlt; j++) { angleProgress = Mathf.Clamp01(j / roundSegmentsfAltMinusOne); innerPoints.Add(segmentStartCenter + new Vector2(Mathf.Cos(radAngle - angleProgress * angle) * halfWidth, Mathf.Sin(radAngle - angleProgress * angle) * halfWidth)); } } innerPoints.AddRange(new Vector2[] { segmentRightStartA, }); } else { innerPoints.AddRange(new Vector2[] { segmentRightEndB, segmentRightStartA, }); outerPoints.AddRange(new Vector2[] { segmentLeftEndB, }); segmentStartCenter = segments[i].startPoint; Vector2 dir = -segments[i].directionNormalizedRotated; float angle = Mathf.Acos(Vector2.Dot(segments[i1].directionNormalized, segments[i].directionNormalized)); radAngle = Mathf.Atan2(dir.y, dir.x); float roundSegmentsfAlt = roundQuality * thickness * (Mathf.Acos(segmentsAngle) / Mathf.PI); if (roundSegmentsfAlt < 1) { roundSegmentsfAlt = 1f; } float roundSegmentsfAltMinusOne = roundSegmentsfAlt; if (roundSegmentsfAltMinusOne > 0) { for (j = 0; j < roundSegmentsfAlt; j++) { angleProgress = Mathf.Clamp01(1f - (j / roundSegmentsfAltMinusOne)); outerPoints.Add(segmentStartCenter + new Vector2(Mathf.Cos(radAngle - angleProgress * angle) * halfWidth, Mathf.Sin(radAngle - angleProgress * angle) * halfWidth)); } } outerPoints.AddRange(new Vector2[] { segmentLeftStartA, }); } } } } } int lastSegmentIndex = segments.Length - 1; segmentLeftStartA = segments[lastSegmentIndex].startPoint - segments[lastSegmentIndex].directionNormalizedRotated * halfWidth; segmentLeftEndA = segments[lastSegmentIndex].endPoint - segments[lastSegmentIndex].directionNormalizedRotated * halfWidth; segmentRightStartA = segments[lastSegmentIndex].startPoint + segments[lastSegmentIndex].directionNormalizedRotated * halfWidth; segmentRightEndA = segments[lastSegmentIndex].endPoint + segments[lastSegmentIndex].directionNormalizedRotated * halfWidth; if (closeLine == ClosePathRule.NEVER) { if (lineCap == StrokeLineCap.butt) { innerPoints.AddRange(new Vector2[] { segmentRightEndA, segmentLeftEndA, }); } else if (lineCap == StrokeLineCap.round) { innerPoints.AddRange(new Vector2[] { segmentRightEndA }); outerPoints.AddRange(new Vector2[] { segmentLeftEndA, }); segmentStartCenter = Vector2.Lerp(segmentLeftEndA, segmentRightEndA, 0.5f); radAngle = Mathf.Atan2(-segments[lastSegmentIndex].directionNormalizedRotated.y, -segments[lastSegmentIndex].directionNormalizedRotated.x); float roundSegmentsfAlt = roundQuality * thickness; float roundSegmentsfAltMinusOne = roundSegmentsfAlt - 1; if (roundSegmentsfAltMinusOne > 0) { for (j = 0; j <= roundSegmentsfAlt; j++) { angleProgress = 1f - Mathf.Clamp01(j / roundSegmentsfAltMinusOne); innerPoints.Add(segmentStartCenter + new Vector2(Mathf.Cos(radAngle + angleProgress * Mathf.PI) * halfWidth, Mathf.Sin(radAngle + angleProgress * Mathf.PI) * halfWidth)); } } } else if (lineCap == StrokeLineCap.square) { Vector2 lastSegmentOffset = segments[lastSegmentIndex].directionNormalized * halfWidth; innerPoints.AddRange(new Vector2[] { segmentRightEndA + lastSegmentOffset, segmentLeftEndA + lastSegmentOffset, }); } } if (closeLine == ClosePathRule.ALWAYS && lineJoin == StrokeLineJoin.miter || lineJoin == StrokeLineJoin.miterClip) { innerPoints.AddRange(new Vector2[] { segmentRightEndA, segmentLeftEndA, }); } outerPoints.Reverse(); innerPoints.AddRange(outerPoints); return(innerPoints); }
public static List <List <Vector2> > StrokeShape(List <StrokeSegment[]> segments, float thickness, Color32 color, StrokeLineJoin lineJoin, StrokeLineCap lineCap, float miterLimit = 4f, float[] dashArray = null, float dashOffset = 0f, ClosePathRule closeLine = ClosePathRule.NEVER, float roundQuality = 10f) { if (segments == null || segments.Count == 0) { return(null); } float totalCurveLength = 0f; int i, j; for (i = 0; i < segments.Count; i++) { if (segments[i] == null) { continue; } for (j = 0; j < segments[i].Length; j++) { totalCurveLength += segments[i][j].length; } } if (totalCurveLength == 0f) { return(null); } bool useDash; ProcessDashArray(ref dashArray, out useDash); ClosePathRule closeSegments = closeLine; List <StrokeSegment[]> finalSegments = new List <StrokeSegment[]>(); for (i = 0; i < segments.Count; i++) { if (segments[i] == null || segments[i].Length == 0) { continue; } if (!useDash) { finalSegments.Add(segments[i]); } else { finalSegments.AddRange(CreateDashedStroke(segments[i], dashArray, dashOffset, ref closeSegments)); } } if (finalSegments.Count > 0) { List <List <Vector2> > finalPoints = new List <List <Vector2> >(); for (i = 0; i < finalSegments.Count; i++) { List <Vector2> points = Stroke(finalSegments[i], thickness, color, lineJoin, lineCap, miterLimit, closeSegments, roundQuality); if (points == null || points.Count < 2) { continue; } List <List <Vector2> > simplifiedShape = SVGGeom.SimplifyPolygon(points); for (j = 0; j < simplifiedShape.Count; j++) { if (simplifiedShape[j] == null || simplifiedShape[j].Count == 0) { continue; } finalPoints.Add(simplifiedShape[j]); } } return(finalPoints); } else { return(null); } }
public static Mesh CreateAntialiasing(List<List<Vector2>> paths, Color color, float antialiasingWidth, bool isStroke = false, ClosePathRule closePath = ClosePathRule.NEVER) { if(SVGAssetImport.antialiasingWidth <= 0f) return null; return SVGSimplePath.CreateAntialiasing(paths, color, antialiasingWidth, closePath); }
public static List <List <Vector2> > CreateStroke(List <List <Vector2> > inputShapes, SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) { if (inputShapes == null || inputShapes.Count == 0 || paintable == null || paintable.strokeWidth <= 0f) { return(null); } List <StrokeSegment[]> segments = new List <StrokeSegment[]>(); for (int i = 0; i < inputShapes.Count; i++) { if (inputShapes[i] == null || inputShapes[i].Count < 2) { continue; } segments.Add(GetSegments(inputShapes[i])); } return(SVGLineUtils.StrokeShape(segments, paintable.strokeWidth, Color.black, GetStrokeLineJoin(paintable.strokeLineJoin), GetStrokeLineCap(paintable.strokeLineCap), paintable.miterLimit, paintable.dashArray, paintable.dashOffset, closePath, SVGGraphics.roundQuality)); }
public static Mesh CreateStrokeSimple(List<List<Vector2>> inputShapes, SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) { if(inputShapes == null || inputShapes.Count == 0 || paintable == null || paintable.strokeWidth <= 0f) return null; AddInputShape(inputShapes); Color color = GetStrokeColor(paintable); float strokeWidth = paintable.strokeWidth; if(inputShapes.Count > 1) { CombineInstance[] combineInstances = new CombineInstance[inputShapes.Count]; for(int i = 0; i < inputShapes.Count; i++) { combineInstances[i] = new CombineInstance(); combineInstances[i].mesh = SVGMeshUtils.VectorLine(inputShapes[i].ToArray(), color, color, strokeWidth, 0f, closePath); } Mesh mesh = new Mesh(); mesh.CombineMeshes(combineInstances, true, false); return mesh; } else { return SVGMeshUtils.VectorLine(inputShapes[0].ToArray(), color, color, strokeWidth, 0f, closePath); } }
public static Mesh CreateAntialiasing(List<List<Vector2>> inputShapes, Color colorA, float width, ClosePathRule closePath = ClosePathRule.NEVER) { if(inputShapes == null || inputShapes.Count == 0) return null; Color colorB = new Color(colorA.r, colorA.g, colorA.b, 0f); if(inputShapes.Count > 1) { CombineInstance[] combineInstances = new CombineInstance[inputShapes.Count]; for(int i = 0; i < inputShapes.Count; i++) { combineInstances[i] = new CombineInstance(); combineInstances[i].mesh = SVGMeshUtils.VectorLine(inputShapes[i].ToArray(), colorA, colorB, width, width * 0.5f, closePath); } Mesh mesh = new Mesh(); mesh.CombineMeshes(combineInstances, true, false); return mesh; } else { return SVGMeshUtils.VectorLine(inputShapes[0].ToArray(), colorA, colorB, width, width * 0.5f, closePath); } }
public static Mesh CreateStrokeSimple(SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) { return CreateStrokeSimple(new List<List<Vector2>>(){new List<Vector2>(SVGGraphics.position_buffer.ToArray())}, paintable, closePath); }
public static Mesh VectorLine(Vector2[] positions, Color32 colorA, Color32 colorB, float size, float offset, ClosePathRule closeLine = ClosePathRule.NEVER) { if (positions == null) { #if DEBUG Debug.LogWarning("Line / Array is Null!"); #endif return null; } if (positions.Length < 2) { #if DEBUG Debug.LogWarning("Line / We need at least 2 positions!"); #endif return null; } if(positions.Length == 2) { closeLine = ClosePathRule.NEVER; } else { if(closeLine == ClosePathRule.AUTO) { if(positions[0] == positions[positions.Length - 1]) closeLine = ClosePathRule.ALWAYS; } } SVGLineData lineData = new SVGLineData(positions); lineData.UpdateAll(); size *= 0.5f; int _vertexCount = positions.Length * 2; int _positionsLength = positions.Length; int totalTriangles = (_positionsLength - 1) * (2 - 1) * 6; int positionIndex = 0, verticeIndex = 0; List<Vector3> _vertices = new List<Vector3>(_vertexCount); List<int> _triangles = new List<int>(totalTriangles); List<Color32> _colors = new List<Color32>(_vertexCount); Vector2 previousPosition; Vector2 currentPosition; Vector2 nextPosition; Vector2 currentEdge, nextEdge; float magnitude = 0f, totalCurveLength = 0f;//currentMagnitude = 0f, int tl = 0; Vector2 rotatedNormal, nextRotatedNormal; Vector2 normal, offsetNormal, directionA, directionB, intersection; int edgeCount = lineData.GetEdgeCount(); int edgeCountMinusOne = edgeCount - 1; float finSize = size; for (positionIndex = 0; positionIndex <= edgeCount; positionIndex++) { currentPosition = positions [positionIndex]; if(positionIndex == 0) { previousPosition = positions[0]; rotatedNormal = SVGMath.RotateVectorClockwise(lineData.GetNormal(0)); nextPosition = positions[1]; nextRotatedNormal = SVGMath.RotateVectorClockwise(lineData.GetNormal(0)); } else if(positionIndex == edgeCount) { previousPosition = positions[positionIndex - 1]; rotatedNormal = SVGMath.RotateVectorClockwise(lineData.GetNormal(positionIndex - 1)); if(closeLine == ClosePathRule.ALWAYS) { nextPosition = positions[0]; nextRotatedNormal = SVGMath.RotateVectorClockwise((positions[positions.Length-1] - positions[0]).normalized); } else { nextPosition = positions[positions.Length - 1]; nextRotatedNormal = SVGMath.RotateVectorClockwise(lineData.GetNormal(edgeCountMinusOne)); } } else { previousPosition = positions[positionIndex - 1]; rotatedNormal = SVGMath.RotateVectorClockwise(lineData.GetNormal(positionIndex - 1)); nextRotatedNormal = SVGMath.RotateVectorClockwise(lineData.GetNormal(positionIndex)); nextPosition = positions[positionIndex + 1]; } Vector2 startA = previousPosition + rotatedNormal * finSize + rotatedNormal * offset; Vector2 endA = currentPosition + rotatedNormal * finSize + rotatedNormal * offset; Vector2 startB = currentPosition + nextRotatedNormal * finSize + nextRotatedNormal * offset; Vector2 endB = nextPosition + nextRotatedNormal * finSize + nextRotatedNormal * offset; if(positionIndex == 0) { _vertices.AddRange(new Vector3[]{ currentPosition + rotatedNormal * -finSize + rotatedNormal * offset, currentPosition + rotatedNormal * finSize + rotatedNormal * offset, }); _colors.AddRange(new Color32[]{colorA, colorB}); verticeIndex += 2; } else { bool intersect = SVGMath.LineLineIntersection(out intersection, startA, endA, startB, endB); if(!intersect) { normal = Vector2.Lerp(rotatedNormal, nextRotatedNormal, 0.5f).normalized; offsetNormal = normal * offset; if(positionIndex == edgeCount && closeLine != ClosePathRule.ALWAYS) { _vertices.AddRange(new Vector3[]{ endA, currentPosition + normal * -finSize + offsetNormal, }); _colors.AddRange(new Color32[]{colorB, colorA}); verticeIndex += 2; _triangles.AddRange(new int[]{ verticeIndex - 4, verticeIndex - 2, verticeIndex - 1, verticeIndex - 2, verticeIndex - 4, verticeIndex - 3, }); } else { _vertices.AddRange(new Vector3[]{ endA, currentPosition + normal * -finSize + offsetNormal, startB }); _colors.AddRange(new Color32[]{colorB, colorA, colorB}); verticeIndex += 3; _triangles.AddRange(new int[]{ verticeIndex - 3, verticeIndex - 2, verticeIndex - 5, verticeIndex - 5, verticeIndex - 4, verticeIndex - 3, verticeIndex - 1, verticeIndex - 2, verticeIndex - 3, }); } } else { normal = Vector2.Lerp(rotatedNormal, nextRotatedNormal, 0.5f).normalized; offsetNormal = normal * offset; _vertices.AddRange(new Vector3[]{ currentPosition + normal * -finSize + offsetNormal, intersection}); _colors.AddRange(new Color32[]{colorA, colorB}); verticeIndex += 2; _triangles.AddRange(new int[]{ verticeIndex - 4, verticeIndex - 2, verticeIndex - 1, verticeIndex - 4, verticeIndex - 1, verticeIndex - 3 }); } } } if(closeLine == ClosePathRule.ALWAYS) { previousPosition = positions[positions.Length - 1]; currentPosition = positions [0]; nextPosition = positions [1]; rotatedNormal = SVGMath.RotateVectorClockwise( (previousPosition - currentPosition).normalized ); nextRotatedNormal = SVGMath.RotateVectorClockwise(lineData.GetNormal(0)); Vector2 startA = previousPosition + rotatedNormal * finSize + rotatedNormal * offset; Vector2 endA = currentPosition + rotatedNormal * finSize + rotatedNormal * offset; Vector2 startB = currentPosition + nextRotatedNormal * finSize + nextRotatedNormal * offset; Vector2 endB = nextPosition + nextRotatedNormal * finSize + nextRotatedNormal * offset; bool intersect = SVGMath.LineLineIntersection(out intersection, startA, endA, startB, endB); if(!intersect) { normal = Vector2.Lerp(rotatedNormal, nextRotatedNormal, 0.5f).normalized; offsetNormal = normal * offset; _vertices.AddRange(new Vector3[]{ endA, currentPosition + normal * -finSize + offsetNormal, startB }); _colors.AddRange(new Color32[]{colorB, colorA, colorB}); verticeIndex += 3; if (positionIndex != 0) { _triangles.AddRange(new int[]{ verticeIndex - 3, verticeIndex - 2, verticeIndex - 5, verticeIndex - 5, verticeIndex - 4, verticeIndex - 3, verticeIndex - 1, verticeIndex - 2, verticeIndex - 3, }); } } else { normal = Vector2.Lerp(rotatedNormal, nextRotatedNormal, 0.5f).normalized; offsetNormal = normal * offset; _vertices.AddRange(new Vector3[]{ currentPosition + normal * -finSize + offsetNormal, intersection}); _colors.AddRange(new Color32[]{colorA, colorB}); verticeIndex += 2; if (positionIndex != 0) { _triangles.AddRange(new int[]{ verticeIndex - 4, verticeIndex - 2, verticeIndex - 1, verticeIndex - 4, verticeIndex - 1, verticeIndex - 3 }); } } _vertices[1] = _vertices[_vertices.Count - 1]; _vertices[0] = _vertices[_vertices.Count - 2]; } Mesh mesh = new Mesh(); mesh.vertices = _vertices.ToArray(); mesh.triangles = _triangles.ToArray(); mesh.colors32 = _colors.ToArray(); return mesh; }
public static void Create(ISVGElement svgElement, string defaultName = null, ClosePathRule closePathRule = ClosePathRule.ALWAYS) { if (svgElement == null) { return; } if (svgElement.paintable.visibility != SVGVisibility.Visible || svgElement.paintable.display == SVGDisplay.None) { return; } List <SVGShape> shapes = new List <SVGShape>(); List <List <Vector2> > inputPaths = svgElement.GetPath(); if (inputPaths.Count == 1) { if (svgElement.paintable.IsFill()) { List <List <Vector2> > path = inputPaths; if (svgElement.paintable.clipPathList != null && svgElement.paintable.clipPathList.Count > 0) { path = SVGGeom.ClipPolygon(new List <List <Vector2> >() { inputPaths[0] }, svgElement.paintable.clipPathList); } SVGShape[] addShapes = SVGGraphics.GetShapes(path, svgElement.paintable, svgElement.transformMatrix); if (addShapes != null && addShapes.Length > 0) { shapes.AddRange(addShapes); } } if (svgElement.paintable.IsStroke()) { List <List <Vector2> > path = SVGSimplePath.CreateStroke(inputPaths[0], svgElement.paintable, closePathRule); if (svgElement.paintable.clipPathList != null && svgElement.paintable.clipPathList.Count > 0) { path = SVGGeom.ClipPolygon(path, svgElement.paintable.clipPathList); } SVGShape[] addShapes = SVGGraphics.GetShapes(path, svgElement.paintable, svgElement.transformMatrix, true); if (addShapes != null && addShapes.Length > 0) { shapes.AddRange(addShapes); } } } else { if (svgElement.paintable.IsFill()) { List <List <Vector2> > fillPaths = inputPaths; if (svgElement.paintable.clipPathList != null && svgElement.paintable.clipPathList.Count > 0) { fillPaths = SVGGeom.ClipPolygon(inputPaths, svgElement.paintable.clipPathList); } SVGShape[] addShapes = SVGGraphics.GetShapes(fillPaths, svgElement.paintable, svgElement.transformMatrix); if (addShapes != null && addShapes.Length > 0) { shapes.AddRange(addShapes); } } if (svgElement.paintable.IsStroke()) { List <List <Vector2> > strokePath = SVGSimplePath.CreateStroke(inputPaths, svgElement.paintable, closePathRule); if (svgElement.paintable.clipPathList != null && svgElement.paintable.clipPathList.Count > 0) { strokePath = SVGGeom.ClipPolygon(strokePath, svgElement.paintable.clipPathList); } SVGShape[] addShapes = SVGGraphics.GetShapes(strokePath, svgElement.paintable, svgElement.transformMatrix, true); if (addShapes != null && addShapes.Length > 0) { shapes.AddRange(addShapes); } } } if (shapes.Count > 0) { string name = svgElement.attrList.GetValue("id"); if (string.IsNullOrEmpty(name)) { name = defaultName; } SVGLayer layer = new SVGLayer(); layer.shapes = shapes.ToArray(); layer.name = name; SVGGraphics.AddLayer(layer); } }
protected static List<StrokeSegment[]> CreateDashedStroke(StrokeSegment[] segments, float[] dashArray, float dashOffset, ref ClosePathRule closeLine) { if(segments == null || segments.Length == 0) return null; if(closeLine == ClosePathRule.ALWAYS || closeLine == ClosePathRule.AUTO) { System.Array.Resize<StrokeSegment>(ref segments, segments.Length + 1); segments[segments.Length - 1] = new StrokeSegment(segments[segments.Length - 2].endPoint, segments[0].startPoint); closeLine = ClosePathRule.NEVER; } List<StrokeSegment[]> finalSegments = new List<StrokeSegment[]>(); int dashArrayLength = dashArray.Length; int dashArrayIndex = 0; int segmentsLength = segments.Length; float dashElapsed = dashOffset, lengthA, lengthB; List<StrokeSegment> strokeSegments = new List<StrokeSegment>(); int i = 0; while(i < segmentsLength) { if(dashArrayIndex % 2 == 0) { lengthA = Mathf.Clamp(dashElapsed, 0f, segments[i].length); lengthB = Mathf.Clamp(dashElapsed + dashArray[dashArrayIndex], 0f, segments[i].length); if(lengthB - lengthA > 0f) { strokeSegments.Add(new StrokeSegment( segments[i].startPoint + segments[i].directionNormalized * lengthA, segments[i].startPoint + segments[i].directionNormalized * lengthB )); } } else { if(strokeSegments.Count > 0) {finalSegments.Add(strokeSegments.ToArray()); strokeSegments.Clear(); } } if(dashElapsed + dashArray[dashArrayIndex] < segments[i].length) { dashElapsed += dashArray[dashArrayIndex]; dashArrayIndex = (dashArrayIndex + 1) % dashArrayLength; } else { dashElapsed -= segments[i].length; i++; } } if(strokeSegments.Count > 0) {finalSegments.Add(strokeSegments.ToArray()); strokeSegments.Clear(); } return finalSegments; }
public static bool CreateAntialiasing(List <List <Vector2> > inputShapes, out SVGShape svgShape, Color colorA, float width, ClosePathRule closePath = ClosePathRule.NEVER) { svgShape = new SVGShape(); if (inputShapes == null || inputShapes.Count == 0) { return(false); } Color colorB = new Color(colorA.r, colorA.g, colorA.b, 0f); if (inputShapes.Count > 1) { List <SVGShape> shapes = new List <SVGShape>(); SVGShape currentLayer; for (int i = 0; i < inputShapes.Count; i++) { if (SVGMeshUtils.VectorLine(inputShapes[i].ToArray(), out currentLayer, colorA, colorB, width, width * 0.5f, closePath)) { shapes.Add(currentLayer); } } if (shapes.Count > 0) { svgShape = SVGShape.MergeShapes(shapes); return(true); } else { return(false); } } else { return(SVGMeshUtils.VectorLine(inputShapes[0].ToArray(), out svgShape, colorA, colorB, width, width * 0.5f, closePath)); } }
public static List<Vector2> Stroke(StrokeSegment[] segments, float thickness, Color32 color, StrokeLineJoin lineJoin, StrokeLineCap lineCap, float miterLimit = 4f, ClosePathRule closeLine = ClosePathRule.NEVER, float roundQuality = 10f) { if(segments == null || segments.Length == 0) return null; if(segments.Length == 1) { closeLine = ClosePathRule.NEVER; } else if (closeLine == ClosePathRule.AUTO) { if(segments[0].startPoint == segments[segments.Length-1].endPoint) { closeLine = ClosePathRule.ALWAYS; } else { closeLine = ClosePathRule.NEVER; } } if(segments[0].startPoint == segments[segments.Length-1].endPoint) { List<StrokeSegment> tempSegments = new List<StrokeSegment>(segments); tempSegments.RemoveAt(tempSegments.Count - 1); segments = tempSegments.ToArray(); } List<Vector2> innerPoints = new List<Vector2>(); List<Vector2> outerPoints = new List<Vector2>(); if(closeLine == ClosePathRule.ALWAYS) { List<StrokeSegment> tempSegments = new List<StrokeSegment>(segments); tempSegments.Add(new StrokeSegment(segments[segments.Length -1].endPoint, segments[0].startPoint)); tempSegments.Add(new StrokeSegment(segments[0].startPoint, segments[0].endPoint)); segments = tempSegments.ToArray(); } miterLimit = (miterLimit - 1f) * thickness * 2f; if(miterLimit < 1f) miterLimit = 1f; int i, i1, j, segmentsLength = segments.Length; float segmentsAngle, segmentsAngleRotated, halfWidth = thickness * 0.5f, angleProgress, radAngle = 0f, miterClipHalf = miterLimit * 0.5f, miterClipHalfDouble = miterClipHalf * miterClipHalf; Vector2 segmentLeftStartA, segmentLeftEndA = Vector2.zero, segmentRightStartA, segmentRightEndA = Vector2.zero, segmentLeftEndB, segmentRightEndB, intersectionLeft, intersectionRight, segmentStartCenter; Matrix4x4 rotationMatrix = Matrix4x4.TRS(Vector2.zero, Quaternion.Euler(0f, 0f, 90f), Vector2.one); if(lineCap == StrokeLineCap.butt || closeLine == ClosePathRule.ALWAYS) { innerPoints.AddRange(new Vector2[]{ segments[0].startPoint - segments[0].directionNormalizedRotated * halfWidth, segments[0].startPoint + segments[0].directionNormalizedRotated * halfWidth, }); } else if(lineCap == StrokeLineCap.round) { segmentStartCenter = Vector2.Lerp(segments[0].startPoint - segments[0].directionNormalizedRotated * halfWidth, segments[0].startPoint + segments[0].directionNormalizedRotated * halfWidth, 0.5f); radAngle = Mathf.Atan2(segments[0].directionNormalizedRotated.y, segments[0].directionNormalizedRotated.x); float roundSegmentsfAlt = roundQuality * thickness; float roundSegmentsfAltMinusOne = roundSegmentsfAlt - 1; if(roundSegmentsfAltMinusOne > 0) { for(j = 0; j <= roundSegmentsfAlt; j++) { angleProgress = 1f - Mathf.Clamp01(j / roundSegmentsfAltMinusOne); innerPoints.Add(segmentStartCenter + new Vector2(Mathf.Cos(radAngle + angleProgress * Mathf.PI) * halfWidth, Mathf.Sin(radAngle + angleProgress * Mathf.PI) * halfWidth)); } } innerPoints.AddRange(new Vector2[]{ segments[0].startPoint + segments[0].directionNormalizedRotated * halfWidth, }); outerPoints.AddRange(new Vector2[]{ segments[0].startPoint - segments[0].directionNormalizedRotated * halfWidth, }); } else if(lineCap == StrokeLineCap.square) { innerPoints.AddRange(new Vector2[]{ segments[0].startPoint - segments[0].directionNormalized * halfWidth - segments[0].directionNormalizedRotated * halfWidth, segments[0].startPoint - segments[0].directionNormalized * halfWidth + segments[0].directionNormalizedRotated * halfWidth, }); } if(segmentsLength > 1) { for(i = 1; i < segmentsLength; i++) { i1 = i - 1; /* if(segments[i1].length == 0f || segments[i].length == 0) { continue; } */ segmentsAngle = Vector2.Dot(segments[i].directionNormalized, segments[i1].directionNormalized); segmentsAngleRotated = Vector2.Dot(segments[i].directionNormalized, segments[i1].directionNormalizedRotated); float miterLength = (1f / Mathf.Sin((Mathf.PI - Mathf.Acos(segmentsAngle)) * 0.5f)) * thickness; float miterLengthHalf = miterLength * 0.5f; Vector2 miterVector = Vector2.Lerp(segments[i1].directionNormalizedRotated, segments[i].directionNormalizedRotated, 0.5f).normalized; Vector2 miterVectorLengthHalf = miterVector * miterLengthHalf; Vector2 miterVectorRotated = rotationMatrix.MultiplyVector(miterVector); segmentLeftStartA = segments[i].startPoint - segments[i].directionNormalizedRotated * halfWidth; segmentLeftEndA = segments[i].endPoint - segments[i].directionNormalizedRotated * halfWidth; segmentRightStartA = segments[i].startPoint + segments[i].directionNormalizedRotated * halfWidth; segmentRightEndA = segments[i].endPoint + segments[i].directionNormalizedRotated * halfWidth; //segmentLeftStartB = segments[i1].startPoint - segments[i1].directionNormalizedRotated * halfWidth; segmentLeftEndB = segments[i1].endPoint - segments[i1].directionNormalizedRotated * halfWidth; //segmentRightStartB = segments[i1].startPoint + segments[i1].directionNormalizedRotated * halfWidth; segmentRightEndB = segments[i1].endPoint + segments[i1].directionNormalizedRotated * halfWidth; if(lineJoin == StrokeLineJoin.miter) { if(miterLimit < miterLength) lineJoin = StrokeLineJoin.bevel; } if(lineJoin == StrokeLineJoin.miter || lineJoin == StrokeLineJoin.miterClip) { if(segmentsAngle == 1f || segmentsAngle == -1f) { innerPoints.AddRange(new Vector2[]{ segmentRightEndB, segmentRightStartA, }); outerPoints.AddRange(new Vector2[]{ segmentLeftEndB, segmentLeftStartA, }); } else { if(segmentsAngleRotated < 0) { if(miterLimit <= miterLength) { Vector2 a = segments[i1].endPoint + miterVector * miterClipHalf; Vector2 b = segments[i1].endPoint + miterVectorLengthHalf; Vector2 c = a + miterVectorRotated; SVGMath.LineLineIntersection(out intersectionLeft, b, segmentRightEndB, a, c); SVGMath.LineLineIntersection(out intersectionRight, b, segmentRightStartA, a, c); if(miterClipHalfDouble <= (Vector2.Lerp(segmentRightEndB, segmentRightStartA, 0.5f) - segments[i1].endPoint).sqrMagnitude) { intersectionLeft = segmentRightEndB; intersectionRight = segmentRightStartA; } innerPoints.AddRange(new Vector2[]{ intersectionLeft, intersectionRight, }); outerPoints.AddRange(new Vector2[]{ segmentLeftEndB, segmentLeftStartA, }); } else { intersectionRight = segments[i1].endPoint + miterVectorLengthHalf; innerPoints.AddRange(new Vector2[]{ intersectionRight, }); outerPoints.AddRange(new Vector2[]{ segmentLeftEndB, segmentLeftStartA, }); } } else { if(miterLimit <= miterLength) { Vector2 a = segments[i1].endPoint - miterVector * miterClipHalf; Vector2 b = segments[i1].endPoint - miterVectorLengthHalf; Vector2 c = a + miterVectorRotated; SVGMath.LineLineIntersection(out intersectionLeft, b, segmentLeftStartA, a, c); SVGMath.LineLineIntersection(out intersectionRight, b, segmentLeftEndB, a, c); if(miterClipHalfDouble <= (Vector2.Lerp(segmentLeftStartA, segmentLeftEndB, 0.5f) - segments[i1].endPoint).sqrMagnitude) { intersectionLeft = segmentLeftStartA; intersectionRight = segmentLeftEndB; } outerPoints.AddRange(new Vector2[]{ intersectionRight, intersectionLeft, }); innerPoints.AddRange(new Vector2[]{ segmentRightEndB, segmentRightStartA, }); } else { intersectionLeft = segments[i1].endPoint - miterVectorLengthHalf; outerPoints.AddRange(new Vector2[]{ intersectionLeft, }); innerPoints.AddRange(new Vector2[]{ segmentRightEndB, segmentRightStartA, }); } } } } else if(lineJoin == StrokeLineJoin.bevel) { innerPoints.AddRange(new Vector2[]{ segmentRightEndB, segmentRightStartA, }); outerPoints.AddRange(new Vector2[]{ segmentLeftEndB, segmentLeftStartA, }); } else if(lineJoin == StrokeLineJoin.round) { if(segmentsAngle == 1f) { innerPoints.AddRange(new Vector2[]{ segmentRightEndB, segmentRightStartA, }); outerPoints.AddRange(new Vector2[]{ segmentLeftEndB, segmentLeftStartA, }); } else { if(segmentsAngleRotated < 0) { innerPoints.AddRange(new Vector2[]{ segmentRightEndB, }); outerPoints.AddRange(new Vector2[]{ segmentLeftEndB, segmentLeftStartA, }); segmentStartCenter = segments[i].startPoint; Vector2 dir = segments[i1].directionNormalizedRotated; float angle = Mathf.Acos(Vector2.Dot(segments[i1].directionNormalized, segments[i].directionNormalized)); radAngle = Mathf.Atan2(dir.y, dir.x); float roundSegmentsfAlt = roundQuality * thickness * (Mathf.Acos(segmentsAngle)/ Mathf.PI); if(roundSegmentsfAlt < 1) roundSegmentsfAlt = 1f; float roundSegmentsfAltMinusOne = roundSegmentsfAlt; if(roundSegmentsfAltMinusOne > 0) { for(j = 0; j < roundSegmentsfAlt; j++) { angleProgress = Mathf.Clamp01(j / roundSegmentsfAltMinusOne); innerPoints.Add(segmentStartCenter + new Vector2(Mathf.Cos(radAngle - angleProgress * angle) * halfWidth, Mathf.Sin(radAngle - angleProgress * angle) * halfWidth)); } } innerPoints.AddRange(new Vector2[]{ segmentRightStartA, }); } else { innerPoints.AddRange(new Vector2[]{ segmentRightEndB, segmentRightStartA, }); outerPoints.AddRange(new Vector2[]{ segmentLeftEndB, }); segmentStartCenter = segments[i].startPoint; Vector2 dir = -segments[i].directionNormalizedRotated; float angle = Mathf.Acos(Vector2.Dot(segments[i1].directionNormalized, segments[i].directionNormalized)); radAngle = Mathf.Atan2(dir.y, dir.x); float roundSegmentsfAlt = roundQuality * thickness * (Mathf.Acos(segmentsAngle)/ Mathf.PI); if(roundSegmentsfAlt < 1) roundSegmentsfAlt = 1f; float roundSegmentsfAltMinusOne = roundSegmentsfAlt; if(roundSegmentsfAltMinusOne > 0) { for(j = 0; j < roundSegmentsfAlt; j++) { angleProgress = Mathf.Clamp01(1f -(j / roundSegmentsfAltMinusOne)); outerPoints.Add(segmentStartCenter + new Vector2(Mathf.Cos(radAngle - angleProgress * angle) * halfWidth, Mathf.Sin(radAngle - angleProgress * angle) * halfWidth)); } } outerPoints.AddRange(new Vector2[]{ segmentLeftStartA, }); } } } } } int lastSegmentIndex = segments.Length - 1; segmentLeftStartA = segments[lastSegmentIndex].startPoint - segments[lastSegmentIndex].directionNormalizedRotated * halfWidth; segmentLeftEndA = segments[lastSegmentIndex].endPoint - segments[lastSegmentIndex].directionNormalizedRotated * halfWidth; segmentRightStartA = segments[lastSegmentIndex].startPoint + segments[lastSegmentIndex].directionNormalizedRotated * halfWidth; segmentRightEndA = segments[lastSegmentIndex].endPoint + segments[lastSegmentIndex].directionNormalizedRotated * halfWidth; if(closeLine == ClosePathRule.NEVER) { if(lineCap == StrokeLineCap.butt) { innerPoints.AddRange(new Vector2[]{ segmentRightEndA, segmentLeftEndA, }); } else if(lineCap == StrokeLineCap.round) { innerPoints.AddRange(new Vector2[]{ segmentRightEndA }); outerPoints.AddRange(new Vector2[]{ segmentLeftEndA, }); segmentStartCenter = Vector2.Lerp(segmentLeftEndA, segmentRightEndA, 0.5f); radAngle = Mathf.Atan2(-segments[lastSegmentIndex].directionNormalizedRotated.y, -segments[lastSegmentIndex].directionNormalizedRotated.x); float roundSegmentsfAlt = roundQuality * thickness; float roundSegmentsfAltMinusOne = roundSegmentsfAlt - 1; if(roundSegmentsfAltMinusOne > 0) { for(j = 0; j <= roundSegmentsfAlt; j++) { angleProgress = 1f - Mathf.Clamp01(j / roundSegmentsfAltMinusOne); innerPoints.Add(segmentStartCenter + new Vector2(Mathf.Cos(radAngle + angleProgress * Mathf.PI) * halfWidth, Mathf.Sin(radAngle + angleProgress * Mathf.PI) * halfWidth)); } } } else if(lineCap == StrokeLineCap.square) { Vector2 lastSegmentOffset = segments[lastSegmentIndex].directionNormalized * halfWidth; innerPoints.AddRange(new Vector2[]{ segmentRightEndA + lastSegmentOffset, segmentLeftEndA + lastSegmentOffset, }); } } if(closeLine == ClosePathRule.ALWAYS && lineJoin == StrokeLineJoin.miter || lineJoin == StrokeLineJoin.miterClip) { innerPoints.AddRange(new Vector2[]{ segmentRightEndA, segmentLeftEndA, }); } outerPoints.Reverse(); innerPoints.AddRange(outerPoints); return innerPoints; }
/* * public static Mesh StrokeMesh(List<StrokeSegment[]> segments, float thickness, Color32 color, StrokeLineJoin lineJoin, StrokeLineCap lineCap, float miterLimit = 4f, float[] dashArray = null, float dashOffset = 0f, ClosePathRule closeLine = ClosePathRule.NEVER, float roundQuality = 10f) * { * List<List<Vector2>> finalPoints = StrokeShape(segments, thickness, color, lineJoin, lineCap, miterLimit, dashArray, dashOffset, closeLine, roundQuality); * return TessellateStroke(finalPoints, color); * } */ protected static List <StrokeSegment[]> CreateDashedStroke(StrokeSegment[] segments, float[] dashArray, float dashOffset, ref ClosePathRule closeLine) { if (segments == null || segments.Length == 0) { return(null); } if (closeLine == ClosePathRule.ALWAYS || closeLine == ClosePathRule.AUTO) { System.Array.Resize <StrokeSegment>(ref segments, segments.Length + 1); segments[segments.Length - 1] = new StrokeSegment(segments[segments.Length - 2].endPoint, segments[0].startPoint); closeLine = ClosePathRule.NEVER; } List <StrokeSegment[]> finalSegments = new List <StrokeSegment[]>(); int dashArrayLength = dashArray.Length; int dashArrayIndex = 0; int segmentsLength = segments.Length; float dashElapsed = dashOffset, lengthA, lengthB; List <StrokeSegment> strokeSegments = new List <StrokeSegment>(); int i = 0; while (i < segmentsLength) { if (dashArrayIndex % 2 == 0) { lengthA = Mathf.Clamp(dashElapsed, 0f, segments[i].length); lengthB = Mathf.Clamp(dashElapsed + dashArray[dashArrayIndex], 0f, segments[i].length); if (lengthB - lengthA > 0f) { strokeSegments.Add(new StrokeSegment( segments[i].startPoint + segments[i].directionNormalized * lengthA, segments[i].startPoint + segments[i].directionNormalized * lengthB )); } } else { if (strokeSegments.Count > 0) { finalSegments.Add(strokeSegments.ToArray()); strokeSegments.Clear(); } } if (dashElapsed + dashArray[dashArrayIndex] < segments[i].length) { dashElapsed += dashArray[dashArrayIndex]; dashArrayIndex = (dashArrayIndex + 1) % dashArrayLength; } else { dashElapsed -= segments[i].length; i++; } } if (strokeSegments.Count > 0) { finalSegments.Add(strokeSegments.ToArray()); strokeSegments.Clear(); } return(finalSegments); }
/* * public static Mesh CreateStrokeMesh(List<Vector2> inputShapes, SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) * { * if(inputShapes == null || inputShapes.Count == 0 || paintable == null || paintable.strokeWidth <= 0f) * return null; * * return CreateStrokeMesh(new List<List<Vector2>>(){inputShapes}, paintable, closePath); * } * * public static Mesh CreateStrokeMesh(List<List<Vector2>> inputShapes, SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) * { * if(inputShapes == null || inputShapes.Count == 0 || paintable == null || paintable.strokeWidth <= 0f) * return null; * * List<StrokeSegment[]> segments = new List<StrokeSegment[]>(); * for(int i = 0; i < inputShapes.Count; i++) * { * if(inputShapes[i] == null || inputShapes[i].Count < 2) * continue; * * segments.Add(GetSegments(inputShapes[i])); * } * * return SVGLineUtils.StrokeMesh(segments, paintable.strokeWidth, GetStrokeColor(paintable), GetStrokeLineJoin(paintable.strokeLineJoin), GetStrokeLineCap(paintable.strokeLineCap), paintable.miterLimit, paintable.dashArray, paintable.dashOffset, closePath, SVGGraphics.roundQuality); * } */ /* * public static Mesh CreateStrokeSimple(SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) * { * return CreateStrokeSimple(new List<List<Vector2>>(){new List<Vector2>(SVGGraphics.position_buffer.ToArray())}, paintable, closePath); * } */ public static Mesh CreateStrokeSimple(List <List <Vector2> > inputShapes, SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) { if (inputShapes == null || inputShapes.Count == 0 || paintable == null || paintable.strokeWidth <= 0f) { return(null); } AddInputShape(inputShapes); Color color = GetStrokeColor(paintable); float strokeWidth = paintable.strokeWidth; if (inputShapes.Count > 1) { CombineInstance[] combineInstances = new CombineInstance[inputShapes.Count]; for (int i = 0; i < inputShapes.Count; i++) { combineInstances[i] = new CombineInstance(); SVGShape svgLayer = new SVGShape(); if (SVGMeshUtils.VectorLine(inputShapes[i].ToArray(), out svgLayer, color, color, strokeWidth, 0f, closePath)) { Mesh localMesh = new Mesh(); int totalVertices = svgLayer.vertices.Length; Vector3[] vertices = new Vector3[totalVertices]; for (int j = 0; j < totalVertices; j++) { vertices[j] = svgLayer.vertices[j]; } localMesh.vertices = vertices; localMesh.triangles = svgLayer.triangles; localMesh.colors32 = svgLayer.colors; combineInstances[i].mesh = localMesh; } } Mesh mesh = new Mesh(); mesh.CombineMeshes(combineInstances, true, false); return(mesh); } else { SVGShape svgLayer = new SVGShape(); if (SVGMeshUtils.VectorLine(inputShapes[0].ToArray(), out svgLayer, color, color, strokeWidth, 0f, closePath)) { Mesh localMesh = new Mesh(); int totalVertices = svgLayer.vertices.Length; Vector3[] vertices = new Vector3[totalVertices]; for (int j = 0; j < totalVertices; j++) { vertices[j] = svgLayer.vertices[j]; } localMesh.vertices = vertices; localMesh.triangles = svgLayer.triangles; localMesh.colors32 = svgLayer.colors; return(localMesh); } return(null); } }
public static List<List<Vector2>> CreateStroke(List<List<Vector2>> inputShapes, SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) { if(inputShapes == null || inputShapes.Count == 0 || paintable == null || paintable.strokeWidth <= 0f) return null; List<StrokeSegment[]> segments = new List<StrokeSegment[]>(); for(int i = 0; i < inputShapes.Count; i++) { if(inputShapes[i] == null || inputShapes[i].Count < 2) continue; segments.Add(GetSegments(inputShapes[i])); } return SVGLineUtils.StrokeShape(segments, paintable.strokeWidth, Color.black, GetStrokeLineJoin(paintable.strokeLineJoin), GetStrokeLineCap(paintable.strokeLineCap), paintable.miterLimit, paintable.dashArray, paintable.dashOffset, closePath, SVGGraphics.roundQuality); }
public static List <List <Vector2> > CreateStroke(List <Vector2> inputShapes, SVGPaintable paintable, ClosePathRule closePath = ClosePathRule.NEVER) { if (inputShapes == null || inputShapes.Count == 0 || paintable == null || paintable.strokeWidth <= 0f) { return(null); } return(CreateStroke(new List <List <Vector2> >() { inputShapes }, paintable, closePath)); }
public static Mesh StrokeMesh(StrokeSegment[] segments, float thickness, Color32 color, StrokeLineJoin lineJoin, StrokeLineCap lineCap, float miterLimit = 4f, float[] dashArray = null, float dashOffset = 0f, ClosePathRule closeLine = ClosePathRule.NEVER, float roundQuality = 10f) { if (segments == null || segments.Length == 0) { return(null); } List <List <Vector2> > finalPoints = StrokeShape(new List <StrokeSegment[]>() { segments }, thickness, color, lineJoin, lineCap, miterLimit, dashArray, dashOffset, closeLine, roundQuality); return(TessellateStroke(finalPoints, color)); }