public void CreateOctahedron(float radius)
 {
     surface = SphericalSurface.Create(Vector3.up, Vector3.right, radius);
     Vector3[] vertexPositionsArray;
     SphericalManifoldUtility.CreateOctahedron((SphericalSurface)surface, out topology, out vertexPositionsArray);
     vertexPositions = vertexPositionsArray.AsVertexAttribute();
     facePositions   = FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions);
     partitioning    = UniversalFaceSpatialPartitioning.Create(surface, topology, vertexPositions);
 }
Exemple #2
0
        protected void Start()
        {
            var hexGridSurface = RectangularHexGrid.Create(
                HexGridDescriptor.CreateSideUp(true, HexGridAxisStyles.StaggeredSymmetric),
                Vector3.zero, Quaternion.Euler(90f, 0f, 0f),
                false, false,
                new IntVector2(topologyWidth, topologyHeight));

            _surface = hexGridSurface;

            Vector3[] vertexPositionsArray;
            _topology            = hexGridSurface.CreateManifold(out vertexPositionsArray);
            _vertexPositions     = vertexPositionsArray.AsVertexAttribute();
            _facePositions       = FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(_topology.internalFaces, _vertexPositions);
            _faceCornerBisectors = EdgeAttributeUtility.CalculateFaceEdgeBisectorsFromVertexPositions(_topology.internalFaces, _vertexPositions, _facePositions);
            _faceBlockedStates   = new bool[_topology.internalFaces.Count].AsFaceAttribute();

            var triangulation = new SeparatedFacesUmbrellaTriangulation(2,
                                                                        (Topology.FaceEdge edge, DynamicMesh.IIndexedVertexAttributes vertexAttributes) =>
            {
                vertexAttributes.position = _vertexPositions[edge];
                vertexAttributes.normal   = Vector3.up;
                vertexAttributes.color    = borderColor;
                vertexAttributes.Advance();

                vertexAttributes.position = _vertexPositions[edge] + _faceCornerBisectors[edge] * 0.05f;
                vertexAttributes.normal   = (vertexAttributes.position + Vector3.up - _facePositions[edge.nearFace]).normalized;
                vertexAttributes.color    = normalColor;
                vertexAttributes.Advance();
            },
                                                                        (Topology.Face face, DynamicMesh.IIndexedVertexAttributes vertexAttributes) =>
            {
                vertexAttributes.position = _facePositions[face];
                vertexAttributes.normal   = Vector3.up;
                vertexAttributes.color    = normalColor;
                vertexAttributes.Advance();
            });

            _dynamicMesh = DynamicMesh.Create(
                _topology.enumerableInternalFaces,
                DynamicMesh.VertexAttributes.Position |
                DynamicMesh.VertexAttributes.Normal |
                DynamicMesh.VertexAttributes.Color,
                triangulation);

            foreach (var mesh in _dynamicMesh.submeshes)
            {
                var meshFilter = Instantiate(submeshPrefab);
                meshFilter.mesh = mesh;
                meshFilter.transform.SetParent(transform, false);
            }

            var partioning = UniversalFaceSpatialPartitioning.Create(_surface, _topology, _vertexPositions);
            var picker     = GetComponent <FaceSpatialPartitioningPicker>();

            picker.partitioning = partioning;
        }
        /// <summary>
        /// Determines the total amount of change between original vertex positions and relaxed vertex positions.
        /// </summary>
        /// <param name="originalVertexPositions">The original vertex positions, before being relaxed.</param>
        /// <param name="relaxedVertexPositions">The relaxed vertex positions.</param>
        /// <returns>The total amount of change between the original and relaxed vertex positions, as a sum of distance deltas.</returns>
        public static float CalculateRelaxationAmount(IVertexAttribute <Vector3> originalVertexPositions, IVertexAttribute <Vector3> relaxedVertexPositions)
        {
            float relaxationAmount = 0f;

            for (int i = 0; i < originalVertexPositions.Count; ++i)
            {
                relaxationAmount += (originalVertexPositions[i] - relaxedVertexPositions[i]).magnitude;
            }
            return(relaxationAmount);
        }
Exemple #4
0
        private void CenterCamera(IVertexAttribute <Vector3> vertexPositions)
        {
            var bounds = new Bounds(vertexPositions[0], Vector3.zero);

            foreach (var vertexPosition in vertexPositions)
            {
                bounds.Encapsulate(vertexPosition);
            }

            Camera.main.transform.position = new Vector3(bounds.center.x - Camera.main.orthographicSize / 3f, bounds.center.y, Camera.main.transform.position.z);
        }
Exemple #5
0
            public static Partition Create(Topology.FaceEdge edge, IVertexAttribute <Vector3> vertexPositions, Surface surface)
            {
                var prevEdge    = edge.prev;
                var vPos0       = vertexPositions[prevEdge];
                var vPos1       = vertexPositions[edge];
                var edgeVector  = vPos1 - vPos0;
                var edgeNormal  = surface.GetNormal(vPos0);
                var planeNormal = Vector3.Cross(edgeVector, edgeNormal).normalized;

                return(new Partition(planeNormal, vPos0, edge.twinIndex, edge.index));
            }
