#pragma warning restore 612, 618

        private static void TessellateShape(Shape vectorShape, List <Geometry> geoms, TessellationOptions tessellationOptions, bool isConvex)
        {
            UnityEngine.Profiling.Profiler.BeginSample("TessellateShape");

            // Don't generate any geometry for pattern fills since these are generated from another SceneNode
            if (vectorShape.Fill != null && !(vectorShape.Fill is PatternFill))
            {
                Color shapeColor = Color.white;
                if (vectorShape.Fill is SolidFill)
                {
                    shapeColor = ((SolidFill)vectorShape.Fill).Color;
                }

                shapeColor.a *= vectorShape.Fill.Opacity;

                if (isConvex && vectorShape.Contours.Length == 1)
                {
                    TessellateConvexContour(vectorShape, vectorShape.PathProps.Stroke, shapeColor, geoms, tessellationOptions);
                }
                else
                {
                    TessellateShapeLibTess(vectorShape, shapeColor, geoms, tessellationOptions);
                }
            }

            var stroke = vectorShape.PathProps.Stroke;

            if (stroke != null && stroke.HalfThickness > VectorUtils.Epsilon)
            {
                foreach (var c in vectorShape.Contours)
                {
                    Vector2[] strokeVerts;
                    UInt16[]  strokeIndices;
                    VectorUtils.TessellatePath(c, vectorShape.PathProps, tessellationOptions, out strokeVerts, out strokeIndices);
                    if (strokeIndices.Length > 0)
                    {
                        geoms.Add(new Geometry()
                        {
                            Vertices = strokeVerts, Indices = strokeIndices, Color = vectorShape.PathProps.Stroke.Color
                        });
                    }
                }
            }

            UnityEngine.Profiling.Profiler.EndSample();
        }
        static PartTessellation GetBodyTessellation(Body body, Func<Face, Color> faceColor, double surfaceDeviation, double angleDeviation)
        {
            var tessellationOptions = new TessellationOptions(surfaceDeviation, angleDeviation);
            var tessellation = body.GetTessellation(null, tessellationOptions);

            var vertices = new Dictionary<PositionNormalTextured, int>();
            var vertexList = new List<Point>();
            var normalList = new List<Direction>();

            var colors = new Dictionary<Color, int>();
            var colorList = new List<Color>();

            var faces = new List<FaceStruct>();
            foreach (var pair in tessellation) {
                var color = faceColor(pair.Key);

                int colorIndex;
                if (!colors.TryGetValue(color, out colorIndex)) {
                    colorList.Add(color);
                    colorIndex = colorList.Count - 1;
                    colors[color] = colorIndex;
                }

                var vertexIndices = new Dictionary<int, int>();

                var i = 0;
                foreach (var vertex in pair.Value.Vertices) {
                    int index;
                    if (!vertices.TryGetValue(vertex, out index)) {
                        vertexList.Add(vertex.Position);
                        normalList.Add(vertex.Normal);
                        index = vertexList.Count - 1;
                        vertices[vertex] = index;
                    }

                    vertexIndices[i] = index;
                    i++;
                }

                foreach (var facet in pair.Value.Facets) {
                    faces.Add(new FaceStruct {
                        Vertex1 = vertexIndices[facet.Vertex0],
                        Vertex2 = vertexIndices[facet.Vertex1],
                        Vertex3 = vertexIndices[facet.Vertex2],
                        Color = colorIndex
                    });
                }
            }

            var faceTessellation = new BodyTessellation {
                VertexPositions = vertexList,
                VertexNormals = normalList,
                FaceColors = colorList,
                Faces = faces
            };

            List<BodyTessellation> edges = new List<BodyTessellation>();
            foreach (var edge in body.Edges) {
                edges.Add(new BodyTessellation {
                    VertexPositions = new List<Point>(edge.GetPolyline())
                });
            }

            return new PartTessellation {
                Lines = edges,
                Meshes = { faceTessellation }
            };
        }
