/// <inheritdoc/>
        public EdgeWrap GetEdgeWrap(int faceIndex, int neighborIndex)
        {
            if (faceIndex < 0 || faceIndex >= _internalFaceCount)
            {
                throw new ArgumentOutOfRangeException("faceIndex");
            }

            if (!axis0.isWrapped && !axis1.isWrapped)
            {
                return(EdgeWrap.None);
            }

            if (!_isInverted)
            {
                switch (neighborIndex)
                {
                case 0: return(EdgeWrapUtility.FromEdgeRelations(
                                   (WrapsOnSidePosAxis0(faceIndex) ? EdgeWrap.NegVertToEdgeAxis0 : EdgeWrap.None) |
                                   (WrapsOnSideNegAxis1(faceIndex) ? EdgeWrap.NegEdgeToFaceAxis1 : EdgeWrap.None)));

                case 1: return(EdgeWrapUtility.FromEdgeRelations(
                                   (WrapsOnSideNegAxis0(faceIndex) ? EdgeWrap.NegEdgeToFaceAxis0 : EdgeWrap.None) |
                                   (WrapsOnSidePosAxis1(faceIndex) ? EdgeWrap.PosEdgeToVertAxis1 : EdgeWrap.None)));

                case 2: return(EdgeWrapUtility.FromEdgeRelations(
                                   (WrapsOnSidePosAxis0(faceIndex) ? EdgeWrap.PosEdgeToVertAxis0 : EdgeWrap.None) |
                                   (WrapsOnSidePosAxis1(faceIndex) ? EdgeWrap.PosFaceToEdgeAxis1 : EdgeWrap.None)));

                case 3: return(EdgeWrapUtility.FromEdgeRelations(
                                   (WrapsOnSidePosAxis0(faceIndex) ? EdgeWrap.PosFaceToEdgeAxis0 : EdgeWrap.None) |
                                   (WrapsOnSidePosAxis1(faceIndex) ? EdgeWrap.NegVertToEdgeAxis1 : EdgeWrap.None)));

                default: throw new ArgumentOutOfRangeException("neighborIndex");
                }
            }
            else
            {
                switch (neighborIndex)
                {
                case 0: return(EdgeWrapUtility.FromEdgeRelations(
                                   (WrapsOnSidePosAxis0(faceIndex) ? EdgeWrap.PosEdgeToVertAxis0 : EdgeWrap.None) |
                                   (WrapsOnSideNegAxis1(faceIndex) ? EdgeWrap.NegEdgeToFaceAxis1 : EdgeWrap.None)));

                case 1: return(EdgeWrapUtility.FromEdgeRelations(
                                   (WrapsOnSideNegAxis0(faceIndex) ? EdgeWrap.PosFaceToEdgeAxis0 : EdgeWrap.None) |
                                   (WrapsOnSidePosAxis1(faceIndex) ? EdgeWrap.PosEdgeToVertAxis1 : EdgeWrap.None)));

                case 2: return(EdgeWrapUtility.FromEdgeRelations(
                                   (WrapsOnSidePosAxis0(faceIndex) ? EdgeWrap.NegVertToEdgeAxis0 : EdgeWrap.None) |
                                   (WrapsOnSidePosAxis1(faceIndex) ? EdgeWrap.PosFaceToEdgeAxis1 : EdgeWrap.None)));

                case 3: return(EdgeWrapUtility.FromEdgeRelations(
                                   (WrapsOnSidePosAxis0(faceIndex) ? EdgeWrap.NegEdgeToFaceAxis0 : EdgeWrap.None) |
                                   (WrapsOnSidePosAxis1(faceIndex) ? EdgeWrap.NegVertToEdgeAxis1 : EdgeWrap.None)));

                default: throw new ArgumentOutOfRangeException("neighborIndex");
                }
            }
        }