Exemple #6
0
        private void CreateHexGrid2(out Topology topology, out IVertexAttribute <Vector3> vertexPositions)
        {
            Vector3[] vertexPositionsArray;
            var       surface = RectangularHexGrid.Create(
                HexGridDescriptor.CreateSideUp(true, HexGridAxisStyles.StaggeredSymmetric),
                Vector3.zero, Quaternion.identity,
                false, false,
                new IntVector2(11, 10));

            topology        = surface.CreateManifold(out vertexPositionsArray);
            vertexPositions = PositionalVertexAttribute.Create(surface, vertexPositionsArray);
        }
        public void CreateSubdividedDodecahedron(float radius)
        {
            surface = SphericalSurface.Create(Vector3.up, Vector3.right, radius);
            Topology baseTopology;

            Vector3[] baseVertexPositionsArray;
            SphericalManifoldUtility.CreateIcosahedron((SphericalSurface)surface, out baseTopology, out baseVertexPositionsArray);
            Vector3[] vertexPositionsArray;
            SphericalManifoldUtility.Subdivide((SphericalSurface)surface, baseTopology, baseVertexPositionsArray.AsVertexAttribute(), 5, out topology, out vertexPositionsArray);
            SphericalManifoldUtility.MakeDual((SphericalSurface)surface, topology, ref vertexPositionsArray);
            vertexPositions = vertexPositionsArray.AsVertexAttribute();
            facePositions   = FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions);
            partitioning    = UniversalFaceSpatialPartitioning.Create(surface, topology, vertexPositions);
        }
        /// <summary>
        /// Creates a new topology based on the one provided, subdividing each face into multiple smaller faces, and adding extra vertices and edges accordingly.  Uses spherical linear interpolation for deriving the positions of new vertices.
        /// </summary>
        /// <param name="surface">The spherical surface describing the overall shape of the manifold.</param>
        /// <param name="topology">The original topology to be subdivided.  Cannot contain internal faces with neighbor counts of any value other than 3 or 4.</param>
        /// <param name="vertexPositions">The positions of the original topology's vertices.</param>
        /// <param name="degree">The degree of subdivision, equivalent to the number of additional vertices that will be added along each original edge.  Must be non-negative, and a value of zero will result in an exact duplicate with no subdivision.</param>
        /// <param name="subdividedTopology">The copied and subdivided topology.</param>
        /// <param name="subdividedVertexPositions">The positions of the subdivided vertices.</param>
        public static void Subdivide(SphericalSurface surface, Topology topology, IVertexAttribute <Vector3> vertexPositions, int degree, out Topology subdividedTopology, out Vector3[] subdividedVertexPositions)
        {
            Func <Vector3, Vector3, float, Vector3> interpolator;

            if (surface.radius == 1f)
            {
                interpolator = (Vector3 p0, Vector3 p1, float t) => { return(Geometry.SlerpUnitVectors(p0, p1, t)); };
            }
            else
            {
                interpolator = (Vector3 p0, Vector3 p1, float t) => { return(Geometry.SlerpUnitVectors(p0 / surface.radius, p1 / surface.radius, t) * surface.radius); };
            }
            ManifoldUtility.Subdivide(topology, vertexPositions, degree, interpolator, out subdividedTopology, out subdividedVertexPositions);
        }
        public void CreateDistortedHexGrid(int width, int height, int seed)
        {
            var hexGrid = RectangularHexGrid.Create(HexGridDescriptor.standardCornerUp, Vector3.zero, Quaternion.identity, false, false, new IntVector2(width, height));

            surface = hexGrid;
            Vector3[] vertexPositionsArray;
            topology        = hexGrid.CreateManifold(out vertexPositionsArray);
            vertexPositions = vertexPositionsArray.AsVertexAttribute();
            var random          = XorShift128Plus.Create(seed);
            var maxOffsetRadius = Mathf.Sqrt(3f) / 8f;

            foreach (var vertex in topology.vertices)
            {
                if (!vertex.hasExternalFaceNeighbor)
                {
                    vertexPositions[vertex] += (Vector3)random.PointWithinCircle(maxOffsetRadius);
                }
            }
            facePositions = FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions);
            partitioning  = UniversalFaceSpatialPartitioning.Create(surface, topology, vertexPositions);
        }
        public void CreateDistortedQuadGrid(int width, int height, int seed)
        {
            var quadGrid = RectangularQuadGrid.Create(Vector2.right, Vector2.up, Vector3.zero, Quaternion.identity, false, false, new IntVector2(width, height));

            surface = quadGrid;
            Vector3[] vertexPositionsArray;
            topology        = quadGrid.CreateManifold(out vertexPositionsArray);
            vertexPositions = vertexPositionsArray.AsVertexAttribute();
            var random          = XorShift128Plus.Create(seed);
            var maxOffsetRadius = Mathf.Sqrt(2f) / 5f;

            for (int y = 1; y < height; ++y)
            {
                for (int x = 1; x < width; ++x)
                {
                    vertexPositions[quadGrid.GetVertexIndex(x, y)] += (Vector3)random.PointWithinCircle(maxOffsetRadius);
                }
            }
            facePositions = FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions);
            partitioning  = UniversalFaceSpatialPartitioning.Create(surface, topology, vertexPositions);
        }
        private static void ClipNear(Vertex prev, Vertex cur, List <Vertex> clipped)
        {
            bool prevVisible = prev.Position.Z >= -prev.Position.W;
            bool curVisible  = cur.Position.Z >= -cur.Position.W;

            if (prevVisible ^ curVisible)
            {
                Vertex v     = new Vertex();
                float  alpha = GetInterpolator(-prev.Position.W, -cur.Position.W, prev.Position.Z, cur.Position.Z);
                v.Position = MathHelper.Lerp(prev.Position, cur.Position, alpha);
                foreach (var key in prev.Attribute)
                {
                    IVertexAttribute data = MathHelper.Lerp(prev.Attribute[key], cur.Attribute[key], alpha);
                    v.Attribute.Add(key, data);
                }
                ClipFar(prev, v, clipped);
            }
            if (curVisible)
            {
                ClipFar(prev, cur, clipped);
            }
        }