예제 #3
0
        #pragma warning disable 612, 618 // Silence use of deprecated IDrawable
        private static List <Geometry> TessellateNodeHierarchyRecursive(SceneNode node, TessellationOptions tessellationOptions, Matrix2D worldTransform, float worldOpacity, Dictionary <SceneNode, float> nodeOpacities)
        {
            if (node.Clipper != null)
            {
                VectorClip.PushClip(TraceNodeHierarchyShapes(node.Clipper, tessellationOptions), worldTransform);
            }

            var geoms = new List <Geometry>();

            if (node.Shapes != null)
            {
                foreach (var shape in node.Shapes)
                {
                    bool isConvex = shape.IsConvex && shape.Contours.Length == 1;
                    TessellateShape(shape, geoms, tessellationOptions, isConvex);
                }
            }

            foreach (var g in geoms)
            {
                g.Color.a        *= worldOpacity;
                g.WorldTransform  = worldTransform;
                g.UnclippedBounds = Bounds(g.Vertices);

                VectorClip.ClipGeometry(g);
            }

            if (node.Children != null)
            {
                foreach (var child in node.Children)
                {
                    var childOpacity = 1.0f;
                    if (nodeOpacities == null || !nodeOpacities.TryGetValue(child, out childOpacity))
                    {
                        childOpacity = 1.0f;
                    }

                    var transform  = worldTransform * child.Transform;
                    var opacity    = worldOpacity * childOpacity;
                    var childGeoms = TessellateNodeHierarchyRecursive(child, tessellationOptions, transform, opacity, nodeOpacities);

                    geoms.AddRange(childGeoms);
                }
            }

            if (node.Clipper != null)
            {
                VectorClip.PopClip();
            }

            return(geoms);
        }
        internal static List <Vector2[]> TraceNodeHierarchyShapes(SceneNode root, TessellationOptions tessellationOptions)
        {
            var shapes = new List <Vector2[]>();

            foreach (var nodeInfo in WorldTransformedSceneNodes(root, null))
            {
                var node = nodeInfo.Node;

                // We process the drawables even though they are obsolete, until we remove the IDrawable interface entirely
                if (node.Drawables != null)
                {
                    foreach (var drawable in node.Drawables)
                    {
                        var vectorShape = drawable as Shape;
                        if (vectorShape != null)
                        {
                            foreach (var c in vectorShape.Contours)
                            {
                                var shape = VectorUtils.TraceShape(c, vectorShape.PathProps.Stroke, tessellationOptions);
                                if (shape.Length > 0)
                                {
                                    shapes.Add(shape.Select(v => nodeInfo.WorldTransform * v).ToArray());
                                }
                            }
                            continue;
                        }

                        var vectorPath = drawable as Path;
                        if (vectorPath != null)
                        {
                            var shape = VectorUtils.TraceShape(vectorPath.Contour, vectorPath.PathProps.Stroke, tessellationOptions);
                            if (shape.Length > 0)
                            {
                                shapes.Add(shape.Select(v => nodeInfo.WorldTransform * v).ToArray());
                            }
                            continue;
                        }

                        var vectorRect = drawable as Rectangle;
                        if (vectorRect != null)
                        {
                            var shape = VectorUtils.TraceRectangle(vectorRect, vectorRect.PathProps.Stroke, tessellationOptions);
                            if (shape.Length > 0)
                            {
                                shapes.Add(shape.Select(v => nodeInfo.WorldTransform * v).ToArray());
                            }
                            continue;
                        }
                    }
                }

                if (node.Shapes != null)
                {
                    foreach (var shape in node.Shapes)
                    {
                        foreach (var c in shape.Contours)
                        {
                            var tracedShape = VectorUtils.TraceShape(c, shape.PathProps.Stroke, tessellationOptions);
                            if (tracedShape.Length > 0)
                            {
                                shapes.Add(tracedShape.Select(v => nodeInfo.WorldTransform * v).ToArray());
                            }
                        }
                    }
                }
            }

            return(shapes);
        }