示例#2
0
        // Pivot an edge clockwise around its implicit near vertex.
        //
        // \         /          \         /
        //  o-------o            o-------o
        //  |       |            |       |
        //  2   N   |            2   N   |
        //  |       |            |       |
        //  B       V      -->   B<--E---V
        // / \     / \          / \       \
        //    1   E   \            1       \
        //     \ v     \ /          \       \ /
        //      A       o            A       o
        //      |   P   |            |   P   |
        //      0       |            0       |
        //      |       |            |       |
        //      o-------o            o-------o
        //     /         \          /         \
        //
        // E:  edge passed in as parameter
        // V:  vertex to pivot E around clockwise
        // A:  old vertex that E is originally pointing at
        // B:  new vertex that E will now point at
        // P:  previous face on the counter-clockwise side of E
        // N:  next face on the clockwise side of E
        // 0:  outer edge 0 points at A, inner edge 0 points away from A
        // 1:  outer edge 1 points at B, inner edge 1 points at A, at N before pivot, at P after pivot
        // 2:  outer edge 2 points away from B
        // Note:  inner edges point downward, outer edges point upward

        private void PivotVertexEdgeForwardUnchecked(int edgeIndex, int twinEdgeIndex, int outerEdgeIndex1, int innerEdgeIndex1)
        {
            var innerEdgeIndex0 = edgeData[twinEdgeIndex].vNext;
            var outerEdgeIndex0 = edgeData[innerEdgeIndex0].twin;
            var outerEdgeIndex2 = edgeData[outerEdgeIndex1].fNext;

            var prevFaceIndex  = edgeData[edgeIndex].face;
            var nextFaceIndex  = edgeData[twinEdgeIndex].face;
            var oldVertexIndex = edgeData[edgeIndex].vertex;
            var newVertexIndex = edgeData[outerEdgeIndex1].vertex;

            // The edge was pointing at the old vertex, will now point at the new vertex.
            edgeData[edgeIndex].vertex = newVertexIndex;

            // The second inner edge was pointing at the next face, will now point at the previous face.
            edgeData[innerEdgeIndex1].face = prevFaceIndex;

            // Remove twin edge from old vertex linked list by skipping over it.
            edgeData[outerEdgeIndex1].vNext = innerEdgeIndex0;

            // Insert twin edge into new vertex linked list.
            edgeData[outerEdgeIndex2].vNext = twinEdgeIndex;
            edgeData[twinEdgeIndex].vNext   = innerEdgeIndex1;

            // Remove second outer edge from next face linked list by skipping over it.
            edgeData[edgeIndex].fNext = outerEdgeIndex2;

            // Insert second outer edge into the previous face linked list.
            edgeData[outerEdgeIndex0].fNext = outerEdgeIndex1;
            edgeData[outerEdgeIndex1].fNext = twinEdgeIndex;

            // Reroot the vertex and face that just lost edges with edges guaranteed to still belong.
            vertexFirstEdgeIndices[oldVertexIndex] = outerEdgeIndex1;
            faceFirstEdgeIndices[nextFaceIndex]    = edgeIndex;

            // Adjust neighbor counts.
            vertexNeighborCounts[oldVertexIndex] -= 1;
            vertexNeighborCounts[newVertexIndex] += 1;
            faceNeighborCounts[nextFaceIndex]    -= 1;         // Dropping below 0 is undefined behavior; it better not ever happen.
            faceNeighborCounts[prevFaceIndex]    += 1;         // Surpassing 32767 is undefined behavior; it better not ever happen.

            // Adjust edge wrap information, coallescing multiple edges together as appropriate.
            edgeData[edgeIndex].wrap       = EdgeWrapUtility.ModifyTargetVertEdgeRelations(edgeData[edgeIndex].wrap, edgeData[outerEdgeIndex1].wrap);       // Edge's target vertex changed, according to Inner Edge 1.
            edgeData[twinEdgeIndex].wrap   = EdgeWrapUtility.ModifySourceVertEdgeRelations(edgeData[twinEdgeIndex].wrap, edgeData[innerEdgeIndex1].wrap);   // Twin Edge's source vertex changed, according to Outer Edge 1.
            edgeData[innerEdgeIndex1].wrap = EdgeWrapUtility.ModifyTargetFaceEdgeRelations(edgeData[innerEdgeIndex1].wrap, edgeData[edgeIndex].wrap);       // Inner Edge 1's target face changed, according to Twin Edge.
            edgeData[outerEdgeIndex1].wrap = EdgeWrapUtility.ModifySourceFaceEdgeRelations(edgeData[outerEdgeIndex1].wrap, edgeData[twinEdgeIndex].wrap);   // Outer Edge 1's source face changed, according to Edge.
        }