Exemple #12
0
        private void CreateGrid(out Topology topology, out IVertexAttribute <Vector3> vertexPositions, out IFaceAttribute <Vector3> facePositions)
        {
            if (squareGridToggle.isOn)
            {
                CreateSquareGrid(out topology, out vertexPositions);
            }
            else if (hexGrid1Toggle.isOn)
            {
                CreateHexGrid1(out topology, out vertexPositions);
            }
            else if (hexGrid2Toggle.isOn)
            {
                CreateHexGrid2(out topology, out vertexPositions);
            }
            else
            {
                CreateJumbledGrid(out topology, out vertexPositions);
            }

            _topology        = topology;
            _vertexPositions = vertexPositions;

            facePositions = FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions);
        }
        /// <summary>
        /// Creates a new topology based on the one provided, subdividing each face into multiple smaller faces, and adding extra vertices and edges accordingly.
        /// </summary>
        /// <param name="topology">The original topology to be subdivided.  Cannot contain internal faces with neighbor counts of any value other than 3 or 4.</param>
        /// <param name="vertexPositions">The positions of the original topology's vertices.</param>
        /// <param name="degree">The degree of subdivision, equivalent to the number of additional vertices that will be added along each original edge.  Must be non-negative, and a value of zero will result in an exact duplicate with no subdivision.</param>
        /// <param name="interpolator">The function that interpolates between two vertex positions along an edge for determining the positions of new vertices.  Particularly useful for curved surfaces.</param>
        /// <param name="subdividedTopology">The copied and subdivided topology.</param>
        /// <param name="subdividedVertexPositions">The positions of the subdivided vertices.</param>
        public static void Subdivide(Topology topology, IVertexAttribute <Vector3> vertexPositions, int degree, Func <Vector3, Vector3, float, Vector3> interpolator, out Topology subdividedTopology, out Vector3[] subdividedVertexPositions)
        {
            if (degree < 0)
            {
                throw new ArgumentOutOfRangeException("degree", degree, "Topology subdivision degree cannot be negative.");
            }
            if (degree == 0)
            {
                subdividedTopology = (Topology)topology.Clone();
                var subdividedVertexPositionsArray = new Vector3[topology.vertices.Count];
                vertexPositions.CopyTo(subdividedVertexPositionsArray, 0);
                subdividedVertexPositions = subdividedVertexPositionsArray;
                return;
            }

            int vertexCount       = 0;
            int edgeCount         = 0;
            int internalFaceCount = 0;

            vertexCount += topology.vertices.Count + (topology.halfEdges.Count / 2) * degree;

            foreach (var face in topology.internalFaces)
            {
                switch (face.neighborCount)
                {
                case 3:
                    vertexCount       += (degree * (degree - 1)) / 2;
                    edgeCount         += (degree + 1) * (degree + 1) * 3;
                    internalFaceCount += (degree + 1) * (degree + 1);
                    break;

                case 4:
                    vertexCount       += degree * degree;
                    edgeCount         += (degree + 1) * (degree + 1) * 4;
                    internalFaceCount += (degree + 1) * (degree + 1);
                    break;

                default:
                    throw new InvalidOperationException("Cannot subdivide a face with anything other than 3 or 4 sides.");
                }
            }

            foreach (var face in topology.externalFaces)
            {
                edgeCount += face.neighborCount * (degree + 1);
            }

            var indexer = new ManualFaceNeighborIndexer(vertexCount, edgeCount, internalFaceCount, topology.externalFaces.Count);
            var subdividedVertexPositionsList = new List <Vector3>();

            foreach (var vertex in topology.vertices)
            {
                subdividedVertexPositionsList.Add(vertexPositions[vertex]);
            }

            var currentVertexCount = topology.vertices.Count;

            var dt   = 1f / (degree + 1);
            var tEnd = 1f - dt * 0.5f;

            var subdividedEdgeVertices = new int[topology.halfEdges.Count * degree];

            foreach (var edge in topology.vertexEdges)
            {
                if (edge.isFirstTwin)
                {
                    var p0 = vertexPositions[edge.nearVertex];
                    var p1 = vertexPositions[edge.farVertex];
                    var t  = dt;
                    var subdividedEdgeVertexIndex = edge.index * degree;
                    while (t < tEnd)
                    {
                        subdividedEdgeVertices[subdividedEdgeVertexIndex++] = currentVertexCount++;
                        subdividedVertexPositionsList.Add(interpolator(p0, p1, t));
                        t += dt;
                    }
                }
                else
                {
                    var subdividedEdgeVertexIndex     = edge.index * degree;
                    var subdividedTwinEdgeVertexIndex = (edge.twinIndex + 1) * degree - 1;
                    for (int i = 0; i < degree; ++i)
                    {
                        subdividedEdgeVertices[subdividedEdgeVertexIndex + i] = subdividedEdgeVertices[subdividedTwinEdgeVertexIndex - i];
                    }
                }
            }

            foreach (var face in topology.internalFaces)
            {
                switch (face.neighborCount)
                {
                case 3:
                    SubdivideTriangle(indexer, face, degree, interpolator, subdividedVertexPositionsList, subdividedEdgeVertices, ref currentVertexCount);
                    break;

                case 4:
                    SubdivideQuadrilateral(indexer, face, degree, interpolator, subdividedVertexPositionsList, subdividedEdgeVertices, ref currentVertexCount);
                    break;

                default:
                    throw new InvalidOperationException("Cannot subdivide a face with anything other than 3 or 4 sides.");
                }
            }

            subdividedTopology        = TopologyUtility.BuildTopology(indexer);
            subdividedVertexPositions = subdividedVertexPositionsList.ToArray();
        }
 /// <summary>
 /// Creates a new topology based on the one provided, subdividing each face into multiple smaller faces, and adding extra vertices and edges accordingly.  Uses linear interpolation for deriving the positions of new vertices.
 /// </summary>
 /// <param name="topology">The original topology to be subdivided.</param>
 /// <param name="vertexPositions">The positions of the original topology's vertices.</param>
 /// <param name="degree">The degree of subdivision, equivalent to the number of additional vertices that will be added along each original edge.  Must be non-negative, and a value of zero will result in an exact duplicate with no subdivision.</param>
 /// <param name="subdividedTopology">The copied and subdivided topology.</param>
 /// <param name="subdividedVertexPositions">The positions of the subdivided vertices.</param>
 public static void Subdivide(Topology topology, IVertexAttribute <Vector3> vertexPositions, int degree, out Topology subdividedTopology, out Vector3[] subdividedVertexPositions)
 {
     Subdivide(topology, vertexPositions, degree, (Vector3 p0, Vector3 p1, float t) => { return(Geometry.LerpUnclamped(p0, p1, t)); }, out subdividedTopology, out subdividedVertexPositions);
 }