예제 #5
0
        private static void TessellateConvexContour(Shape shape, Stroke stroke, Color color, List <Geometry> geoms, TessellationOptions tessellationOptions)
        {
            if (shape.Contours.Length != 1 || shape.Contours[0].Segments.Length == 0)
            {
                return;
            }

            UnityEngine.Profiling.Profiler.BeginSample("TessellateConvexContour");

            // Compute geometric mean
            var contour = shape.Contours[0];
            var mean    = Vector2.zero;

            foreach (var seg in contour.Segments)
            {
                mean += seg.P0;
            }
            mean /= contour.Segments.Length;

            // Trace the shape and build triangle fan
            var tracedShape = VectorUtils.TraceShape(contour, stroke, tessellationOptions);
            var vertices    = new Vector2[tracedShape.Length + 1];
            var indices     = new UInt16[tracedShape.Length * 3];

            vertices[0] = mean;
            for (int i = 0; i < tracedShape.Length; ++i)
            {
                vertices[i + 1]    = tracedShape[i];
                indices[i * 3]     = 0;
                indices[i * 3 + 1] = (UInt16)(i + 1);
                indices[i * 3 + 2] = ((i + 2) >= vertices.Length) ? (UInt16)1 : (UInt16)(i + 2);
            }

            geoms.Add(new Geometry()
            {
                Vertices = vertices, Indices = indices, Color = color, Fill = shape.Fill, FillTransform = shape.FillTransform
            });

            UnityEngine.Profiling.Profiler.EndSample();
        }
예제 #6
0
        private static void TessellateShapeLibTess(Shape vectorShape, Color color, List <Geometry> geoms, TessellationOptions tessellationOptions)
        {
            UnityEngine.Profiling.Profiler.BeginSample("LibTess");

            var tess = new Tess();

            var angle  = 45.0f * Mathf.Deg2Rad;
            var mat    = Matrix2D.RotateLH(angle);
            var invMat = Matrix2D.RotateLH(-angle);

            foreach (var c in vectorShape.Contours)
            {
                var contour = new List <Vector2>(100);
                foreach (var v in VectorUtils.TraceShape(c, vectorShape.PathProps.Stroke, tessellationOptions))
                {
                    contour.Add(mat.MultiplyPoint(v));
                }

                tess.AddContour(contour.Select(v => new ContourVertex()
                {
                    Position = new Vec3()
                    {
                        X = v.x, Y = v.y
                    }
                }).ToArray(), ContourOrientation.Original);
            }

            var windingRule = (vectorShape.Fill.Mode == FillMode.OddEven) ? WindingRule.EvenOdd : WindingRule.NonZero;

            try
            {
                tess.Tessellate(windingRule, ElementType.Polygons, 3);
            }
            catch (System.Exception)
            {
                Debug.LogWarning("Shape tessellation failed, skipping...");
                UnityEngine.Profiling.Profiler.EndSample();
                return;
            }

            var indices  = tess.Elements.Select(i => (UInt16)i);
            var vertices = tess.Vertices.Select(v => invMat.MultiplyPoint(new Vector2(v.Position.X, v.Position.Y)));

            if (indices.Count() > 0)
            {
                geoms.Add(new Geometry()
                {
                    Vertices = vertices.ToArray(), Indices = indices.ToArray(), Color = color, Fill = vectorShape.Fill, FillTransform = vectorShape.FillTransform
                });
            }

            UnityEngine.Profiling.Profiler.EndSample();
        }
예제 #7
0
        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();
        }