示例#3
0
        /// <summary>
        /// Build a full topology data structure from the minimal data described by a face neighbor indexer.
        /// </summary>
        /// <param name="indexer">A minimal description of the faces and their neighbors constituting a topology.</param>
        /// <returns>A fully constructed topology matching the description of the provided face neighbor indexer.</returns>
        /// <remarks>
        /// <para>For the edge wrap data returned by the face neighbor indexer, only the vertex-to-edge, edge-to-vertex,
        /// and face-to-edge data needs to be supplied.  If there are no external faces, then the edge-to-vertex data is
        /// also unnecessary.</para>
        /// </remarks>
        public static Topology BuildTopology(IFaceNeighborIndexer indexer)
        {
            var vertexNeighborCounts   = new ushort[indexer.vertexCount];
            var vertexFirstEdgeIndices = new int[indexer.vertexCount];
            var edgeData             = new Topology.EdgeData[indexer.edgeCount];
            var faceNeighborCounts   = new ushort[indexer.faceCount];
            var faceFirstEdgeIndices = new int[indexer.faceCount];

            // Initialize the face roots and neighbor counts, and the next vertex, fNext, vNext, and wrap
            // fields of the internal edges.  The vNext fields will result in linked lists that contain the
            // correct members (aside from external edges), but in an unspecified order.
            int edgeIndex = 0;

            for (int faceIndex = 0; faceIndex < indexer.internalFaceCount; ++faceIndex)
            {
                var neighborCount = indexer.GetNeighborCount(faceIndex);
                faceNeighborCounts[faceIndex]   = neighborCount;
                faceFirstEdgeIndices[faceIndex] = edgeIndex;

                var priorVertexIndex = indexer.GetNeighborVertexIndex(faceIndex, neighborCount - 1);
                for (int neighborIndex = 0; neighborIndex < neighborCount; ++neighborIndex)
                {
                    var vertexIndex = indexer.GetNeighborVertexIndex(faceIndex, neighborIndex);
                    edgeData[edgeIndex].vertex = vertexIndex;
                    edgeData[edgeIndex].fNext  = edgeIndex + 1;
                    edgeData[edgeIndex].face   = -1;
                    edgeData[edgeIndex].twin   = -1;
                    edgeData[edgeIndex].wrap   = indexer.GetEdgeWrap(faceIndex, neighborIndex);

                    AddEdgeToVertexUnordered(edgeIndex, priorVertexIndex, vertexNeighborCounts, vertexFirstEdgeIndices, edgeData);
                    priorVertexIndex = vertexIndex;
                    ++edgeIndex;
                }

                // Correct the face's last edge to refer back to the face's first edge.
                edgeData[edgeIndex - 1].fNext = edgeIndex - neighborCount;
            }

            var internalEdgeCount = edgeIndex;

            // Use the partial information constructed in the prior loop to determine edge twins, and set
            // the vertex, fNext, vNext, and wrap fields for external edges too.
            edgeIndex = 0;
            var externalEdgeIndex = internalEdgeCount;

            for (int faceIndex = 0; faceIndex < indexer.internalFaceCount; ++faceIndex)
            {
                var neighborCount    = indexer.GetNeighborCount(faceIndex);
                var priorVertexIndex = indexer.GetNeighborVertexIndex(faceIndex, neighborCount - 1);
                for (int neighborIndex = 0; neighborIndex < neighborCount; ++neighborIndex)
                {
                    var vertexIndex = indexer.GetNeighborVertexIndex(faceIndex, neighborIndex);

                    if (edgeData[edgeIndex].twin == -1)
                    {
                        // The current edge is pointing at the current vertex index.  Its twin will be one
                        // of the edges pointing out from the current vertex, and will be pointing at the
                        // prior vertex.  Search for this edge.
                        var vertexFirstEdgeIndex = vertexFirstEdgeIndices[vertexIndex];
                        var vertexEdgeIndex      = vertexFirstEdgeIndex;
                        while (edgeData[vertexEdgeIndex].vertex != priorVertexIndex)
                        {
                            vertexEdgeIndex = edgeData[vertexEdgeIndex].vNext;
                            if (vertexEdgeIndex == vertexFirstEdgeIndex)
                            {
                                // This edge's twin is an external edge which needs to be initialized.
                                edgeData[externalEdgeIndex].vertex = priorVertexIndex;
                                edgeData[externalEdgeIndex].fNext  = -1;                                // Cannot be determined until the vNext linked lists are in the correct order.
                                edgeData[externalEdgeIndex].face   = -1;
                                edgeData[externalEdgeIndex].wrap   = EdgeWrapUtility.Invert(edgeData[edgeIndex].wrap);

                                AddEdgeToVertexUnordered(externalEdgeIndex, vertexIndex, vertexNeighborCounts, vertexFirstEdgeIndices, edgeData);

                                vertexEdgeIndex = externalEdgeIndex;
                                ++externalEdgeIndex;
                                break;
                            }
                        }
                        edgeData[vertexEdgeIndex].twin = edgeIndex;
                        edgeData[edgeIndex].twin       = vertexEdgeIndex;
                    }

                    ++edgeIndex;
                    priorVertexIndex = vertexIndex;
                }
            }

            // Correct the order of the vNext linked lists, and set the face indices of the twins of all internal edges.
            edgeIndex = 0;
            for (int faceIndex = 0; faceIndex < indexer.internalFaceCount; ++faceIndex)
            {
                var neighborCount  = indexer.GetNeighborCount(faceIndex);
                var priorEdgeIndex = edgeIndex + neighborCount - 1;
                for (int neighborIndex = 0; neighborIndex < neighborCount; ++neighborIndex)
                {
                    var twinEdgeIndex = edgeData[priorEdgeIndex].twin;
                    PutEdgesInSequence(edgeIndex, twinEdgeIndex, edgeData);

                    edgeData[twinEdgeIndex].face = faceIndex;

                    priorEdgeIndex = edgeIndex;
                    ++edgeIndex;
                }
            }

            if (indexer.externalFaceCount > 0)
            {
                // Now that all vertex and edge relationships are established, locate all edges that don't have a
                // face relationship specified (those which are pointing out toward external faces) and spin around
                // each face to establish those edge -> face relationships and count external face neighbors.
                var faceIndex = indexer.internalFaceCount;
                for (edgeIndex = 0; edgeIndex < indexer.edgeCount; ++edgeIndex)
                {
                    if (edgeData[edgeIndex].face == -1)
                    {
                        // Starting with the current edge, follow the edge links appropriately to wind around the
                        // implicit face counter-clockwise and link the edges to the face.
                        var neighborCount      = 0;
                        var twinEdgeIndex      = edgeIndex;
                        var faceEdgeIndex      = edgeData[edgeIndex].twin;
                        var faceFirstEdgeIndex = faceEdgeIndex;
                        do
                        {
                            edgeData[twinEdgeIndex].face = faceIndex;
                            twinEdgeIndex = edgeData[faceEdgeIndex].vNext;
                            var nextFaceEdgeIndex = edgeData[twinEdgeIndex].twin;
                            edgeData[nextFaceEdgeIndex].fNext = faceEdgeIndex;
                            faceEdgeIndex = nextFaceEdgeIndex;
                            ++neighborCount;
                            if (neighborCount > vertexFirstEdgeIndices.Length)
                            {
                                throw new InvalidOperationException("Face neighbors were specified such that an external face was misconfigured.");
                            }
                        } while (twinEdgeIndex != edgeIndex);

                        faceNeighborCounts[faceIndex]   = (ushort)neighborCount;
                        faceFirstEdgeIndices[faceIndex] = faceFirstEdgeIndex;
                        ++faceIndex;
                    }
                }
            }

            // Fill out the remainder of the edge wrap data, based on the possibly limited info supplied.
            for (edgeIndex = 0; edgeIndex < indexer.edgeCount; ++edgeIndex)
            {
                var twinIndex = edgeData[edgeIndex].twin;
                if (edgeIndex < twinIndex)
                {
                    EdgeWrapUtility.CrossMergeTwins(ref edgeData[edgeIndex].wrap, ref edgeData[twinIndex].wrap);
                }
            }

            return(Topology.Create(vertexNeighborCounts, vertexFirstEdgeIndices, edgeData, faceNeighborCounts, faceFirstEdgeIndices, indexer.internalFaceCount));
        }
 /// <inheritdoc/>
 public override Vector3 ReverseOffsetFaceToFaceAttribute(Vector3 position, EdgeWrap edgeWrap)
 {
     return(ReverseOffsetAttribute(position, EdgeWrapUtility.FaceToFaceAsGeneric(edgeWrap)));
 }
 /// <inheritdoc/>
 public override Vector3 ReverseOffsetVertToVertAttribute(Vector3 position, EdgeWrap edgeWrap)
 {
     return(ReverseOffsetAttribute(position, EdgeWrapUtility.VertToVertAsGeneric(edgeWrap)));
 }
 /// <inheritdoc/>
 public override Vector3 OffsetFaceToVertAttribute(Vector3 position, EdgeWrap edgeWrap)
 {
     return(OffsetAttribute(position, EdgeWrapUtility.FaceToVertAsGeneric(edgeWrap)));
 }