Exemple #15
0
        private void CreateJumbledGrid(out Topology topology, out IVertexAttribute <Vector3> vertexPositions)
        {
            Vector3[] vertexPositionsArray;
            var       surface = RectangularHexGrid.Create(
                HexGridDescriptor.CreateSideUp(true, HexGridAxisStyles.StaggeredSymmetric),
                Vector3.zero, Quaternion.identity,
                false, false,
                new IntVector2(11, 10));

            topology        = surface.CreateManifold(out vertexPositionsArray);
            vertexPositions = PositionalVertexAttribute.Create(surface, vertexPositionsArray);

            var regularityWeight = 0.5f;
            var equalAreaWeight  = 1f - regularityWeight;

            var regularityRelaxedVertexPositions = new Vector3[topology.vertices.Count].AsVertexAttribute();
            var equalAreaRelaxedVertexPositions  = new Vector3[topology.vertices.Count].AsVertexAttribute();
            var relaxedVertexPositions           = regularityRelaxedVertexPositions;
            var faceCentroids = PositionalFaceAttribute.Create(surface, topology.internalFaces.Count);
            var vertexAreas   = new float[topology.vertices.Count].AsVertexAttribute();

            FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions, faceCentroids);
            VertexAttributeUtility.CalculateVertexAreasFromVertexPositionsAndFaceCentroids(topology.vertices, vertexPositions, faceCentroids, vertexAreas);

            var totalArea = 0f;

            foreach (var vertexArea in vertexAreas)
            {
                totalArea += vertexArea;
            }

            var localTopology        = topology;
            var localVertexPositions = vertexPositions;

            Func <float> relaxIterationFunction = () =>
            {
                PlanarManifoldUtility.RelaxVertexPositionsForRegularity(localTopology, localVertexPositions, false, regularityRelaxedVertexPositions);
                PlanarManifoldUtility.RelaxVertexPositionsForEqualArea(localTopology, localVertexPositions, totalArea, false, equalAreaRelaxedVertexPositions, faceCentroids, vertexAreas);
                for (int i = 0; i < relaxedVertexPositions.Count; ++i)
                {
                    relaxedVertexPositions[i] = regularityRelaxedVertexPositions[i] * regularityWeight + equalAreaRelaxedVertexPositions[i] * equalAreaWeight;
                }
                var relaxationAmount = PlanarManifoldUtility.CalculateRelaxationAmount(localVertexPositions, relaxedVertexPositions);
                for (int i = 0; i < localVertexPositions.Count; ++i)
                {
                    localVertexPositions[i] = relaxedVertexPositions[i];
                }
                return(relaxationAmount);
            };

            Func <bool> repairFunction = () =>
            {
                return(PlanarManifoldUtility.ValidateAndRepair(localTopology, surface.normal, localVertexPositions, 0.5f, false));
            };

            Action relaxationLoopFunction = TopologyRandomizer.CreateRelaxationLoopFunction(20, 20, 0.95f, relaxIterationFunction, repairFunction);

            TopologyRandomizer.Randomize(
                topology, 1, 0.2f,
                3, 3, 5, 7, false,
                XorShift128Plus.Create(23478),
                relaxationLoopFunction);
        }
        /// <summary>
        /// Attempts to move the positions of vertices such that they have roughly uniform density, with a bias towards also making sure that the shapes of the faces become more regular.
        /// </summary>
        /// <param name="topology">The topology to relax.</param>
        /// <param name="vertexPositions">The original positions of the vertices to relax.</param>
        /// <param name="lockBoundaryPositions">Indicates that vertices with an external neighboring face should not have their positions altered.</param>
        /// <param name="relaxedVertexPositions">A pre-allocated collection in which the relaxed vertex positions will be stored.  Should not be the same collection as <paramref name="vertexPositions"/>.</param>
        /// <returns>The relaxed vertex positions.</returns>
        public static IVertexAttribute <Vector3> RelaxVertexPositionsForRegularity(Topology topology, IVertexAttribute <Vector3> vertexPositions, bool lockBoundaryPositions, IVertexAttribute <Vector3> relaxedVertexPositions)
        {
            foreach (var vertex in topology.vertices)
            {
                if (!lockBoundaryPositions || !vertex.hasExternalFaceNeighbor)
                {
                    var firstEdge             = vertex.firstEdge;
                    var relaxedVertexPosition = vertexPositions[firstEdge];
                    var edge = firstEdge.next;
                    while (edge != firstEdge)
                    {
                        relaxedVertexPosition += vertexPositions[edge];
                        edge = edge.next;
                    }

                    relaxedVertexPositions[vertex] = relaxedVertexPosition / vertex.neighborCount;
                }
                else
                {
                    relaxedVertexPositions[vertex] = vertexPositions[vertex];
                }
            }

            return(relaxedVertexPositions);
        }
 /// <summary>
 /// Attempts to move the positions of vertices such that they have roughly uniform density, with a bias towards also making sure that the shapes of the faces become more regular.
 /// </summary>
 /// <param name="topology">The topology to relax.</param>
 /// <param name="vertexPositions">The original positions of the vertices to relax.</param>
 /// <param name="lockBoundaryPositions">Indicates that vertices with an external neighboring face should not have their positions altered.</param>
 /// <returns>The relaxed vertex positions.</returns>
 public static IVertexAttribute <Vector3> RelaxVertexPositionsForRegularity(Topology topology, IVertexAttribute <Vector3> vertexPositions, bool lockBoundaryPositions)
 {
     return(RelaxVertexPositionsForRegularity(topology, vertexPositions, lockBoundaryPositions, new Vector3[topology.vertices.Count].AsVertexAttribute()));
 }