예제 #8
0
        #pragma warning disable 612, 618 // Silence use of deprecated IDrawable
        private static List <Geometry> TessellateNodeHierarchyRecursive(SceneNode node, TessellationOptions tessellationOptions, Matrix2D worldTransform, float worldOpacity, Dictionary <SceneNode, float> nodeOpacities)
        {
            if (node.Clipper != null)
            {
                VectorClip.PushClip(TraceNodeHierarchyShapes(node.Clipper, tessellationOptions), worldTransform);
            }

            var geoms = new List <Geometry>();

            if (node.Drawables != null)
            {
                // We process the drawables even though they are obsolete, until we remove the IDrawable interface entirely
                foreach (var drawable in node.Drawables)
                {
                    var vectorShape = drawable as Shape;
                    if (vectorShape != null)
                    {
                        bool isConvex = vectorShape.IsConvex && vectorShape.Contours.Length == 1;
                        TessellateShape(vectorShape, geoms, tessellationOptions, isConvex);
                        continue;
                    }

                    var vectorPath = drawable as Path;
                    if (vectorPath != null)
                    {
                        TessellatePath(vectorPath.Contour, vectorPath.PathProps, geoms, tessellationOptions);
                        continue;
                    }

                    var vectorRect = drawable as Rectangle;
                    if (vectorRect != null)
                    {
                        TessellateRectangle(vectorRect, geoms, tessellationOptions);
                        continue;
                    }
                }
            }

            if (node.Shapes != null)
            {
                foreach (var shape in node.Shapes)
                {
                    bool isConvex = shape.IsConvex && shape.Contours.Length == 1;
                    TessellateShape(shape, geoms, tessellationOptions, isConvex);
                }
            }

            foreach (var g in geoms)
            {
                g.Color.a        *= worldOpacity;
                g.WorldTransform  = worldTransform;
                g.UnclippedBounds = Bounds(g.Vertices);

                VectorClip.ClipGeometry(g);
            }

            if (node.Children != null)
            {
                foreach (var child in node.Children)
                {
                    var childOpacity = 1.0f;
                    if (nodeOpacities == null || !nodeOpacities.TryGetValue(child, out childOpacity))
                    {
                        childOpacity = 1.0f;
                    }

                    var transform  = worldTransform * child.Transform;
                    var opacity    = worldOpacity * childOpacity;
                    var childGeoms = TessellateNodeHierarchyRecursive(child, tessellationOptions, transform, opacity, nodeOpacities);

                    geoms.AddRange(childGeoms);
                }
            }

            if (node.Clipper != null)
            {
                VectorClip.PopClip();
            }

            return(geoms);
        }
        private static void TessellateRectangleRoundedCorners(Rectangle rect, List <Geometry> geoms, TessellationOptions tessellationOptions)
        {
            var contour = BuildRectangleContour(rect);
            var shape   = new Shape()
            {
                Contours      = new BezierContour[] { contour },
                PathProps     = rect.PathProps,
                Fill          = rect.Fill,
                FillTransform = rect.FillTransform
            };

            ShapeUtils.TessellateShape(shape, geoms, tessellationOptions, true);
        }
        internal static void TessellateRectangle(Rectangle rect, List <Geometry> geoms, TessellationOptions tessellationOptions)
        {
            var width  = rect.Size.x;
            var height = rect.Size.y;

            if (width <= ShapeUtils.Epsilon || height <= ShapeUtils.Epsilon)
            {
                return;
            }

            if (IsSimpleRectangle(rect))
            {
                // Fast path, square corners, no patterns
                TessellateRectangleSquareCorners(rect, geoms);
            }
            else
            {
                TessellateRectangleRoundedCorners(rect, geoms, tessellationOptions);
            }
        }
예제 #11
0
        /// <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;
            }

            tessellateOptions.MaxCordDeviation     = Mathf.Max(0.0001f, tessellateOptions.MaxCordDeviation);
            tessellateOptions.MaxTanAngleDeviation = Mathf.Max(0.0001f, tessellateOptions.MaxTanAngleDeviation);

            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();
        }