示例#7
0
        /// <summary>
        /// Exports the given manifold to SVG format with the given style, flattening it onto a plane if necessary.
        /// </summary>
        /// <param name="writer">The writer to which the SVG-formatted manifold will be written.</param>
        /// <param name="surface">The surface describing the manifold.</param>
        /// <param name="topology">The topology of the manifold.</param>
        /// <param name="orientation">The orientation of the plane to which the manifold will be flattened.</param>
        /// <param name="scale">The scale which will be applied to the manifold while writing out SVG positions.</param>
        /// <param name="vertexPositions">The vertex positions of the manifold.</param>
        /// <param name="numericFormat">The format specifier to use for each number written to <paramref name="writer"/>.</param>
        /// <param name="style">The style details to apply to the SVG content.</param>
        public static void ExportToSVG(System.IO.TextWriter writer, ISurface surface, Topology topology, Quaternion orientation, Vector3 scale, IVertexAttribute <Vector3> vertexPositions, string numericFormat, SVGStyle style)
        {
            var inverseOrientation = Quaternion.Inverse(orientation);

            var plane = new Plane(orientation * Vector3.back, 0f);

            var transform = Matrix4x4.TRS(Vector3.zero, inverseOrientation, scale);

            Vector2 transformedVertexDotRadius = new Vector2(style.vertexCircleRadius * transform.m00, style.vertexCircleRadius * transform.m11);

            Func <Vector3, Vector2> flatten = (Vector3 point) => transform *plane.ClosestPoint(point);

            Vector2 min = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
            Vector2 max = new Vector2(float.NegativeInfinity, float.NegativeInfinity);

            foreach (var face in topology.faces)
            {
                foreach (var edge in face.edges)
                {
                    var p = flatten(vertexPositions[edge]);
                    min = Geometry.AxisAlignedMin(min, p);
                    max = Geometry.AxisAlignedMax(max, p);
                }
            }

            var range = max - min;

            flatten = (Vector3 point) =>
            {
                Vector2 p = transform * plane.ClosestPoint(point);
                p.y = max.y - p.y + min.y;
                return(p);
            };

            writer.WriteLine("<svg viewBox=\"{0} {1} {2} {3}\" xmlns=\"http://www.w3.org/2000/svg\">",
                             (min.x - style.padding.x).ToString(numericFormat), (min.y - style.padding.y).ToString(numericFormat),
                             (range.x + style.padding.x * 2f).ToString(numericFormat), (range.y + style.padding.y * 2f).ToString(numericFormat));

            writer.WriteLine("\t<style>");

            writer.WriteLine("\t\tsvg");
            writer.WriteLine("\t\t{");
            if (!style.svg.ContainsKey("width"))
            {
                writer.WriteLine("\t\t\twidth: 100%;");
            }
            if (!style.svg.ContainsKey("height"))
            {
                writer.WriteLine("\t\t\theight: 100%;");
            }
            foreach (var keyValue in style.svg)
            {
                writer.WriteLine("\t\t\t{0}: {1};", keyValue.Key, keyValue.Value);
            }
            writer.WriteLine("\t\t}");

            writer.WriteLine("\t\ttext.index");
            writer.WriteLine("\t\t{");
            foreach (var keyValue in style.index)
            {
                writer.WriteLine("\t\t\t{0}: {1};", keyValue.Key, keyValue.Value);
            }
            writer.WriteLine("\t\t}");

            writer.WriteLine("\t\tellipse.vertex");
            writer.WriteLine("\t\t{");
            if (!style.vertexCircle.ContainsKey("stroke"))
            {
                writer.WriteLine("\t\t\tstroke: black;");
            }
            if (!style.vertexCircle.ContainsKey("stroke-width"))
            {
                writer.WriteLine("\t\t\tstroke-width: 1;");
            }
            if (!style.vertexCircle.ContainsKey("fill"))
            {
                writer.WriteLine("\t\t\tfill: none;");
            }
            foreach (var keyValue in style.vertexCircle)
            {
                writer.WriteLine("\t\t\t{0}: {1};", keyValue.Key, keyValue.Value);
            }
            writer.WriteLine("\t\t}");

            writer.WriteLine("\t\ttext.vertex.index");
            writer.WriteLine("\t\t{");
            if (!style.vertexIndex.ContainsKey("fill"))
            {
                writer.WriteLine("\t\t\tfill: black;");
            }
            if (!style.vertexIndex.ContainsKey("font-size"))
            {
                writer.WriteLine("\t\t\tfont-size: {0:F0}px;", transformedVertexDotRadius.x);
            }
            if (!style.vertexIndex.ContainsKey("text-anchor"))
            {
                writer.WriteLine("\t\t\ttext-anchor: middle;");
            }
            if (!style.vertexIndex.ContainsKey("dominant-baseline"))
            {
                writer.WriteLine("\t\t\tdominant-baseline: central;");
            }
            foreach (var keyValue in style.vertexIndex)
            {
                writer.WriteLine("\t\t\t{0}: {1};", keyValue.Key, keyValue.Value);
            }
            writer.WriteLine("\t\t}");

            writer.WriteLine("\t\tpolygon.face");
            writer.WriteLine("\t\t{");
            if (!style.facePolygon.ContainsKey("fill"))
            {
                writer.WriteLine("\t\t\tfill: #CCC;");
            }
            if (!style.facePolygon.ContainsKey("stroke"))
            {
                writer.WriteLine("\t\t\tstroke: none;");
            }
            foreach (var keyValue in style.facePolygon)
            {
                writer.WriteLine("\t\t\t{0}: {1};", keyValue.Key, keyValue.Value);
            }
            writer.WriteLine("\t\t}");

            writer.WriteLine("\t\ttext.face.index");
            writer.WriteLine("\t\t{");
            if (!style.faceIndex.ContainsKey("fill"))
            {
                writer.WriteLine("\t\t\tfill: black;");
            }
            if (!style.faceIndex.ContainsKey("font-size"))
            {
                writer.WriteLine("\t\t\tfont-size: {0:F0}px;", transformedVertexDotRadius.x * 2f);
            }
            if (!style.faceIndex.ContainsKey("text-anchor"))
            {
                writer.WriteLine("\t\t\ttext-anchor: middle;");
            }
            if (!style.faceIndex.ContainsKey("dominant-baseline"))
            {
                writer.WriteLine("\t\t\tdominant-baseline: central;");
            }
            foreach (var keyValue in style.faceIndex)
            {
                writer.WriteLine("\t\t\t{0}: {1};", keyValue.Key, keyValue.Value);
            }
            writer.WriteLine("\t\t}");

            writer.WriteLine("\t\tpolyline.edge");
            writer.WriteLine("\t\t{");
            if (!style.edgePath.ContainsKey("stroke"))
            {
                writer.WriteLine("\t\t\tstroke: black;");
            }
            if (!style.edgePath.ContainsKey("stroke-width"))
            {
                writer.WriteLine("\t\t\tstroke-width: 1;");
            }
            if (!style.edgePath.ContainsKey("stroke-linecap"))
            {
                writer.WriteLine("\t\t\tstroke-linecap: square;");
            }
            if (!style.edgePath.ContainsKey("stroke-linejoin"))
            {
                writer.WriteLine("\t\t\tstroke-linejoin: miter;");
            }
            if (!style.edgePath.ContainsKey("fill"))
            {
                writer.WriteLine("\t\t\tfill: none;");
            }
            foreach (var keyValue in style.edgePath)
            {
                writer.WriteLine("\t\t\t{0}: {1};", keyValue.Key, keyValue.Value);
            }
            writer.WriteLine("\t\t}");

            writer.WriteLine("\t\ttext.edge.index");
            writer.WriteLine("\t\t{");
            if (!style.edgeIndex.ContainsKey("fill"))
            {
                writer.WriteLine("\t\t\tfill: black;");
            }
            if (!style.edgeIndex.ContainsKey("font-size"))
            {
                writer.WriteLine("\t\t\tfont-size: {0:F0}px;", style.edgeIndexOffset * Mathf.Sqrt(transform.m00 * transform.m11));
            }
            if (!style.edgeIndex.ContainsKey("text-anchor"))
            {
                writer.WriteLine("\t\t\ttext-anchor: middle;");
            }
            if (!style.edgeIndex.ContainsKey("dominant-baseline"))
            {
                writer.WriteLine("\t\t\tdominant-baseline: central;");
            }
            foreach (var keyValue in style.edgeIndex)
            {
                writer.WriteLine("\t\t\t{0}: {1};", keyValue.Key, keyValue.Value);
            }
            writer.WriteLine("\t\t}");

            writer.WriteLine("\t</style>");

            var facePointFormat   = string.Format("{{0:{0}}},{{1:{0}}}", numericFormat);
            var faceIndexFormat   = string.Format("\t<text class=\"face index\" x=\"{{0:{0}}}\" y=\"{{1:{0}}}\">{{2}}</text>", numericFormat);
            var arrowFormat       = string.Format("\t<polyline class=\"edge{{6}}\" points=\"{{0:{0}}},{{1:{0}}} {{2:{0}}},{{3:{0}}} {{4:{0}}},{{5:{0}}}\" />", numericFormat);
            var edgeIndexFormat   = string.Format("\t<text class=\"edge index{{3}}\" x=\"{{0:{0}}}\" y=\"{{1:{0}}}\">{{2}}</text>", numericFormat);
            var vertexFormat      = string.Format("\t<ellipse class=\"vertex{{4}}\" cx=\"{{0:{0}}}\" cy=\"{{1:{0}}}\" rx=\"{{2:{0}}}\" ry=\"{{3:{0}}}\" />", numericFormat);
            var vertexIndexFormat = string.Format("\t<text class=\"vertex index{{3}}\" x=\"{{0:{0}}}\" y=\"{{1:{0}}}\">{{2}}</text>", numericFormat);

            var centroids = FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions);
            var bisectors = EdgeAttributeUtility.CalculateFaceEdgeBisectorsFromVertexPositions(topology.internalFaces, surface, vertexPositions, centroids);

            writer.WriteLine();
            writer.WriteLine("\t<!-- Faces -->");

            foreach (var face in topology.internalFaces)
            {
                writer.Write("\t<polygon class=\"face\" points=\"");
                foreach (var edge in face.edges)
                {
                    if (edge != face.firstEdge)
                    {
                        writer.Write(" ");
                    }
                    var p = flatten(vertexPositions[edge] + bisectors[edge] * style.faceInset);
                    writer.Write(facePointFormat, p.x, p.y);
                }
                writer.WriteLine("\" />");

                if (style.showFaceIndices)
                {
                    var p = flatten(centroids[face]);
                    writer.WriteLine(faceIndexFormat, p.x, p.y, face.index);
                }
            }

            writer.WriteLine();
            writer.WriteLine("\t<!-- Edges -->");

            foreach (var face in topology.faces)
            {
                foreach (var edge in face.edges)
                {
                    ExportEdgeToSVG(writer, surface, edge, edge.prev, edge, flatten, vertexPositions, arrowFormat, edgeIndexFormat, style, " not-wrapped");
                    if ((edge.wrap & EdgeWrap.FaceToFace) != EdgeWrap.None)
                    {
                        ExportEdgeToSVG(writer, surface, edge, edge.twin, edge.twin.vertexEdge.next.twin.faceEdge, flatten, vertexPositions, arrowFormat, edgeIndexFormat, style, " wrapped");
                    }
                }
            }

            writer.WriteLine();
            writer.WriteLine("\t<!-- Vertices -->");

            foreach (var vertex in topology.vertices)
            {
                bool neitherAxis = false;
                bool axis0       = false;
                bool axis1       = false;
                bool bothAxes    = false;
                foreach (var edge in vertex.edges)
                {
                    var faceEdge = edge.twin.faceEdge;
                    var wrap     = faceEdge.wrap & EdgeWrap.FaceToVert;
                    if (EdgeWrapUtility.WrapsOnNeitherAxis(wrap))
                    {
                        if (!neitherAxis)
                        {
                            neitherAxis = true;
                            ExportVertexToSVG(writer, vertex, faceEdge, flatten, vertexPositions, transformedVertexDotRadius, vertexFormat, vertexIndexFormat, style, " wrapped-neither");
                        }
                    }
                    else if (EdgeWrapUtility.WrapsOnOnlyAxis0(wrap))
                    {
                        if (!axis0)
                        {
                            axis0 = true;
                            ExportVertexToSVG(writer, vertex, faceEdge, flatten, vertexPositions, transformedVertexDotRadius, vertexFormat, vertexIndexFormat, style, " wrapped-axis-0");
                        }
                    }
                    else if (EdgeWrapUtility.WrapsOnOnlyAxis1(wrap))
                    {
                        if (!axis1)
                        {
                            axis1 = true;
                            ExportVertexToSVG(writer, vertex, faceEdge, flatten, vertexPositions, transformedVertexDotRadius, vertexFormat, vertexIndexFormat, style, " wrapped-axis-1");
                        }
                    }
                    else
                    {
                        if (!bothAxes)
                        {
                            bothAxes = true;
                            ExportVertexToSVG(writer, vertex, faceEdge, flatten, vertexPositions, transformedVertexDotRadius, vertexFormat, vertexIndexFormat, style, " wrapped-both");
                        }
                    }
                }
            }

            writer.WriteLine("</svg>");
        }