Exemple #18
0
 public void Add(string id, IVertexAttribute a)
 {
     attribute.Add(id, a);
 }
        private static void ExportEdgeToSVG(System.IO.TextWriter writer, ISurface surface, Topology.FaceEdge edge, Topology.FaceEdge nearVertexFaceEdge, Topology.FaceEdge farVertexFaceEdge, Func <Vector3, Vector2> flatten, IVertexAttribute <Vector3> vertexPositions, string arrowFormat, string edgeIndexFormat, SVGStyle style, string additionalClasses)
        {
            var p0            = vertexPositions[nearVertexFaceEdge];
            var p1            = vertexPositions[farVertexFaceEdge];
            var n0            = surface.GetNormal(p0);
            var n1            = surface.GetNormal(p1);
            var edgeDirection = (p1 - p0).normalized;
            var edgeNormal0   = (n0 + n1).normalized;
            var edgeNormal1   = Vector3.Cross(edgeDirection, edgeNormal0);

            var offset0 = edgeDirection * (style.vertexCircleRadius + style.edgeSeparation);
            var offset1 = edgeNormal1 * style.edgeSeparation * 0.5f;
            var offset2 = -edgeDirection * style.edgeArrowOffset.x + edgeNormal1 * style.edgeArrowOffset.y;

            Vector2 arrow0 = flatten(p0 + offset0 + offset1);
            Vector2 arrow1 = flatten(p1 - offset0 + offset1);
            Vector2 arrow2 = flatten(p1 - offset0 + offset1 + offset2);

            writer.WriteLine(arrowFormat, arrow0.x, arrow0.y, arrow1.x, arrow1.y, arrow2.x, arrow2.y, additionalClasses);

            if (style.showEdgeIndices)
            {
                var edgeCenter = flatten((p0 + p1) * 0.5f + edgeNormal1 * (style.edgeSeparation * 0.5f + style.edgeIndexOffset));
                writer.WriteLine(edgeIndexFormat, edgeCenter.x, edgeCenter.y, edge.index, additionalClasses);
            }
        }