예제 #12
0
        static void GenerateTip(BezierSegment segment, bool atStart, float t, PathEnding ending, float halfThickness, TessellationOptions tessellateOptions, List <Vector2> verts, List <UInt16> inds)
        {
            // The tip includes the vertices at the end itself
            Vector2 tan, nrm;
            var     pos        = VectorUtils.EvalFull(segment, t, out tan, out nrm);
            int     indexStart = verts.Count;

            switch (ending)
            {
            case PathEnding.Chop:
                if (atStart)
                {
                    verts.Add(pos + nrm * halfThickness);
                    verts.Add(pos - nrm * halfThickness);
                }
                else
                {
                    // Not much, path segments are always expected to be generated perpendicular to the path
                    // at the segment point location, so we don't have to do anything for the ending
                }
                break;

            case PathEnding.Square:
                if (atStart)
                {
                    verts.Add(pos + nrm * halfThickness - tan * halfThickness);
                    verts.Add(pos - nrm * halfThickness - tan * halfThickness);
                    verts.Add(pos + nrm * halfThickness);
                    verts.Add(pos - nrm * halfThickness);

                    inds.Add((UInt16)(indexStart + 0));
                    inds.Add((UInt16)(indexStart + 3));
                    inds.Add((UInt16)(indexStart + 1));
                    inds.Add((UInt16)(indexStart + 0));
                    inds.Add((UInt16)(indexStart + 2));
                    inds.Add((UInt16)(indexStart + 3));
                }
                else
                {
                    // Relying on the last two vertices, and just adding two of our own here
                    verts.Add(pos + nrm * halfThickness + tan * halfThickness);
                    verts.Add(pos - nrm * halfThickness + tan * halfThickness);

                    inds.Add((UInt16)(indexStart + 0 - 2));
                    inds.Add((UInt16)(indexStart + 3 - 2));
                    inds.Add((UInt16)(indexStart + 1 - 2));
                    inds.Add((UInt16)(indexStart + 0 - 2));
                    inds.Add((UInt16)(indexStart + 2 - 2));
                    inds.Add((UInt16)(indexStart + 3 - 2));
                }
                break;

            case PathEnding.Round:
                float arcSign     = atStart ? -1 : 1;
                int   arcSegments = CalculateArcSteps(halfThickness, 0, Mathf.PI, tessellateOptions);
                for (int i = 1; i < arcSegments; i++)
                {
                    float angle = Mathf.PI * (i / (float)arcSegments);
                    verts.Add(pos + Matrix2D.RotateLH(angle) * nrm * halfThickness * arcSign);
                }

                if (atStart)
                {
                    // Note how we maintain the last two vertices being setup for connection by the rest of the path vertices
                    int indexTipStart = verts.Count;
                    verts.Add(pos + nrm * halfThickness);
                    verts.Add(pos - nrm * halfThickness);

                    for (int i = 1; i < arcSegments; i++)
                    {
                        inds.Add((UInt16)(indexTipStart + 1));
                        inds.Add((UInt16)(indexStart + i - 1));
                        inds.Add((UInt16)(indexStart + i));
                    }
                }
                else
                {
                    inds.Add((UInt16)(indexStart - 1));
                    inds.Add((UInt16)(indexStart - 2));
                    inds.Add((UInt16)(indexStart + 0));
                    for (int i = 1; i < arcSegments - 1; i++)
                    {
                        inds.Add((UInt16)(indexStart - 1));
                        inds.Add((UInt16)(indexStart + i - 1));
                        inds.Add((UInt16)(indexStart + i));
                    }
                }
                break;

            default:
                System.Diagnostics.Debug.Assert(false);     // Joining has its own function
                break;
            }
        }