示例#8
0
        /// <inheritdoc/>
        public EdgeWrap GetEdgeWrap(int faceIndex, int neighborIndex)
        {
            if (faceIndex < 0 || faceIndex >= _internalFaceCount)
            {
                throw new ArgumentOutOfRangeException("faceIndex");
            }

            if (!_wrapRows && !_wrapCols)
            {
                return(EdgeWrap.None);
            }

            EdgeWrap wrap;

            switch (!_reverseWindingOrder ? neighborIndex : (6 - neighborIndex) % 6)
            {
            case 0: wrap =
                (WrapsOnSidePosRow(faceIndex) && !IsBackIndentedLower(faceIndex) ? EdgeWrap.NegVertToEdgeAxis0 : EdgeWrap.None);
                break;

            case 1: wrap =
                (WrapsOnSidePosCol(faceIndex) ? EdgeWrap.PosEdgeToVertAxis1 : EdgeWrap.None);
                break;

            case 2: wrap =
                (WrapsOnSidePosCol(faceIndex) ? EdgeWrap.PosFaceToEdgeAxis1 : EdgeWrap.None) |
                (WrapsOnSidePosRow(faceIndex) && !IsBackIndentedUpper(faceIndex) ? EdgeWrap.PosEdgeToVertAxis0 : EdgeWrap.None);
                break;

            case 3: wrap =
                (WrapsOnSidePosCol(faceIndex) ? EdgeWrap.PosFaceToEdgeAxis1 : EdgeWrap.None) |
                (WrapsOnSidePosRow(faceIndex) && !IsBackIndentedUpper(faceIndex) ? EdgeWrap.PosFaceToEdgeAxis0 : EdgeWrap.None) |
                (WrapsOnSidePosRow(faceIndex) && IsBackIndentedUpper(faceIndex) ? EdgeWrap.PosEdgeToVertAxis0 : EdgeWrap.None);
                break;

            case 4: wrap =
                (WrapsOnSidePosRow(faceIndex) ? EdgeWrap.PosFaceToEdgeAxis0 : EdgeWrap.None) |
                (WrapsOnSidePosCol(faceIndex) ? EdgeWrap.NegVertToEdgeAxis1 : EdgeWrap.None);
                break;

            case 5: wrap =
                (WrapsOnSidePosRow(faceIndex) && !IsBackIndentedLower(faceIndex) ? EdgeWrap.PosFaceToEdgeAxis0 : EdgeWrap.None) |
                (WrapsOnSidePosRow(faceIndex) && IsBackIndentedLower(faceIndex) ? EdgeWrap.NegVertToEdgeAxis0 : EdgeWrap.None);
                break;

            default:
                throw new ArgumentOutOfRangeException("neighborIndex");
            }

            if (_reverseWindingOrder)
            {
                wrap = EdgeWrapUtility.InvertVertexEdgeRelations(wrap);
            }

            if (_reverseColumnsRows)
            {
                wrap = EdgeWrapUtility.SwapAxes(wrap);
            }

            return(wrap);
        }