Exemple #20
0
 /// <summary>
 /// Finds the shortest or path from the specified source vertex to the specified target vertex,
 /// using the A* algorithm and the supplied vertex positions to measure standard Euclidean
 /// distance between vertices and over vertex edges.
 /// </summary>
 /// <param name="source">The source vertex from which the path should start.</param>
 /// <param name="target">The target vertex that the path should attempt to reach.</param>
 /// <param name="vertexPositions">The three dimensional positions of each face in the world.</param>
 /// <param name="path">An optional existing path created by an earlier call to one of the <seealso cref="O:MakeIt.Tile.PathFinder.FindPath"/> functions, which will be overwritten with the new path data.</param>
 /// <returns>A vertex edge path instance describing the path found from source to target, or an incomplete object if no path was found.</returns>
 /// <remarks><para>The optional <paramref name="path"/> parameter is useful for reducing allocation activity
 /// and pressure on the garbage collector.  Reusing an existing path object will not require an additional
 /// allocation to store the path as long as the new path fits inside the capacity already available in the
 /// existing path.</para></remarks>
 public IVertexEdgePath FindEuclideanPath(Topology.Vertex source, Topology.Vertex target, IVertexAttribute <Vector3> vertexPositions, IVertexEdgePath path = null)
 {
     return(FindPath(source, target,
                     (Topology.Vertex s, Topology.Vertex t, int pathLength) =>
     {
         return (vertexPositions[s] - vertexPositions[t]).magnitude;
     },
                     (Topology.VertexEdge edge, int pathLength) =>
     {
         return (vertexPositions[edge.nearVertex] - vertexPositions[edge.farVertex]).magnitude;
     },
                     path));
 }
        /// <summary>
        /// Attempts to move the positions of vertices such that they have roughly uniform density, with a bias towards also making sure that the surface areas of the faces also become more uniform.
        /// </summary>
        /// <param name="topology">The topology to relax.</param>
        /// <param name="vertexPositions">The original positions of the vertices to relax.</param>
        /// <param name="totalArea">The total surface area of the internal faces of the topology.</param>
        /// <param name="lockBoundaryPositions">Indicates that vertices with an external neighboring face should not have their positions altered.</param>
        /// <param name="relaxedVertexPositions">A pre-allocated collection in which the relaxed vertex positions will be stored.  Should not be the same collection as <paramref name="vertexPositions"/>.</param>
        /// <param name="faceCentroids">A pre-allocated collection in which the intermediate face centroid positions will be stored.</param>
        /// <param name="vertexAreas">A pre-allocated collection in which the intermediate nearby surface areas of vertices will be stored.</param>
        /// <returns>The relaxed vertex positions.</returns>
        public static IVertexAttribute <Vector3> RelaxVertexPositionsForEqualArea(Topology topology, IVertexAttribute <Vector3> vertexPositions, float totalArea, bool lockBoundaryPositions, IVertexAttribute <Vector3> relaxedVertexPositions, IFaceAttribute <Vector3> faceCentroids, IVertexAttribute <float> vertexAreas)
        {
            var idealArea = totalArea / topology.vertices.Count;

            FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.internalFaces, vertexPositions, faceCentroids);
            VertexAttributeUtility.CalculateVertexAreasFromVertexPositionsAndFaceCentroids(topology.vertices, vertexPositions, faceCentroids, vertexAreas);

            for (int i = 0; i < topology.vertices.Count; ++i)
            {
                relaxedVertexPositions[i] = new Vector3(0f, 0f, 0f);
            }

            foreach (var vertex in topology.vertices)
            {
                var multiplier = Mathf.Sqrt(idealArea / vertexAreas[vertex]);
                foreach (var edge in vertex.edges)
                {
                    var neighborVertex         = edge.vertex;
                    var neighborRelativeCenter = vertexPositions[edge.twin];
                    relaxedVertexPositions[neighborVertex] += (vertexPositions[neighborVertex] - neighborRelativeCenter) * multiplier + neighborRelativeCenter;
                }
            }

            foreach (var vertex in topology.vertices)
            {
                if (!lockBoundaryPositions || !vertex.hasExternalFaceNeighbor)
                {
                    relaxedVertexPositions[vertex] /= vertex.neighborCount;
                }
                else
                {
                    relaxedVertexPositions[vertex] = vertexPositions[vertex];
                }
            }

            return(relaxedVertexPositions);
        }
 /// <summary>
 /// Attempts to move the positions of vertices such that they have roughly uniform density, with a bias towards also making sure that the surface areas of the faces also become more uniform.
 /// </summary>
 /// <param name="topology">The topology to relax.</param>
 /// <param name="vertexPositions">The original positions of the vertices to relax.</param>
 /// <param name="totalArea">The total surface area of the internal faces of the topology.</param>
 /// <param name="lockBoundaryPositions">Indicates that vertices with an external neighboring face should not have their positions altered.</param>
 /// <param name="relaxedVertexPositions">A pre-allocated collection in which the relaxed vertex positions will be stored.  Should not be the same collection as <paramref name="vertexPositions"/>.</param>
 /// <returns>The relaxed vertex positions.</returns>
 public static IVertexAttribute <Vector3> RelaxForEqualArea(Topology topology, IVertexAttribute <Vector3> vertexPositions, float totalArea, bool lockBoundaryPositions, IVertexAttribute <Vector3> relaxedVertexPositions)
 {
     return(RelaxVertexPositionsForEqualArea(topology, vertexPositions, totalArea, lockBoundaryPositions, relaxedVertexPositions, new Vector3[topology.internalFaces.Count].AsFaceAttribute(), new float[topology.vertices.Count].AsVertexAttribute()));
 }