예제 #13
0
        static void GenerateJoining(JoiningInfo joinInfo, PathCorner corner, float halfThickness, float tippedCornerLimit, TessellationOptions tessellateOptions, List <Vector2> verts, List <UInt16> inds)
        {
            // The joining generates the vertices at both ends as well as the joining itself
            if (verts.Count == 0)
            {
                // Starting a path with a joining (meaning a loop)
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessEnd : joinInfo.InnerCornerVertex);
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessEnd);
            }

            System.Diagnostics.Debug.Assert(verts.Count >= 2);
            int indexStart = verts.Count - 2; // Using the last two vertices

            // Convert a tipped corner to a beveled one if tippedCornerLimit ratio is reached
            if (corner == PathCorner.Tipped && tippedCornerLimit >= 1.0f)
            {
                var theta = Vector2.Angle(-joinInfo.TanAtEnd, joinInfo.TanAtStart) * Mathf.Deg2Rad;
                var ratio = 1.0f / Mathf.Sin(theta / 2.0f);
                if (ratio > tippedCornerLimit)
                {
                    corner = PathCorner.Beveled;
                }
            }

            if (joinInfo.SimpleJoin)
            {
                // TODO
            }
            else if (corner == PathCorner.Tipped)
            {
                verts.Add(joinInfo.PosThicknessClosingPoint);
                verts.Add(joinInfo.NegThicknessClosingPoint);
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessStart : joinInfo.InnerCornerVertex);
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessStart);

                // Ending to tip
                inds.Add((UInt16)(indexStart + 0));
                inds.Add((UInt16)(indexStart + 3));
                inds.Add((UInt16)(indexStart + 1));
                inds.Add((UInt16)(indexStart + 0));
                inds.Add((UInt16)(indexStart + 2));
                inds.Add((UInt16)(indexStart + 3));

                // Tip to starting
                inds.Add((UInt16)(indexStart + 4));
                inds.Add((UInt16)(indexStart + 3));
                inds.Add((UInt16)(indexStart + 2));
                inds.Add((UInt16)(indexStart + 4));
                inds.Add((UInt16)(indexStart + 5));
                inds.Add((UInt16)(indexStart + 3));

                return;
            }
            else if (corner == PathCorner.Beveled)
            {
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessEnd : joinInfo.InnerCornerVertex);   // 2
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessEnd);   // 3
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.PosThicknessStart : joinInfo.InnerCornerVertex); // 4
                verts.Add(joinInfo.RoundPosThickness ? joinInfo.InnerCornerVertex : joinInfo.NegThicknessStart); // 5

                // Ending to tip
                inds.Add((UInt16)(indexStart + 0));
                inds.Add((UInt16)(indexStart + 2));
                inds.Add((UInt16)(indexStart + 1));
                inds.Add((UInt16)(indexStart + 1));
                inds.Add((UInt16)(indexStart + 2));
                inds.Add((UInt16)(indexStart + 3));

                // Bevel
                if (joinInfo.RoundPosThickness)
                {
                    inds.Add((UInt16)(indexStart + 2));
                    inds.Add((UInt16)(indexStart + 4));
                    inds.Add((UInt16)(indexStart + 3));
                }
                else
                {
                    inds.Add((UInt16)(indexStart + 3));
                    inds.Add((UInt16)(indexStart + 2));
                    inds.Add((UInt16)(indexStart + 5));
                }

                return;
            }

            if (corner == PathCorner.Round)
            {
                float sweepAngle = Mathf.Acos(Vector2.Dot(joinInfo.NormAtEnd, joinInfo.NormAtStart));
                bool  flipArc    = false;
                if (!PointOnTheLeftOfLine(Vector2.zero, joinInfo.NormAtEnd, joinInfo.NormAtStart))
                {
                    sweepAngle = -sweepAngle;
                    flipArc    = true;
                }

                UInt16 innerCornerVertexIndex = (UInt16)verts.Count;
                verts.Add(joinInfo.InnerCornerVertex);

                int arcSegments = CalculateArcSteps(halfThickness, 0, sweepAngle, tessellateOptions);
                for (int i = 0; i <= arcSegments; i++)
                {
                    float   angle = sweepAngle * (i / (float)arcSegments);
                    Vector2 nrm   = Matrix2D.RotateLH(angle) * joinInfo.NormAtEnd;
                    if (flipArc)
                    {
                        nrm = -nrm;
                    }
                    verts.Add(nrm * halfThickness + joinInfo.JoinPos);

                    if (i == 0)
                    {
                        inds.Add((UInt16)(indexStart + 0));
                        inds.Add((UInt16)(indexStart + 3));
                        inds.Add((UInt16)(indexStart + (joinInfo.RoundPosThickness ? 2 : 1)));

                        inds.Add((UInt16)(indexStart + 0));
                        inds.Add((UInt16)(indexStart + 2));
                        inds.Add((UInt16)(indexStart + (joinInfo.RoundPosThickness ? 1 : 3)));
                    }
                    else
                    {
                        if (joinInfo.RoundPosThickness)
                        {
                            inds.Add((UInt16)(indexStart + i + (flipArc ? 3 : 2)));
                            inds.Add((UInt16)(indexStart + i + (flipArc ? 2 : 3)));
                            inds.Add(innerCornerVertexIndex);
                        }
                        else
                        {
                            inds.Add((UInt16)(indexStart + i + (flipArc ? 3 : 2)));
                            inds.Add((UInt16)(indexStart + i + (flipArc ? 2 : 3)));
                            inds.Add(innerCornerVertexIndex);
                        }
                    }
                }

                // Manually add the last segment, maintain the expected vertex positioning
                int endingVerticesIndex = verts.Count;
                if (joinInfo.RoundPosThickness)
                {
                    verts.Add(joinInfo.PosThicknessStart);
                    verts.Add(joinInfo.InnerCornerVertex);
                }
                else
                {
                    verts.Add(joinInfo.InnerCornerVertex);
                    verts.Add(joinInfo.NegThicknessStart);
                }
                inds.Add((UInt16)(endingVerticesIndex - 1));
                inds.Add((UInt16)(endingVerticesIndex + 0));
                inds.Add(innerCornerVertexIndex);
            }
        }