Exemple #23
0
        private void RasterizeTriangle(Vertex v0, Vertex v1, Vertex v2)
        {
            // Rasterizes triangle, barycentric version

            // Bounding box
            float xMin = MathHelper.Min3(v0.Position.X, v1.Position.X, v2.Position.X);
            float xMax = MathHelper.Max3(v0.Position.X, v1.Position.X, v2.Position.X);
            float yMin = MathHelper.Min3(v0.Position.Y, v1.Position.Y, v2.Position.Y);
            float yMax = MathHelper.Max3(v0.Position.Y, v1.Position.Y, v2.Position.Y);

            // Out of screen, throw away
            if (xMin > DeviceSize.Width - 1 || xMax < 0 || yMin > DeviceSize.Height - 1 || yMax < 0)
            {
                return;
            }
            // Limit to boundaries
            int x0 = (int)Math.Max(0, xMin);
            int x1 = (int)Math.Min(DeviceSize.Width - 1, Math.Floor(xMax));
            int y0 = (int)Math.Max(0, yMin);
            int y1 = (int)Math.Min(DeviceSize.Height - 1, Math.Floor(yMax));

            float   area       = 1 / MathHelper.Edge(v0.Position, v1.Position, v2.Position);
            Vector4 firstPixel = new Vector4(x0 + 0.5f, y0 + 0.5f, 0, 0); // Centre of the first pixel

            // Precompute edge function
            float w0c     = MathHelper.Edge(v1.Position, v2.Position, firstPixel);
            float w0StepX = (v2.Position.Y - v1.Position.Y);
            float w0StepY = -(v2.Position.X - v1.Position.X);

            w0c -= w0StepX;
            float w0Start = w0c;

            float w1c     = MathHelper.Edge(v2.Position, v0.Position, firstPixel);
            float w1StepX = (v0.Position.Y - v2.Position.Y);
            float w1StepY = -(v0.Position.X - v2.Position.X);

            w1c -= w1StepX;
            float w1Start = w1c;

            float w2c     = MathHelper.Edge(v0.Position, v1.Position, firstPixel);
            float w2StepX = (v1.Position.Y - v0.Position.Y);
            float w2StepY = -(v1.Position.X - v0.Position.X);

            w2c -= w2StepX;
            float w2Start = w2c;

            for (int y = y0; y <= y1; ++y)
            {
                for (int x = x0; x <= x1; ++x)
                {
                    w0c += w0StepX; // Increment X
                    w1c += w1StepX;
                    w2c += w2StepX;

                    float w0 = w0c; // Barycentric coordinates
                    float w1 = w1c;
                    float w2 = w2c;

                    bool cullACW = !(w0 >= 0 && w1 >= 0 && w2 >= 0);
                    bool cullCW  = !(w0 <= 0 && w1 <= 0 && w2 <= 0);

                    if (CullMode == CullMode.AntiClockwise && cullACW)
                    {
                        continue; // Pixel isn't within the triangle
                    }
                    else if (CullMode == CullMode.Clockwise && cullCW)
                    {
                        continue;
                    }
                    else if (CullMode == CullMode.None && cullACW && cullCW)
                    {
                        continue;
                    }

                    w0 *= area;
                    w1 *= area;
                    w2 *= area;

                    Vertex result = new Vertex();
                    // Interpolate final vertex
                    result.Position = v0.Position * w0 + v1.Position * w1 + v2.Position * w2;
                    // Divide by W
                    result.Position = result.Position.Homogenised;

                    int sequentialPosition = y * DeviceSize.Width + x;

                    // Depth Test
                    bool depthTest = true;
                    if (DepthTest != DepthTestState.Disable)
                    {
                        depthTest = TestFunction(DepthFunction, result.Position.Z + DepthBias, zBuffer[sequentialPosition]);
                    }
                    // Stencil Test
                    if (StencilEnable)
                    {
                        byte destStencil = (byte)(stencilBuffer[sequentialPosition] & StencilFunctionMask);
                        byte srcStencil  = (byte)(StencilReference & StencilFunctionMask);
                        bool stencilTest = TestFunction(StencilFunction, srcStencil, destStencil);
                        if (!stencilTest)
                        {
                            stencilBuffer[sequentialPosition] = GetStencilValue(StencilFail, srcStencil, destStencil); // Only Depth passes
                        }
                        else if (stencilTest && !depthTest)
                        {
                            stencilBuffer[sequentialPosition] = GetStencilValue(DepthFail, srcStencil, destStencil); // Only stencil passes
                        }
                        else if (stencilTest && depthTest)
                        {
                            stencilBuffer[sequentialPosition] = GetStencilValue(StencilDepthPass, srcStencil, destStencil); // Both pass
                        }
                        if (!stencilTest)                                                                                   // Skip if failed
                        {
                            continue;
                        }
                    }
                    if (!depthTest) // Skip if depthtest failed
                    {
                        continue;
                    }

                    // Interpolate vertex attribute
                    foreach (var key in v0.Attribute)
                    {
                        IVertexAttribute data = v0.Attribute[key].Multiply(w0).Add(v1.Attribute[key].Multiply(w1).Add(v2.Attribute[key].Multiply(w2))).Multiply(result.Position.W);
                        result.Attribute.Add(key, data);
                    }

                    if (PixelShader(result, out Vector4 finalColour))
                    {
                        if (DepthTest == DepthTestState.Enable)
                        {
                            // Write Z Buffer if not discarded
                            zBuffer[sequentialPosition] = result.Position.Z;
                        }
                        int sequentialPos2 = (BytesPerPixel * y * DeviceSize.Width) + x * BytesPerPixel;
                        if (BlendEnable)
                        {
                            Vector4 destColour = new Vector4();
                            destColour.Z = buffer[sequentialPos2] / 255.0f;
                            destColour.Y = buffer[sequentialPos2 + 1] / 255.0f;
                            destColour.X = buffer[sequentialPos2 + 2] / 255.0f;
                            destColour.W = buffer[sequentialPos2 + 3] / 255.0f;
                            finalColour  = Blend(finalColour, destColour);
                        }
                        buffer[sequentialPos2]     = (byte)(finalColour.Z * 255);
                        buffer[sequentialPos2 + 1] = (byte)(finalColour.Y * 255);
                        buffer[sequentialPos2 + 2] = (byte)(finalColour.X * 255);
                        buffer[sequentialPos2 + 3] = (byte)(finalColour.W * 255);
                    }
                }
                w0Start += w0StepY; // Increment Y
                w0c      = w0Start;
                w1Start += w1StepY;
                w1c      = w1Start;
                w2Start += w2StepY;
                w2c      = w2Start;
            }
        }
        /// <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>");
        }
 /// <summary>
 /// Reverses the roles of vertices and faces, as when taking the dual of a polyhedron.
 /// </summary>
 /// <param name="topology">The topology containing the vertices and faces to swap.</param>
 /// <param name="vertexPositions">The original positions of the vertices.</param>
 /// <param name="dualVertexPositions">The positions of the vertices after the swap, calculated as the face centroids of the original topology.</param>
 public static void MakeDual(Topology topology, IVertexAttribute <Vector3> vertexPositions, out Vector3[] dualVertexPositions)
 {
     ManifoldUtility.MakeDual(topology, FaceAttributeUtility.CalculateFaceCentroidsFromVertexPositions(topology.faces, vertexPositions), out dualVertexPositions);
 }
Exemple #26
0
 public void Set(string identifier, IVertexAttribute value)
 {
     attribute[identifier] = value;
 }
 /// <summary>
 /// Creates a copy of the specified topology, but with the roles of vertices and faces reversed, as when taking the dual of a polyhedron.
 /// </summary>
 /// <param name="topology">The original topology containing the vertices and faces to swap.</param>
 /// <param name="vertexPositions">The original positions of the vertices.</param>
 /// <param name="dualTopology">The copied topology with the vertices and faces swapped.</param>
 /// <param name="dualVertexPositions">The positions of the vertices after the swap, calculated as the face centroids of the original topology.</param>
 public static void GetDualManifold(Topology topology, IVertexAttribute <Vector3> vertexPositions, out Topology dualTopology, out Vector3[] dualVertexPositions)
 {
     dualTopology = (Topology)topology.Clone();
     MakeDual(dualTopology, vertexPositions, out dualVertexPositions);
 }
        private static void ExportVertexToSVG(System.IO.TextWriter writer, Topology.Vertex vertex, Topology.FaceEdge edge, Func <Vector3, Vector2> flatten, IVertexAttribute <Vector3> vertexPositions, Vector2 transformedVertexDotRadius, string vertexFormat, string vertexIndexFormat, SVGStyle style, string additionalClasses)
        {
            var p = flatten(vertexPositions[edge]);

            writer.WriteLine(vertexFormat, p.x, p.y, transformedVertexDotRadius.x, transformedVertexDotRadius.y, additionalClasses);

            if (style.showVertexIndices)
            {
                writer.WriteLine(vertexIndexFormat, p.x, p.y, vertex.index, additionalClasses);
            }
        }
Exemple #29
0
 /// <summary>
 /// Finds the shortest or path from the specified source vertex to the specified target vertex,
 /// using the A* algorithm and the supplied vertex positions to measure spherical arc distance
 /// between vertices and over vertex edges.
 /// </summary>
 /// <param name="source">The source vertex from which the path should start.</param>
 /// <param name="target">The target vertex that the path should attempt to reach.</param>
 /// <param name="surface">The surface describing the overall shape of the spherical manifold.</param>
 /// <param name="vertexPositions">The three dimensional positions of each face in the world.</param>
 /// <param name="path">An optional existing path created by an earlier call to one of the <seealso cref="O:MakeIt.Tile.PathFinder.FindPath"/> functions, which will be overwritten with the new path data.</param>
 /// <returns>A vertex edge path instance describing the path found from source to target, or an incomplete object if no path was found.</returns>
 /// <remarks><para>The optional <paramref name="path"/> parameter is useful for reducing allocation activity
 /// and pressure on the garbage collector.  Reusing an existing path object will not require an additional
 /// allocation to store the path as long as the new path fits inside the capacity already available in the
 /// existing path.</para></remarks>
 public IVertexEdgePath FindSphericalEuclideanPath(Topology.Vertex source, Topology.Vertex target, SphericalSurface surface, IVertexAttribute <Vector3> vertexPositions, IVertexEdgePath path = null)
 {
     return(FindPath(source, target,
                     (Topology.Vertex s, Topology.Vertex t, int pathLength) =>
     {
         var sourcePosition = vertexPositions[s];
         var targetPosition = vertexPositions[t];
         return Geometry.SphericalArcLength(sourcePosition, targetPosition, surface.radius);
     },
                     (Topology.VertexEdge edge, int pathLength) =>
     {
         var sourcePosition = vertexPositions[edge.nearVertex];
         var targetPosition = vertexPositions[edge.farVertex];
         return Geometry.SphericalArcLength(sourcePosition, targetPosition, surface.radius);
     },
                     path));
 }
        /// <summary>
        /// Scans the topology's vertices and their positions for any egregious anomolies, such as inverted triangles, and attempts to correct them when encountered.
        /// </summary>
        /// <param name="topology">The topology to validate and repair.</param>
        /// <param name="surfaceNormal">The surface normal of the manifold.</param>
        /// <param name="vertexPositions">The topology vertex positions, which will be modified by this function.</param>
        /// <param name="adjustmentWeight">The degree to which final repaired vertex positions should conform to the ideal computed positions, moving away from the original positions.</param>
        /// <param name="lockBoundaryPositions">Indicates that vertices with an external neighboring face should not have their positions altered.</param>
        /// <returns>True if all vertex positions were validated and thus not changed, and false if at least one vertex failed validation and had its position at least partially repaired.</returns>
        public static bool ValidateAndRepair(Topology topology, Vector3 surfaceNormal, IVertexAttribute <Vector3> vertexPositions, float adjustmentWeight, bool lockBoundaryPositions)
        {
            bool  repaired       = false;
            float originalWeight = 1f - adjustmentWeight;

            foreach (var vertex in topology.vertices)
            {
                if (!lockBoundaryPositions || !vertex.hasExternalFaceNeighbor)
                {
                    var center = vertexPositions[vertex];
                    var edge   = vertex.firstEdge;
                    var p0     = vertexPositions[edge];
                    edge = edge.next;
                    var p1 = vertexPositions[edge];
                    edge = edge.next;
                    var centroid0 = (center + p0 + p1);
                    var firstEdge = edge;
                    do
                    {
                        var p2          = vertexPositions[edge];
                        var centroid1   = (center + p1 + p2);
                        var localNormal = Vector3.Cross(centroid0 - center, centroid1 - center);
                        if (Vector3.Dot(localNormal, surfaceNormal) < 0f)
                        {
                            goto repair;
                        }
                        p0        = p1;
                        p1        = p2;
                        centroid0 = centroid1;
                        edge      = edge.next;
                    } while (edge != firstEdge);

                    continue;

                    repair : repaired = true;
                    var average = new Vector3();
                    edge = firstEdge;
                    do
                    {
                        average += vertexPositions[edge];
                        edge     = edge.next;
                    } while (edge != firstEdge);
                    average /= vertex.neighborCount;

                    vertexPositions[vertex] = (center * originalWeight + average * adjustmentWeight);
                }
            }

            return(!repaired);
        }