예제 #14
0
        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);
            }
        }
예제 #15
0
        static Vector2[] TraceShape(BezierContour contour, Stroke stroke, TessellationOptions tessellateOptions)
        {
            if (tessellateOptions.StepDistance < Epsilon)
            {
                throw new Exception("stepDistance too small");
            }

            if (contour.Segments.Length < 2)
            {
                return(new Vector2[0]);
            }

            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);
            var strokePattern       = stroke != null ? stroke.Pattern : null;
            var strokePatternOffset = stroke != null ? stroke.PatternOffset : 0.0f;

            if (strokePattern != null)
            {
                approxStepCount += strokePattern.Length * 2;
            }

            List <Vector2> verts = new List <Vector2>(approxStepCount); // A little bit possibly for the endings

            var patternIt = new PathPatternIterator(strokePattern, strokePatternOffset);
            var pathIt    = new PathDistanceForwardIterator(contour.Segments, true, tessellateOptions.MaxCordDeviationSquared, tessellateOptions.MaxTanAngleDeviationCosine, tessellateOptions.SamplingStepSize);

            verts.Add(pathIt.EvalCurrent());

            while (!pathIt.Ended)
            {
                float distance        = patternIt.SegmentLength;
                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)
                    {
                        verts.Add(pathIt.EvalCurrent());
                    }

                    if ((unitsRemaining <= Epsilon) &&
                        !TryGetMoreRemainingUnits(ref unitsRemaining, pathIt, startingLength, distance, tessellateOptions.StepDistance))
                    {
                        break;
                    }

                    if (result == PathDistanceForwardIterator.Result.Stepped)
                    {
                        verts.Add(pathIt.EvalCurrent());
                    }
                }

                // Ending
                if (endedEntirePath)
                {
                    break;
                }
                else
                {
                    verts.Add(pathIt.EvalCurrent());
                }
                patternIt.Advance();
            }

            if ((verts[0] - verts[verts.Count - 1]).sqrMagnitude < Epsilon)
            {
                verts.RemoveAt(verts.Count - 1);
            }
            return(verts.ToArray()); // Why not return verts itself?
        }