Ejemplo n.º 1
0
        private void MainForm_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (points == null)
                {
                    points = new List<DelaunauTriangulationSample.Classes.Point>();
                    g.Clear(Color.White);
                }

                DelaunauTriangulationSample.Classes.Point mbToAdd = new DelaunauTriangulationSample.Classes.Point(e.X, Size.Height - e.Y);
                if (!points.Contains(mbToAdd))
                    points.Add(mbToAdd);
                g.DrawEllipse(new Pen(Color.Red), e.X - 1, e.Y - 1, 3, 3);
            }
            else
            {
                if (points == null)
                {
                    g.Clear(Color.White);
                    Random rnd = new Random();
                    points = new List<DelaunauTriangulationSample.Classes.Point>();
                    for (int i = 0; i < Count.Value; i++)
                    {
                        DelaunauTriangulationSample.Classes.Point tmp = new DelaunauTriangulationSample.Classes.Point(rnd.Next(Size.Width - 200) + 30, rnd.Next(Size.Height - 100) + 50);
                        if (!points.Contains(tmp))
                            points.Add(tmp);
                        g.DrawEllipse(defaultPointPen, (int)tmp.X - 1, Size.Height - (int)tmp.Y + 1, 3, 3);
                    }
                }
                Triangulation tb = new Triangulation(points, g, defaultLinePen, defaultPointPen, defaultNewLintPen, Size.Height, Delay.Value);
                points = null;
            }
        }
Ejemplo n.º 2
0
 public VoronoiDiagram(MeshFilter mesh, Transform transform)
 {
     _initial = new Triangle(new Pnt(-10000, -10000), new Pnt(10000, -10000), new Pnt(0, 10000));
     _delaunay = new Triangulation(_initial);
     _mesh = mesh;
     _transform = transform;
 }
Ejemplo n.º 3
0
 public void UpdateTriangulation(Triangulation tri)
 {
     TriangulationView.UpdateTriangulation(tri);
     SetNeedsDisplay();
 }
Ejemplo n.º 4
0
    public static Solid CreateSolidRevolve(List <List <Entity> > entitiyLoops, float angle, float helixStep, Vector3 axis, Vector3 origin, float angleStep, UnityEngine.Matrix4x4 tf, IdPath feature)
    {
        var  ids      = new List <List <Id> >();
        var  polygons = Sketch.GetPolygons(entitiyLoops, ref ids);
        bool isHelix  = (Math.Abs(helixStep) > 1e-6);

        if (!isHelix && Mathf.Abs(angle) > 360f)
        {
            angle = Mathf.Sign(angle) * 360f;
        }
        bool inversed = angle < 0f;
        int  subdiv   = (int)Mathf.Ceil(Math.Abs(angle) / angleStep);
        var  drot     = UnityEngine.Matrix4x4.Translate(origin) * UnityEngine.Matrix4x4.Rotate(Quaternion.AngleAxis(angle / subdiv, axis)) * UnityEngine.Matrix4x4.Translate(-origin);

        Func <float, Vector3, Vector3> PointOn = (float a, Vector3 point) => {
            var ax  = axis;
            var axn = axis.normalized;
            var t   = a / 360.0f;
            var o   = origin;
            var prj = ExpVector.ProjectPointToLine(point, o, o + ax);
            var ra  = Mathf.Atan2(helixStep / 4.0f, (point - prj).magnitude);
            var res = ExpVector.RotateAround(point, point - prj, o, ra);
            res = ExpVector.RotateAround(res, ax, o, a * Mathf.PI / 180.0f);
            return(res + axn * t * helixStep);
        };

        var polys = new List <Polygon>();
        Func <Vector3, Vector3> TF = v => tf.MultiplyPoint(v);

        for (int pi = 0; pi < polygons.Count; pi++)
        {
            var p         = polygons[pi];
            var pid       = ids[pi];
            var pv        = new List <Vector3>(p);
            var triangles = Triangulation.Triangulate(pv);

            IdPath polyId  = feature.With(new Id(-1));
            bool   invComp = true;

            if (Math.Abs(Math.Abs(angle) - 360f) > 1e-6 || isHelix)
            {
                float a = 0f;
                for (int side = 0; side < 2; side++)
                {
                    for (int i = 0; i < triangles.Count / 3; i++)
                    {
                        var polygonVertices = new List <Vertex>();
                        for (int j = 0; j < 3; j++)
                        {
                            polygonVertices.Add(PointOn(a, TF(triangles[i * 3 + j])).ToVertex());
                        }
                        if (inversed == invComp)
                        {
                            polygonVertices.Reverse();
                        }
                        polys.Add(new Polygon(polygonVertices, polyId));
                    }
                    polyId  = feature.With(new Id(-2));
                    invComp = false;
                    a       = angle;
                }
            }

            Dictionary <Id, IdPath> paths = new Dictionary <Id, IdPath>();
            for (int i = 0; i < p.Count; i++)
            {
                float a  = 0f;
                float da = angle / subdiv;
                for (int j = 0; j < subdiv; j++)
                {
                    var polygonVertices = new List <Vertex>();
                    polygonVertices.Add(PointOn(a, TF(p[(i + 1) % p.Count])).ToVertex());
                    polygonVertices.Add(PointOn(a + da, TF(p[(i + 1) % p.Count])).ToVertex());
                    polygonVertices.Add(PointOn(a + da, TF(p[i])).ToVertex());
                    polygonVertices.Add(PointOn(a, TF(p[i])).ToVertex());
                    a += da;
                    if (!inversed)
                    {
                        polygonVertices.Reverse();
                    }
                    IdPath curPath = null;
                    if (!paths.ContainsKey(pid[i]))
                    {
                        curPath = feature.With(pid[i]);
                        paths.Add(pid[i], curPath);
                    }
                    else
                    {
                        curPath = paths[pid[i]];
                    }

                    if (isHelix)
                    {
                        var verts = new List <Vertex>();
                        verts.Add(polygonVertices[0]);
                        verts.Add(polygonVertices[1]);
                        verts.Add(polygonVertices[2]);
                        polys.Add(new Polygon(verts, curPath));

                        verts = new List <Vertex>();
                        verts.Add(polygonVertices[0]);
                        verts.Add(polygonVertices[2]);
                        verts.Add(polygonVertices[3]);
                        polys.Add(new Polygon(verts, curPath));
                    }
                    else
                    {
                        polys.Add(new Polygon(polygonVertices, curPath));
                    }
                }
            }
        }
        return(Solid.FromPolygons(polys));
    }
Ejemplo n.º 5
0
        public void setTriangulateData(Triangulation.Delaunay delaunayTriangulation)
        {
            this.triangulation = delaunayTriangulation;

            if (this.triangulation != null && this.triangulation.pointList != null && this.triangulation.triangleList != null
                && this.triangulation.pointList.Count != 0 && this.triangulation.triangleList.Count != 0)
            {

                setVertices(triangulation.pointList);
                setMap();
                setIndicesMesh();
                CreateMesh();

            }
        }
Ejemplo n.º 6
0
 public MeshPrimitive GenerateTriangleMesh(Triangulation triangulation, IEnumerable <Vector4> colors = null, PBRTexture texture = null)
 {
     return(GenerateTriangleMesh(triangulation.Positions, triangulation.Indices.ToList(), colors, texture));
 }
Ejemplo n.º 7
0
        /// <summary>
        /// Generate a triangle mesh from supplied height map, triangulating and optionaly mapping UVs
        /// </summary>
        /// <param name="heightMap"></param>
        /// <param name="colors"></param>
        /// <param name="texture">Texture path relative from the model</param>
        /// <returns></returns>
        public MeshPrimitive GenerateTriangleMesh(HeightMap heightMap, IEnumerable <Vector4> colors = null, PBRTexture texture = null)
        {
            Triangulation triangulation = _meshService.TriangulateHeightMap(heightMap);

            return(GenerateTriangleMesh(triangulation, colors, texture));
        }
Ejemplo n.º 8
0
    private List <SlicedMesh> GenerateMesh(SlicedMesh original, Plane plane, bool positiveSide, Transform objectTransform)
    {
        SlicedMesh slicePositive = new SlicedMesh();
        SlicedMesh sliceNegative = new SlicedMesh();
        //polygon we use to fill in the sliced surface
        PolygonCreator polygonPositive = new PolygonCreator(objectTransform, plane.normal * -1);
        PolygonCreator polygonNegative = new PolygonCreator(objectTransform, plane.normal);
        bool           matPositiveAdded = false, matNegativeAdded = false;

        //we loop over all submeshes
        for (int submesh = 0; submesh < original.triangles.Length; submesh++)
        {
            int[] originalTriangles = original.triangles[submesh];
            setEdge = false;

            //increase t by 3 because a triangle consist out of 3 vertices;
            for (int t = 0; t < originalTriangles.Length; t += 3)
            {
                //which triangle we need
                int t1 = t, t2 = t + 1, t3 = t + 2;

                //Check if vertice is on positive side of the plane
                bool sideA = plane.GetSide(original.vertices[originalTriangles[t1]]) == positiveSide;
                bool sideB = plane.GetSide(original.vertices[originalTriangles[t2]]) == positiveSide;
                bool sideC = plane.GetSide(original.vertices[originalTriangles[t3]]) == positiveSide;

                //how many vertices are on the positive side of the plane
                int sideCount = (sideA ? 1 : 0) +
                                (sideB ? 1 : 0) +
                                (sideC ? 1 : 0);

                //if none of the vertices is located on the positive side
                if (sideCount == 0)
                {
                    //add entire triangle to negative side
                    sliceNegative.AddTriangle(submesh, original.vertices[originalTriangles[t1]], original.vertices[originalTriangles[t2]], original.vertices[originalTriangles[t3]],
                                              original.normals[originalTriangles[t1]], original.normals[originalTriangles[t2]], original.normals[originalTriangles[t3]],
                                              original.uv[originalTriangles[t1]], original.uv[originalTriangles[t2]], original.uv[originalTriangles[t3]]);
                    if (!matNegativeAdded)
                    {
                        matNegativeAdded = true;
                        sliceNegative.materialIndex.Add(submesh);
                    }

                    continue;
                }
                //if all the vertices are located on the positive side
                else if (sideCount == 3)
                {
                    //add entire triangle to positive side
                    slicePositive.AddTriangle(submesh, original.vertices[originalTriangles[t1]], original.vertices[originalTriangles[t2]], original.vertices[originalTriangles[t3]],
                                              original.normals[originalTriangles[t1]], original.normals[originalTriangles[t2]], original.normals[originalTriangles[t3]],
                                              original.uv[originalTriangles[t1]], original.uv[originalTriangles[t2]], original.uv[originalTriangles[t3]]);
                    if (!matPositiveAdded)
                    {
                        matPositiveAdded = true;
                        slicePositive.materialIndex.Add(submesh);
                    }

                    continue;
                }

                //else a triangle is cut and submesh material must be added to both sides
                if (!matNegativeAdded)
                {
                    matNegativeAdded = true;
                    sliceNegative.materialIndex.Add(submesh);
                }
                if (!matPositiveAdded)
                {
                    matPositiveAdded = true;
                    slicePositive.materialIndex.Add(submesh);
                }


                //determines which vertex in the triangle is solely located on one side of the plane
                int singleIndex = sideB == sideC ? 0 : sideA == sideC ? 1 : 2;
                int indexB = t + ((singleIndex + 1) % 3), indexC = t + ((singleIndex + 2) % 3);
                singleIndex += t;

                //calculate which vertices/normals/uv should be used to calculate intersection points
                Vector3 singleVertex = original.vertices[originalTriangles[singleIndex]],
                        vertexB      = original.vertices[originalTriangles[indexB]],            //right vertex
                        vertexC      = original.vertices[originalTriangles[indexC]];            //left vertex
                Vector3 singleNormal = original.normals[originalTriangles[singleIndex]],
                        normalB      = original.normals[originalTriangles[indexB]],
                        normalC      = original.normals[originalTriangles[indexC]];
                Vector2 singleUv     = original.uv[originalTriangles[singleIndex]],
                        uvB          = original.uv[originalTriangles[indexB]],
                        uvC          = original.uv[originalTriangles[indexC]];

                //calculate new vertices/normals/uv where edge intersects plane
                float   lerpB, lerpC;
                Vector3 newVertexB = PointOnPlane(plane, singleVertex, vertexB, out lerpB),     //new right vertex
                        newVertexC = PointOnPlane(plane, singleVertex, vertexC, out lerpC);     //new left vertex
                Vector3 newNormalB = Vector3.Lerp(singleNormal, normalB, lerpB),                //lerp to get the point between the old vertices where the new vertex is located
                        newNormalC = Vector3.Lerp(singleNormal, normalC, lerpC);
                Vector2 newUvB     = Vector2.Lerp(singleUv, uvB, lerpB),
                        newUvC     = Vector2.Lerp(singleUv, uvC, lerpC);

                if (!concave)
                {
                    //add and edge to "fill" the mesh
                    AddSliceTriangle(submesh, slicePositive, newVertexB, newVertexC,
                                     plane.normal * -1,
                                     newUvB, newUvC);
                    AddSliceTriangle(submesh, sliceNegative, newVertexB, newVertexC,
                                     plane.normal,
                                     newUvB, newUvC);
                }

                if (sideCount == 1)
                {
                    //positive data
                    slicePositive.AddTriangle(submesh, singleVertex, newVertexB, newVertexC, singleNormal, newNormalB, newNormalC, singleUv, newUvB, newUvC);
                    //negative data
                    sliceNegative.AddTriangle(submesh, newVertexB, vertexB, vertexC, newNormalB, normalB, normalC, newUvB, uvB, uvC);
                    sliceNegative.AddTriangle(submesh, newVertexB, vertexC, newVertexC, newNormalB, normalC, newNormalC, newUvB, uvC, newUvC);

                    if (concave)
                    {
                        //positive
                        Edge edgePositive = new Edge(newVertexB, newVertexC, plane.normal * -1, newUvB, newUvC);
                        polygonPositive.AddEdge(edgePositive);
                        //negative
                        Edge edgeNegative = new Edge(newVertexC, newVertexB, plane.normal, newUvC, newUvB);
                        polygonNegative.AddEdge(edgeNegative);
                    }
                    continue;
                }
                else if (sideCount == 2)
                {
                    //positive data
                    slicePositive.AddTriangle(submesh, newVertexB, vertexB, vertexC, newNormalB, normalB, normalC, newUvB, uvB, uvC);
                    slicePositive.AddTriangle(submesh, newVertexB, vertexC, newVertexC, newNormalB, normalC, newNormalC, newUvB, uvC, newUvC);
                    //negative data
                    sliceNegative.AddTriangle(submesh, singleVertex, newVertexB, newVertexC, singleNormal, newNormalB, newNormalC, singleUv, newUvB, newUvC);
                    if (concave)
                    {
                        //positive
                        Edge edgePositive = new Edge(newVertexC, newVertexB, plane.normal * -1, newUvC, newUvB);
                        polygonPositive.AddEdge(edgePositive);
                        //negative
                        Edge edgeNegative = new Edge(newVertexB, newVertexC, plane.normal, newUvB, newUvC);
                        polygonNegative.AddEdge(edgeNegative);
                    }
                    continue;
                }
            }
        }

        if (concave)
        {
            //build polygons
            polygonPositive.ConnectEdges();
            polygonNegative.ConnectEdges();

            //build meshdata for polygons
            FinishedPolygon polygonPositiveFinished = Triangulation.Triangulate(polygonPositive, slicePositive.VertexCount);
            FinishedPolygon polygonNegativeFinished = Triangulation.Triangulate(polygonNegative, sliceNegative.VertexCount);

            //add meshdata to slices
            slicePositive.AddPolygon(polygonPositiveFinished);
            sliceNegative.AddPolygon(polygonNegativeFinished);
        }

        slicePositive.FillArray(correctData);
        sliceNegative.FillArray(correctData);

        return(new List <SlicedMesh>()
        {
            slicePositive, sliceNegative
        });
    }
Ejemplo n.º 9
0
    /// <summary>
    /// Function to display only a specified range (i.e., subset) of indices of the FrequencyPolygons.
    /// </summary>
    /// <param name="startIndex">Start index (including).</param>
    /// <param name="endIndex">End index (including).</param>
    public void displayRange(int startIndex, int endIndex)
    {
        // get positions for selected range
        Vector3[] rangePositions = frequencyPointPositionsToArrayForRange(fpcList, startIndex, endIndex);

        // update LineRenderer's positions (if required)
        if (tdrcInterface.cnfg2d_isFrequencyPolygonLineEnabled)
        {
            // line renderer incl. connecting to the 2D Frequency Polygon origin axis
            lineRenderer.positionCount = rangePositions.Length + 2;
            Vector3[] lrPoints = new Vector3[lineRenderer.positionCount];
            lrPoints[0] = new Vector3(rangePositions[0].x, origin.y, origin.z);                                                         // add one point at the start
            for (int i = 1; i <= rangePositions.Length; i++)
            {
                lrPoints[i] = rangePositions[i - 1];                                                                                    // add points for the Frequency Polygon
            }
            lrPoints[lineRenderer.positionCount - 1] = new Vector3(rangePositions[rangePositions.Length - 1].x, origin.y, origin.z);    // add one point at the end
            lineRenderer.SetPositions(lrPoints);                                                                                        // set line renderer positions
        }

        // mesh for renderering the FrequencyPolygon in the UI layer
        if (tdrcInterface.cnfg2d_isFrequencyPolygonEnabled)
        {
            // init mesh and required variables
            Mesh                   polygonMesh = new Mesh();
            List <Vector2>         meshPoints  = new List <Vector2>();
            List <int>             indices     = null;
            List <Vector3>         vertices    = null;
            List <List <Vector2> > holesList   = new List <List <Vector2> >();

            // setup mesh coordinates
            meshPoints.Add(new Vector2(rangePositions[0].x, origin.y));                             // add one point at origin at the start of the polygon
            foreach (Vector3 v in rangePositions)
            {
                meshPoints.Add(new Vector2(v.x, v.y));                                              // add points for the Frequency Polygon
            }
            meshPoints.Add(new Vector2(rangePositions[rangePositions.Length - 1].x, origin.y));     // add one end point at the end of the polygon

            // perform Triangulation
            // Developer Note: This is based on the implemented PolyExtruder package, Source: https://github.com/nicoversity/unity_polyextruder
            Triangulation.triangulate(meshPoints, holesList, 0.0f, out indices, out vertices);

            // update mesh with new data
            polygonMesh.Clear();
            polygonMesh.vertices  = vertices.ToArray();
            polygonMesh.triangles = indices.ToArray();
            polygonMesh.RecalculateNormals();
            polygonMesh.RecalculateBounds();

            // setup mesh in the UI GameObject
            canvasRendererRef.SetMesh(polygonMesh);


            // IN_ENGINE_SCREENSHOT_CAPTURE
            // Developer Note: Enable this to capture polygons in 360 screenshot capture.
            //MeshFilter mf = canvasRendererRef.gameObject.GetComponent<MeshFilter>();
            //if (mf == null) mf = canvasRendererRef.gameObject.AddComponent<MeshFilter>();
            //mf.mesh = polygonMesh;
            //MeshRenderer mr = canvasRendererRef.gameObject.GetComponent<MeshRenderer>();
            //if (mr == null) mr = canvasRendererRef.gameObject.AddComponent<MeshRenderer>();
        }
    }
Ejemplo n.º 10
0
    /// <summary>
    /// Initialize the display / drawing of the FrequencyPolygon based on its input data.
    /// </summary>
    /// <param name="fpl">List of FrequencyPoints representing the Frequency Polygon.</param>
    private void initFrequencyPolygonFromListData(List <TDRCFrequencyPoint> fpl)
    {
        // init FrequencyPointComponents
        for (int i = 0; i < fpl.Count; i++)
        {
            // calculate and set position
            float yPos = getDataValueForVisualizationBasedOnTDRCInterfaceConfig(fpl[i].value, tdrcInterface);       // transform original data value based on configured options
            fpl[i].pos = new Vector3(i * tdrcInterface.cnfg2d_frequencyPointDistance, yPos, 0.0f);                  // distance between data points as configures in the 3D Radar Chart's Interface
            //fpl[i].pos = new Vector3(i, yPos, 0.0f);                                                              // distance between data points is default (i.e., 1.0f)
            fpl[i].color = pointColor;                                                                              // keep track of color configuration

            // setup new GameObject
            Transform p = Instantiate((Resources.Load(frequencyPointSpherePrefabRef) as GameObject).transform);
            p.parent        = pointsRef;
            p.localPosition = fpl[i].pos;
            p.localScale    = Vector3.one * tdrcInterface.cnfg2d_frequencyPointScale;
            p.name          = fpl[i].dimension + "_" + fpl[i].time;
            p.gameObject.GetComponent <Renderer>().material.color = pointColor;

            // attach and set FrequencyPoint data structure to GameObject
            TDRCFrequencyPointComponent fpc = p.gameObject.AddComponent <TDRCFrequencyPointComponent>();
            fpc.fp = fpl[i];

            // keep track of all instantiated FrequencyPointComponents
            fpcList.Add(fpc);
        }

        // update length property for drawing
        length = fpcList[fpcList.Count - 1].transform.localPosition.x;

        // init LineRenderer component
        if (tdrcInterface.cnfg2d_isFrequencyPolygonLineEnabled)
        {
            // initialize all LineRenderer component related properties
            lineRenderer                = pointsRef.gameObject.AddComponent <LineRenderer>();
            lineRenderer.startWidth     = lineWidth;
            lineRenderer.endWidth       = lineWidth;
            lineRenderer.useWorldSpace  = false;
            lineRenderer.material       = new Material(Shader.Find("Standard"));
            lineRenderer.material.color = pointColor;

            // update LineRenderer's positions
            //

            // line renderer only using original points
            //lr.positionCount = fpl.Count;
            //lr.SetPositions(frequencyPointPositionsToArray(fpl));

            // line renderer incl. connecting to the 2D Frequency Polygon origin axis
            lineRenderer.positionCount = fpl.Count + 2;
            Vector3[] lrPoints = new Vector3[lineRenderer.positionCount];
            lrPoints[0] = new Vector3(origin.x, origin.y, origin.z);                                                // add one point at the start
            for (int i = 1; i <= fpl.Count; i++)
            {
                lrPoints[i] = fpl[i - 1].pos;                                                                       // add points for the Frequency Polygon
            }
            lrPoints[lineRenderer.positionCount - 1] = new Vector3(fpl[fpl.Count - 1].pos.x, origin.y, origin.z);   // add one point at the end
            lineRenderer.SetPositions(lrPoints);                                                                    // set line renderer positions
        }

        // init mesh for renderering the FrequencyPolygon (in the UI layer)
        if (tdrcInterface.cnfg2d_isFrequencyPolygonEnabled)
        {
            // init mesh and required variables
            Mesh                   polygonMesh = new Mesh();
            List <Vector2>         meshPoints  = new List <Vector2>();
            List <int>             indices     = null;
            List <Vector3>         vertices    = null;
            List <List <Vector2> > holesList   = new List <List <Vector2> >();

            // setup mesh coordinates
            Vector3[] originalPositions = frequencyPointPositionsToArray(fpl);
            meshPoints.Add(new Vector2(origin.x, origin.y));                                            // add one point at origin at the start of the polygon
            foreach (Vector3 v in originalPositions)
            {
                meshPoints.Add(new Vector2(v.x, v.y));                                                  // add points for the Frequency Polygon
            }
            meshPoints.Add(new Vector2(originalPositions[originalPositions.Length - 1].x, origin.y));   // add one end point at the end of the polygon

            // perform Triangulation
            // Developer Note: This is based on the implemented PolyExtruder package, Source: https://github.com/nicoversity/unity_polyextruder
            Triangulation.triangulate(meshPoints, holesList, 0.0f, out indices, out vertices);

            // update mesh with new data
            polygonMesh.Clear();
            polygonMesh.vertices  = vertices.ToArray();
            polygonMesh.triangles = indices.ToArray();
            polygonMesh.RecalculateNormals();
            polygonMesh.RecalculateBounds();

            // setup mesh in the UI GameObject
            canvasRendererRef.SetMesh(polygonMesh);

            // setup the mesh's material
            Material polygonMeshMat = new Material(Shader.Find("UI/Default"));
            polygonMeshMat.color = polygonColor;
            canvasRendererRef.SetMaterial(polygonMeshMat, null);

            // rotate due to Triangulation (i.e., PolyExtruder) implementation
            canvasRendererRef.transform.Rotate(Vector3.right * -90.0f);


            // IN_ENGINE_SCREENSHOT_CAPTURE
            // Developer Note: Enable this to capture polygons in 360 screenshot capture.
            //MeshFilter mf = canvasRendererRef.gameObject.GetComponent<MeshFilter>();
            //if (mf == null) mf = canvasRendererRef.gameObject.AddComponent<MeshFilter>();
            //mf.mesh = polygonMesh;
            //MeshRenderer mr = canvasRendererRef.gameObject.GetComponent<MeshRenderer>();
            //if (mr == null) mr = canvasRendererRef.gameObject.AddComponent<MeshRenderer>();
            //mr.material = polygonMeshMat;
        }
    }
Ejemplo n.º 11
0
    // Update is called once per frame
    void Update()
    {
        float rt = Time.realtimeSinceStartup;

        if (doTriangulation)
        {
            doTriangulation = false;
            if (!firstTriangulationDone)
            {
                Debug.Log("Get random points");
                // create random points
                randomPoints = new RandomPoints(max_x, max_z, Yplane);
                points       = randomPoints.CreateRandomPoints(pointsNum, VERBOSE);

                Debug.Log("Compute triangulation");
                triangulation = null;
                triangulation = new Triangulation(max_x, max_z, Yplane, points, VERBOSE, validateAfterEveryPoint);
                triangulation.ComputeTriangulation();
                firstTriangulationDone = true;

                if (showTimer)
                {
                    Debug.Log("(Triangulation) Timer: " + (Time.realtimeSinceStartup - rt) + " s");
                }
            }
            else
            {
                Debug.LogError("Triangulation has already been computed.");
            }
        }
        if (doVoronoi)
        {
            doVoronoi = false;
            if (!firstVoronoiDone && firstTriangulationDone)
            {
                Debug.Log("Convert triangulation to Voronoi");
                ConvertTriangulationToVoronoi();
                firstVoronoiDone = true;

                int numOfValidCells = 0;
                foreach (VoronoiCell cell in voronoi.voronoiCells)
                {
                    if (cell.isValid)
                    {
                        numOfValidCells++;
                    }
                }
                Debug.Log("Number of cells = " + numOfValidCells);
            }
            else if (!firstTriangulationDone)
            {
                Debug.LogError("Triangulation must be computed before the Voronoi.");
            }
            else
            {
                Debug.LogError("First Voronoi has already been computed.");
            }
        }

        if (doRelaxation)
        {
            doRelaxation = false;
            if (firstVoronoiDone)
            {
                if (relaxTimes < 0)
                {
                    relaxTimes = 0;
                }
                Debug.Log("The Voronoi will be relaxed " + relaxTimes + " time" + (relaxTimes != 1 ? "s." : "."));
                while (relaxTimesCounter < relaxTimes)
                {
                    totalRelaxesDone++;
                    Debug.Log("Apply Relaxation #" + totalRelaxesDone);
                    RelaxVoronoi();
                    ConvertTriangulationToVoronoi();
                    relaxTimesCounter++;

                    if (showTimer)
                    {
                        Debug.Log("(Relaxation) Timer: " + (Time.realtimeSinceStartup - rt) + " s");
                    }
                }
                relaxTimesCounter = 0;
            }
            else
            {
                Debug.LogError("The first Voronoi must be computed before the diagram is relaxed.");
            }
        }

        if (doMesh)
        {
            doMesh = false;
            if (!meshesCreated && firstVoronoiDone)
            {
                Debug.Log("Generate meshes");
                CreateMeshColumns();
                FindCellNeighbors();
                meshesCreated = true;
            }
            else if (meshesCreated)
            {
                Debug.LogError("Meshes have already been generated.");
            }
            else
            {
                Debug.LogError("The first Voronoi must be computed before the meshes are created.");
            }
        }

        if (doPerlin)
        {
            doPerlin = false;
            if (meshesCreated)
            {
                Debug.Log("Apply Perlin Noise to column heights");
                ApplyPerlinToVoronoi();
            }
            else
            {
                Debug.LogError("Meshes must be generated before noise is applied.");
            }
        }

        if (doRandomRipple)
        {
            doRandomRipple  = false;
            doRandomPathing = false;
            if (meshesCreated)
            {
                Debug.Log("Do random ripple (may need to reset first)");
                // rippleResetNeeded = true;
                ChooseRandomObjectToStartRipple();
            }
            else
            {
                Debug.LogError("Meshes must be generated before ripple is applied.");
            }
        }

        if (doRandomPathing)
        {
            doRandomPathing = false;
            if (meshesCreated)
            {
                Debug.Log("Do random pathing (may need to reset first)");
                // rippleResetNeeded = true;
                ChooseRandomObjectToStartPathing();
            }
            else
            {
                Debug.LogError("Meshes must be generated before pathing is applied.");
            }
        }

        if (resetRipples)
        {
            resetRipples = false;
            if (meshesCreated)
            {
                Debug.Log("Reset ripple");
                // reset all RippleColumn
                foreach (GameObject obj in meshObjects)
                {
                    obj.GetComponent <RippleColumn>().RESET = true;
                }
                // rippleResetNeeded = false;
            }
        }
        //showTimer = false;  // so timer only gets shown the first time Update() runs
    }
Ejemplo n.º 12
0
    private void Update()
    {
        // Mouse left button released
        if (Input.GetMouseButtonUp(0))
        {
            Ray ray = _raycastCamera.ScreenPointToRay(Input.mousePosition);

            if (Physics.Raycast(ray, out RaycastHit hit))
            {
                _lineRenderer.startColor = Color.white;
                _lineRenderer.endColor   = _lineRenderer.startColor;
                if (_polygonOutline.Count > 1)
                {
                    _lineRenderer.startColor = Color.green;
                    _lineRenderer.endColor   = _lineRenderer.startColor;
                }

                _gateEdgeOverlap = false;
                if (_lineRenderer.positionCount > 2)
                {
                    if (EdgeOverlapsOutline(_lineRenderer.GetPosition(_lineRenderer.positionCount - 1), hit.point))
                    {
                        return;
                    }

                    if (EdgeOverlapsOutline(hit.point, _lineRenderer.GetPosition(0)))
                    {
                        _lineRenderer.startColor = Color.red;
                        _lineRenderer.endColor   = _lineRenderer.startColor;
                        _gateEdgeOverlap         = true;
                    }
                }

                GameObject go = Instantiate(_pointPrefab);
                go.transform.position = hit.point + _positionOffset;

                _polygonOutline.Add(go);

                _lineRenderer.positionCount = _polygonOutline.Count;
                _lineRenderer.SetPositions(_polygonOutline.Select(gam => gam.transform.position).ToArray());
            }
            else
            {
                Debug.LogError("Mouse position raycast failed, this should never happen!");
            }
        }
        else if (Input.GetKeyUp(KeyCode.Return) && !_gateEdgeOverlap)
        {
            if (_polygonOutline.Count < 3)
            {
                Debug.LogWarning("Cannot triangulate only one edge.");
                return;
            }

            Mesh      m    = new Mesh();
            Vector3[] edge = _polygonOutline.Select(gam => gam.transform.position).ToArray();
            m.vertices = edge;
            Triangulation.CapMesh(m, Enumerable.Range(0, edge.Length).ToList(), 0, _convexSupport);

            MeshObject outputGO = Instantiate(_outputGameObjectPrefab.gameObject).GetComponent <MeshObject>();
            outputGO.SetMesh(m);
            outputGO.SetPolygonCollider(edge.Select(v3 => new Vector2(v3.x, v3.y)).ToArray());

            _polygonOutline.ForEach(go => Destroy(go));
            _polygonOutline.Clear();
            _lineRenderer.positionCount = _polygonOutline.Count;
            _lineRenderer.startColor    = Color.white;
            _lineRenderer.endColor      = _lineRenderer.startColor;
        }
    }
Ejemplo n.º 13
0
        // Mouse released
        public override void OnMouseUp(MouseEventArgs e)
        {
            ICollection <Sector> selected;
            PixelColor           c;

            base.OnMouseUp(e);

            // Item highlighted?
            if ((highlighted != null) && !highlighted.IsDisposed)
            {
                Sector s = highlighted;

                // Remove highlight to avoid confusion
                Highlight(null);

                // Get a triangulator and bind events
                Triangulation t = new Triangulation();
                t.OnShowPolygon = ShowPolygon;
                t.OnShowEarClip = ShowEarClip;
                //t.OnShowRemaining = ShowRemaining;
                t.Triangulate(s);

                // Start with a clear display
                if (renderer.StartPlotter(true))
                {
                    // Render lines and vertices
                    renderer.PlotLinedefSet(General.Map.Map.Linedefs);
                    renderer.PlotVerticesSet(General.Map.Map.Vertices);

                    // Go for all triangle vertices to render the inside lines only
                    for (int i = 0; i < t.Vertices.Count; i += 3)
                    {
                        if (t.Sidedefs[i + 0] == null)
                        {
                            renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], PixelColor.FromColor(Color.DeepSkyBlue));
                        }
                        if (t.Sidedefs[i + 1] == null)
                        {
                            renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], PixelColor.FromColor(Color.DeepSkyBlue));
                        }
                        if (t.Sidedefs[i + 2] == null)
                        {
                            renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], PixelColor.FromColor(Color.DeepSkyBlue));
                        }
                    }

                    // Go for all triangle vertices to renderthe outside lines only
                    for (int i = 0; i < t.Vertices.Count; i += 3)
                    {
                        if (t.Sidedefs[i + 0] != null)
                        {
                            renderer.PlotLine(t.Vertices[i + 0], t.Vertices[i + 1], PixelColor.FromColor(Color.Red));
                        }
                        if (t.Sidedefs[i + 1] != null)
                        {
                            renderer.PlotLine(t.Vertices[i + 1], t.Vertices[i + 2], PixelColor.FromColor(Color.Red));
                        }
                        if (t.Sidedefs[i + 2] != null)
                        {
                            renderer.PlotLine(t.Vertices[i + 2], t.Vertices[i + 0], PixelColor.FromColor(Color.Red));
                        }
                    }

                    // Done
                    renderer.Finish();
                    renderer.Present();
                    Thread.Sleep(200);
                }
            }
        }
Ejemplo n.º 14
0
    /// <summary>
    /// Function to initialize and setup the (meshes of the) prism based on the the PolyExtruder's set properties.
    /// </summary>
    private void initPrism()
    {
        // The prism consists of 3 meshes:
        // 1. bottom mesh with y = 0
        // 2. top mesh with y = dynamically assigned
        // 3. surrounding polygon connecting 1 and 2 on their outline

        // Note: Meshes will be created as individual Child GameObjects.



        // 1. BOTTOM MESH
        //

        // create bottom GameObject with required components
        GameObject goB = new GameObject();

        goB.transform.parent = this.transform;
        goB.name             = "bottom_" + this.prismName;
        MeshFilter mfB = goB.AddComponent <MeshFilter>();

        goB.AddComponent <MeshCollider>();
        bottomMeshRenderer          = goB.AddComponent <MeshRenderer>();
        bottomMeshRenderer.material = new Material(Shader.Find("Diffuse"));

        // keep reference to bottom mesh
        this.bottomMesh = mfB.mesh;

        // init helper values to create bottom mesh
        List <Vector2>         pointsB   = new List <Vector2>();
        List <List <Vector2> > holesB    = new List <List <Vector2> >();
        List <int>             indicesB  = null;
        List <Vector3>         verticesB = null;

        // convert original polygon data for bottom mesh
        foreach (Vector2 v in originalPolygonVertices)
        {
            pointsB.Add(v);
        }

        // handle hole data (if existing)
        List <Vector2> holeB = new List <Vector2>();

        // perform TRIANGULATION
        Triangulation.triangulate(pointsB, holesB, DEFAULT_BOTTOM_Y, out indicesB, out verticesB);

        // assign indices and vertices and create mesh
        redrawMesh(this.bottomMesh, verticesB, indicesB);

        // reset mesh collider after (re-)creation
        goB.GetComponent <MeshCollider>().sharedMesh = this.bottomMesh;

        /*
         * // generate a simple UVmap
         * Vector2[] uvsB = new Vector2[this.bottomMesh.vertices.Length];
         * for (int i = 0; i < uvsB.Length; i++)
         * {
         *  uvsB[i] = new Vector2(this.bottomMesh.vertices[i].x, this.bottomMesh.vertices[i].y);
         * }
         * this.bottomMesh.uv = uvsB;
         */

        // if polygon is being extruded, "flip" bottom polygon
        // (otherwise it would be facing its rendered side "inside" the extrusion, thus not being visible)
        if (this.is3D)
        {
            goB.transform.localScale    = new Vector3(-1.0f, -1.0f, -1.0f);
            goB.transform.localRotation = Quaternion.Euler(0.0f, 180.0f, 0.0f);
        }


        // create and render top and surround mesh only if GameObject is created as 3D
        //
        if (this.is3D)
        {
            // 2. TOP MESH
            //

            // create top GameObject with required components
            GameObject goT = new GameObject();
            goT.transform.parent = this.transform;
            goT.name             = "top_" + this.prismName;
            MeshFilter mfT = goT.AddComponent <MeshFilter>();
            goT.AddComponent <MeshCollider>();
            topMeshRenderer          = goT.AddComponent <MeshRenderer>();
            topMeshRenderer.material = new Material(Shader.Find("Diffuse"));

            // keep reference to top mesh
            this.topMesh = mfT.mesh;

            // init helper values to create top mesh
            List <Vector2>         pointsT   = new List <Vector2>();
            List <List <Vector2> > holesT    = new List <List <Vector2> >();
            List <int>             indicesT  = null;
            List <Vector3>         verticesT = null;

            // convert original polygon data for top mesh
            foreach (Vector2 v in originalPolygonVertices)
            {
                pointsT.Add(v);
            }

            // handle hole data (if existing)
            List <Vector2> holeT = new List <Vector2>();

            // perform TRIANGULATION
            Triangulation.triangulate(pointsT, holesT, DEFAULT_TOP_Y, out indicesT, out verticesT);

            // assign indices and vertices and create mesh
            redrawMesh(this.topMesh, verticesT, indicesT);

            // reset mesh collider after (re-)creation
            goT.GetComponent <MeshCollider>().sharedMesh = this.topMesh;

            /*
             * // generate a simple UV map
             * Vector2[] uvsT = new Vector2[this.topMesh.vertices.Length];
             * for (int i = 0; i < uvsT.Length; i++)
             * {
             *      uvsT[i] = new Vector2(this.topMesh.vertices[i].x, this.topMesh.vertices[i].y);
             * }
             * this.topMesh.uv = uvsT;
             */



            // 3. SURROUNDING MESH
            //

            // create surrounding GameObject with required components
            GameObject goS = new GameObject();
            goS.transform.parent = this.transform;
            goS.name             = "surround_" + this.prismName;
            MeshFilter mfS = goS.AddComponent <MeshFilter>();
            goS.AddComponent <MeshCollider>();
            surroundMeshRenderer          = goS.AddComponent <MeshRenderer>();
            surroundMeshRenderer.material = new Material(Shader.Find("Diffuse"));

            // keep reference to surrounding mesh
            this.surroundMesh = mfS.mesh;

            // init helper values to create surrounding mesh
            List <int>     indicesS  = new List <int>();
            List <Vector3> verticesS = new List <Vector3>();

            // prepare bottom and top mesh data to build surrounding mesh
            foreach (Vector3 bv in pointsB)
            {
                verticesS.Add(new Vector3(bv.x, DEFAULT_BOTTOM_Y, bv.y));
            }
            foreach (Vector2 tv in pointsT)
            {
                verticesS.Add(new Vector3(tv.x, DEFAULT_TOP_Y, tv.y));
            }

            // CONSTRUCT TRIANGLES:
            // create an array of integers
            // 0 -> verticesB[0]
            // pointsB.Count -> pointsT[0]

            // create 2 triangles (= quad) in each loop
            int indexB   = 0;
            int indexT   = pointsB.Count;
            int sumQuads = verticesS.Count / 2;

            for (int i = 0; i < sumQuads; i++)
            {
                // handle closing triangles
                if (i == (sumQuads - 1))
                {
                    // second to last triangle (1st in quad)
                    indicesS.Add(indexB);
                    indicesS.Add(0);                // flipped with 3. index
                    indicesS.Add(indexT);           // flipped with 2. index

                    // last triangle (2nd in quad)
                    indicesS.Add(0);
                    indicesS.Add(pointsB.Count);    // flipped with 6. index
                    indicesS.Add(indexT);           // flipped with 5. index
                }
                // handle normal case
                else
                {
                    // triangle 1
                    indicesS.Add(indexB);
                    indicesS.Add(indexB + 1);
                    indicesS.Add(indexT);

                    // triangle 2
                    indicesS.Add(indexB + 1);
                    indicesS.Add(indexT + 1);
                    indicesS.Add(indexT);

                    // increment bottom and top index
                    indexB++;
                    indexT++;
                }
            }

            // assign indices and vertices and create mesh
            redrawMesh(this.surroundMesh, verticesS, indicesS);

            /*
             * // reset mesh collider after (re-)creation (not needed right now since no mesh collider is attached)
             *          goS.GetComponent<MeshCollider>().sharedMesh = this.surroundMesh;
             *
             *          // generate a simple UV map
             *          Vector2[] uvsS = new Vector2[this.surroundMesh.vertices.Length];
             *          for (int i = 0; i < uvsS.Length; i++)
             *          {
             *                  uvsS[i] = new Vector2(this.surroundMesh.vertices[i].x, this.surroundMesh.vertices[i].y);
             *          }
             * this.surroundMesh.uv = uvsS;
             */

            // note: for 3D prism, only keep top mesh collider activated (adapt to own preferences this if needed)
            goB.GetComponent <MeshCollider>().enabled = false;
            goT.GetComponent <MeshCollider>().enabled = true;
            goS.GetComponent <MeshCollider>().enabled = false;
        }

        // set height and color
        updateHeight(this.extrusionHeightY);
        updateColor(this.prismColor);
    }
Ejemplo n.º 15
0
 public ModelRoot CreateTerrainMesh(Triangulation triangulation, PBRTexture textures, bool doubleSided = true)
 {
     return(AddTerrainMesh(CreateNewModel(), triangulation, textures, doubleSided));
 }
Ejemplo n.º 16
0
        public ModelRoot GenerateTriangleMesh(List <GeoPoint> points, List <int> indices, PBRTexture textures)
        {
            Triangulation triangulation = new Triangulation(points, indices);

            return(CreateTerrainMesh(triangulation, textures));
        }
Ejemplo n.º 17
0
 public void Flip()
 {
     #region Arrange
     // Nodes.
     var A = new Point(0, 5);
     var B = new Point(5, 0);
     var C = new Point(0, 0);
     var D = new Point(5, 5);
     var L = new Point(-3, 2);
     var T = new Point(3, 7);
     var R = new Point(7, 2);
     var Q = new Point(3, -2);
     // Triangles.
     var LT = new Triangle();
     var TT = new Triangle();
     var RT = new Triangle();
     var QT = new Triangle();
     var T1 = new Triangle();
     var T2 = new Triangle();
     // Ribs.
     // Left triangle.
     var LA = new Rib(L, A, LT, null);
     var LC = new Rib(L, C, LT, null);
     var AC = new Rib(A, C, LT, T1);
     // Top triangle.
     var TA = new Rib(T, A, TT, null);
     var TD = new Rib(T, D, TT, null);
     var AD = new Rib(A, D, TT, T2);
     // Right triangle.
     var RD = new Rib(R, D, RT, null);
     var RB = new Rib(R, B, RT, null);
     var BD = new Rib(B, D, RT, T2);
     // Bottom triangle.
     var QC = new Rib(Q, C, QT, null);
     var QB = new Rib(Q, B, QT, null);
     var BC = new Rib(B, C, QT, T1);
     // Diagonal.
     var AB = new Rib(A, B, T1, T2);
     // Set ribs for triangles.
     LT.SetRibs(LA, LC, AC);
     TT.SetRibs(TA, TD, AD);
     RT.SetRibs(RD, RB, BD);
     QT.SetRibs(QC, QB, BC);
     T1.SetRibs(AC, BC, AB);
     T2.SetRibs(AD, BD, AB);
     // Update triangles.
     Triangle.Update(LT, TT, RT, QT, T1, T2);
     #endregion
     #region Act
     Triangulation.Flip(T1, T2);
     #endregion
     #region Assert
     // Left.
     Assert.IsTrue(LT.IsAdjacent(T1));
     Assert.IsFalse(LT.IsAdjacent(T2));
     Assert.IsTrue(LT.IsAdjacent(null));
     // Top.
     Assert.IsTrue(TT.IsAdjacent(T1));
     Assert.IsFalse(TT.IsAdjacent(T2));
     Assert.IsTrue(TT.IsAdjacent(null));
     // Right.
     Assert.IsTrue(RT.IsAdjacent(T2));
     Assert.IsFalse(RT.IsAdjacent(T1));
     Assert.IsTrue(RT.IsAdjacent(null));
     // Bottom.
     Assert.IsTrue(QT.IsAdjacent(T2));
     Assert.IsFalse(QT.IsAdjacent(T1));
     Assert.IsTrue(QT.IsAdjacent(null));
     // T1 & T2.
     Assert.IsTrue(T1.IsAdjacent(T2));
     Assert.IsTrue(T2.IsAdjacent(T1));
     Assert.IsFalse(T1.IsAdjacent(null));
     Assert.IsFalse(T2.IsAdjacent(null));
     #endregion
 }
Ejemplo n.º 18
0
    static void splitTrianglesLH(Vector4 plane, Vector3[] snapshot, PlaneTriResult[] sidePlanes, int[] sourceIndices, MeshCache meshCache,
                                 TurboList <int> frontIndices, TurboList <int> backIndices,
                                 Infill?infillMode, TurboList <int> frontInfill, TurboList <int> backInfill)
    {
        bool doInfill = infillMode.HasValue && frontInfill != null && backInfill != null;

        Vector3[]    sourceGeometry = meshCache.vertices.array;
        Vector3[]    sourceNormals  = meshCache.normals.array;
        Vector2[]    sourceUVs      = meshCache.UVs.array;
        BoneWeight[] sourceWeights  = meshCache.weights.array;

        float[] pointClassifications = new float[sourceIndices.Length];
        for (int i = 0; i < pointClassifications.Length; i++)
        {
            pointClassifications[i] = MuffinSliceCommon.classifyPoint(ref plane, ref snapshot[sourceIndices[i]]);
        }

        //Now we're going to do the decision making pass. This is where we assess the side figures and produce actions...

        int inputTriangleCount = sourceIndices.Length / 3;

        //A good action count estimate can avoid reallocations.
        //We expect exactly five actions per triangle.
        int actionEstimate = inputTriangleCount * 5;
        List <SplitAction> splitActions = new List <SplitAction>(actionEstimate);

        //We want to count how many vertices are yielded from each triangle split. This will be used later to add the indices.
        short[] frontVertexCount = new short[inputTriangleCount];
        short[] backVertexCount  = new short[inputTriangleCount];

        short totalFront = 0, totalBack = 0;

        for (int i = 0; i < sourceIndices.Length; i += 3)
        {
            int[] indices = { sourceIndices[i], sourceIndices[i + 1], sourceIndices[i + 2] };

            float[] sides = { pointClassifications[i], pointClassifications[i + 1], pointClassifications[i + 2] };

            short indexA = 2;

            short front = 0, back = 0;

            for (short indexB = 0; indexB < 3; indexB++)
            {
                float sideA = sides[indexA];
                float sideB = sides[indexB];

                if (sideB > 0f)
                {
                    if (sideA < 0f)
                    {
                        //Find intersection between A, B. Add to BOTH
                        splitActions.Add(new SplitAction(indices[indexA], indices[indexB], i));
                        front++;
                        back++;
                    }
                    //Add B to FRONT.
                    splitActions.Add(new SplitAction(true, false, indices[indexB]));
                    front++;
                }
                else if (sideB < 0f)
                {
                    if (sideA > 0f)
                    {
                        //Find intersection between A, B. Add to BOTH
                        splitActions.Add(new SplitAction(indices[indexA], indices[indexB], i));
                        front++;
                        back++;
                    }
                    //Add B to BACK.
                    splitActions.Add(new SplitAction(false, true, indices[indexB]));
                    back++;
                }
                else
                {
                    //Add B to BOTH.
                    splitActions.Add(new SplitAction(false, true, indices[indexB]));
                    front++;
                    back++;
                }

                indexA = indexB;
            }

            int j = i / 3;             //This is the triangle counter.

            frontVertexCount[j] = front;
            backVertexCount[j]  = back;

            totalFront += front;
            totalBack  += back;
        }

        // We're going to iterate through the splits only several times, so let's
        //find the subset once now.
        // Since these are STRUCTs, this is going to COPY the array content. The
        //intersectionInverseRelation table made below helps us put it back into the
        //main array before we use it.
        SplitAction[] intersectionActions;
        int[]         intersectionInverseRelation;
        {
            int intersectionCount = 0;

            foreach (SplitAction sa in splitActions)
            {
                if ((sa.flags & SplitAction.INTERSECT) == SplitAction.INTERSECT)
                {
                    intersectionCount++;
                }
            }

            intersectionActions         = new SplitAction[intersectionCount];
            intersectionInverseRelation = new int[intersectionCount];

            int j = 0;
            for (int i = 0; i < splitActions.Count; i++)
            {
                SplitAction sa = splitActions[i];
                if ((sa.flags & SplitAction.INTERSECT) == SplitAction.INTERSECT)
                {
                    intersectionActions[j]         = sa;
                    intersectionInverseRelation[j] = i;
                    j++;
                }
            }
        }

        // Next, we're going to find out which splitActions replicate the work of other split actions.
        //A given SA replicates another if and only if it _both_ calls for an intersection _and_ has
        //the same two parent indices (index0 and index1). This is because all intersections are called
        //with the same other parameters, so any case with an index0 and index1 matching will yield the
        //same results.
        // Only caveat is that two given splitActions might as the source indices in reverse order, so
        //we'll arbitrarily decide that "greater first" or something is the correct order. Flipping this
        //order has no consequence until after the intersection is found (at which point flipping the order
        //necessitates converting intersection i to 1-i to flip it as well.)
        // We can assume that every SA has at most 1 correlation. For a given SA, we'll search the list
        //UP TO its own index and, if we find one, we'll take the other's index and put it into the CLONE OF
        //slot.
        // So if we had a set like AFDBAK, than when the _latter_ A comes up for assessment, it'll find
        //the _first_ A (with an index of 0) and set the latter A's cloneOf figure to 0. This way we know
        //any latter As are a clone of the first A.

        for (int i = 0; i < intersectionActions.Length; i++)
        {
            SplitAction a = intersectionActions[i];

            //Ensure that the index0, index1 figures are all in the same order.
            //(We'll do this as we walk the list.)
            if (a.index0 > a.index1)
            {
                int j = a.index0;
                a.index0 = a.index1;
                a.index1 = j;
            }

            Vector3 aVector = sourceGeometry[a.index0] + sourceGeometry[a.index1];

            //Only latters clone formers, so we don't need to search up to and past the self.
            for (int j = 0; j < i; j++)
            {
                SplitAction b = intersectionActions[j];

                bool match = a.index0 == b.index0 && a.index1 == b.index1;

                if (!match)
                {
                    Vector3 bVector = sourceGeometry[b.index0] + sourceGeometry[b.index1];

//					match = Mathf.Approximately(aVector.x, bVector.x);
//					match &= Mathf.Approximately(aVector.y, bVector.y);
//					match &= Mathf.Approximately(aVector.z, bVector.z);

                    // What are the chances, really?

                    match = Mathf.Approximately(aVector.x + aVector.y + aVector.z, bVector.x + bVector.y + bVector.z);
                }

                if (match)
                {
                    a.cloneOf = j;
                }
            }

            intersectionActions[i] = a;
        }

        //Next, we want to perform all INTERSECTIONS. Any action which has an intersection needs to have that, like, done.

        for (int i = 0; i < intersectionActions.Length; i++)
        {
            SplitAction sa = intersectionActions[i];

            if (sa.cloneOf == SplitAction.nullIndex)
            {
                /*float ir = vertexSums[ sa.index0 ] + vertexSums[ sa.index1 ];
                 *
                 * ir += 1f;
                 * ir *= 0.5f;
                 * ir = 1f - ir;
                 *
                 * sa.intersectionResult = ir;*/

                Vector3 pointA = snapshot[sa.index0];
                Vector3 pointB = snapshot[sa.index1];
                sa.intersectionResult  = MuffinSliceCommon.intersectCommon(ref pointB, ref pointA, ref plane);
                intersectionActions[i] = sa;
            }
        }

        // Let's create a table that relates an INTERSECTION index to a GEOMETRY index with an offset of 0 (for example
        //to refer to our newVertices or to the transformedVertices or whatever; internal use.)
        // We can also set up our realIndex figures in the same go.
        int newIndexStartsAt  = meshCache.vertices.Count;
        int uniqueVertexCount = 0;

        int[] localIndexByIntersection = new int[intersectionActions.Length];
        {
            int currentLocalIndex = 0;
            for (int i = 0; i < intersectionActions.Length; i++)
            {
                SplitAction sa = intersectionActions[i];

                int j;

                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    j = currentLocalIndex++;
                }
                else
                {
                    //This assumes that the widget that we are a clone of already has its localIndexByIntersection assigned.
                    //We assume this because above – where we seek for clones – we only look behind for cloned elements.
                    j = localIndexByIntersection[sa.cloneOf];
                }

                sa.realIndex = newIndexStartsAt + j;

                localIndexByIntersection[i] = j;

                intersectionActions[i] = sa;
            }
            uniqueVertexCount = currentLocalIndex;
        }

        //Let's figure out how much geometry we might have.
        //The infill geometry is a pair of clones of this geometry, but with different NORMALS and UVs. (Each set has different normals.)

        int newGeometryEstimate = uniqueVertexCount * (doInfill ? 3 : 1);

        //In this ACTION pass we'll act upon intersections by fetching both referred vertices and LERPing as appropriate.
        //The resultant indices will be written out over the index0 figures.

        Vector3[]    newVertices = new Vector3[newGeometryEstimate];
        Vector3[]    newNormals  = new Vector3[newGeometryEstimate];
        Vector2[]    newUVs      = new Vector2[newGeometryEstimate];
        BoneWeight[] newWeights  = new BoneWeight[newGeometryEstimate];

        //LERP to create vertices
        {
            int currentNewIndex = 0;
            foreach (SplitAction sa in intersectionActions)
            {
                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    Vector3 v  = sourceGeometry[sa.index0];
                    Vector3 v2 = sourceGeometry[sa.index1];
                    newVertices[currentNewIndex] = Vector3.Lerp(v2, v, sa.intersectionResult);
                    currentNewIndex++;
                }
            }
        }

        //Normals:
        {
            int currentNewIndex = 0;
            foreach (SplitAction sa in intersectionActions)
            {
                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    Vector3 n  = sourceNormals[sa.index0];
                    Vector3 n2 = sourceNormals[sa.index1];
                    newNormals[currentNewIndex] = Vector3.Lerp(n2, n, sa.intersectionResult);
                    currentNewIndex++;
                }
            }
        }

        //UVs:
        {
            int currentNewIndex = 0;
            foreach (SplitAction sa in intersectionActions)
            {
                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    Vector2 uv  = sourceUVs[sa.index0];
                    Vector2 uv2 = sourceUVs[sa.index1];
                    newUVs[currentNewIndex] = Vector2.Lerp(uv2, uv, sa.intersectionResult);
                    currentNewIndex++;
                }
            }
        }

        //Bone Weights:
        {
            int currentNewIndex = 0;
            foreach (SplitAction sa in intersectionActions)
            {
                if (sa.cloneOf == SplitAction.nullIndex)
                {
                    BoneWeight bw;

                    if (sidePlanes[sa.index0] == PlaneTriResult.PTR_FRONT)
                    {
                        bw = sourceWeights[sa.index0];
                    }
                    else
                    {
                        bw = sourceWeights[sa.index1];
                    }

                    newWeights[currentNewIndex] = bw;
                    currentNewIndex++;
                }
            }
        }

        //All the polygon triangulation algorithms depend on having a 2D polygon. We also need the slice plane's
        //geometry in two-space to map the UVs.

        //NOTE that as we only need this data to analyze polygon geometry for triangulation, we can TRANSFORM (scale, translate, rotate)
        //these figures any way we like, as long as they retain the same relative geometry. So we're going to perform ops on this
        //data to create the UVs by scaling it around, and we'll feed the same data to the triangulator.

        //Our's exists in three-space, but is essentially flat... So we can transform it onto a flat coordinate system.
        //The first three figures of our plane four-vector describe the normal to the plane, so if we can create
        //a transformation matrix from that normal to the up normal, we can transform the vertices for observation.
        //We don't need to transform them back; we simply refer to the original vertex coordinates by their index,
        //which (as this is an ordered set) will match the indices of coorisponding transformed vertices.

        //This vector-vector transformation comes from Benjamin Zhu at SGI, pulled from a 1992
        //forum posting here: http://steve.hollasch.net/cgindex/math/rotvecs.html

        /*	"A somewhat "nasty" way to solve this problem:
         *
         *      Let V1 = [ x1, y1, z1 ], V2 = [ x2, y2, z2 ]. Assume V1 and V2 are already normalized.
         *
         *          V3 = normalize(cross(V1, V2)). (the normalization here is mandatory.)
         *          V4 = cross(V3, V1).
         *
         *               [ V1 ]
         *          M1 = [ V4 ]
         *               [ V3 ]
         *
         *          cos = dot(V2, V1), sin = dot(V2, V4)
         *
         *               [ cos   sin    0 ]
         *          M2 = [ -sin  cos    0 ]
         *               [ 0     0      1 ]
         *
         *      The sought transformation matrix is just M1^-1 * M2 * M1. This might well be a standard-text solution."
         *
         *      -Ben Zhu, SGI, 1992
         */

        Vector2[] transformedVertices = new Vector2[0];
        int       infillFrontOffset = 0, infillBackOffset = 0;

        if (doInfill)
        {
            transformedVertices = new Vector2[newGeometryEstimate / 3];

            Matrix4x4 flattenTransform;

            //Based on the algorithm described above, this will create a matrix permitting us
            //to multiply a given vertex yielding a vertex transformed to an XY plane (where Z is
            //undefined.)
            {
                Vector3 v1 = Vector3.forward;
                Vector3 v2 = new Vector3(plane.x, plane.y, plane.z).normalized;

                float difference = (v1 - v2).magnitude;

                if (difference > 0.01f)
                {
                    Vector3 v3 = Vector3.Cross(v1, v2).normalized;
                    Vector3 v4 = Vector3.Cross(v3, v1);

                    float cos = Vector3.Dot(v2, v1);
                    float sin = Vector3.Dot(v2, v4);

                    Matrix4x4 m1 = Matrix4x4.identity;
                    m1.SetRow(0, (Vector4)v1);
                    m1.SetRow(1, (Vector4)v4);
                    m1.SetRow(2, (Vector4)v3);

                    Matrix4x4 m1i = m1.inverse;

                    Matrix4x4 m2 = Matrix4x4.identity;
                    m2.SetRow(0, new Vector4(cos, sin, 0, 0));
                    m2.SetRow(1, new Vector4(-sin, cos, 0, 0));

                    flattenTransform = m1i * m2 * m1;
                }
                else
                {
                    flattenTransform = Matrix4x4.identity;
                }
            }
            for (int i = 0; i < transformedVertices.Length; i++)
            {
                transformedVertices[i] = (Vector2)flattenTransform.MultiplyPoint3x4(newVertices[i]);
//				Debug.Log(newVertices[i] + " > " + transformedVertices[i]);
            }

            // We want to normalize the entire transformed vertices. To do this, we find the largest
            //floats in either (by abs). Then we scale. Of course, this normalizes us to figures
            //in the range of [-1f,1f] (not necessarily extending all the way on both sides), and
            //what we need are figures between 0f and 1f (not necessarily filling, but necessarily
            //not spilling.) So we'll shift it here.
            {
                float x = 0f, y = 0f;

                for (int i = 0; i < transformedVertices.Length; i++)
                {
                    Vector2 v = transformedVertices[i];

                    v.x = Mathf.Abs(v.x);
                    v.y = Mathf.Abs(v.y);

                    if (v.x > x)
                    {
                        x = v.x;
                    }
                    if (v.y > y)
                    {
                        y = v.y;
                    }
                }

                //We would use 1f/x, 1f/y but we also want to scale everything to half (and perform an offset) as
                //described above.
                x = 0.5f / x;
                y = 0.5f / y;

                Rect r = new Rect(0, 0, 1f, 1f);

                for (int i = 0; i < transformedVertices.Length; i++)
                {
                    Vector2 v = transformedVertices[i];
                    v.x *= x;
                    v.y *= y;
                    v.x += 0.5f;
                    v.y += 0.5f;
                    v.x *= r.width;
                    v.y *= r.height;
                    v.x += r.x;
                    v.y += r.y;
                    transformedVertices[i] = v;
                }
            }

            //Now let's build the geometry for the two slice in-fills.
            //One is for the front side, and the other for the back side. Each has differing normals.

            infillFrontOffset = uniqueVertexCount;
            infillBackOffset  = uniqueVertexCount * 2;

            //The geometry is identical...

            System.Array.Copy(newVertices, 0, newVertices, infillFrontOffset, uniqueVertexCount);
            System.Array.Copy(newVertices, 0, newVertices, infillBackOffset, uniqueVertexCount);

            System.Array.Copy(newWeights, 0, newWeights, infillFrontOffset, uniqueVertexCount);
            System.Array.Copy(newWeights, 0, newWeights, infillBackOffset, uniqueVertexCount);

            System.Array.Copy(transformedVertices, 0, newUVs, infillFrontOffset, uniqueVertexCount);
            System.Array.Copy(transformedVertices, 0, newUVs, infillBackOffset, uniqueVertexCount);

            Vector3 infillFrontNormal = ((Vector3)plane) * -1f;
            infillFrontNormal.Normalize();

            for (int i = infillFrontOffset; i < infillBackOffset; i++)
            {
                newNormals[i] = infillFrontNormal;
            }

            Vector3 infillBackNormal = (Vector3)plane;
            infillBackNormal.Normalize();

            for (int i = infillBackOffset; i < newNormals.Length; i++)
            {
                newNormals[i] = infillBackNormal;
            }
        }

        //Get the exact indices into two tables. Note that these are indices for TRIANGLES and QUADS, which we'll triangulate in the next section.
        int[] newFrontIndex = new int[totalFront];
        int[] newBackIndex  = new int[totalBack];

        //Note that here we refer to split actions again, so let's copy back the updated splitActions.
        for (int i = 0; i < intersectionActions.Length; i++)
        {
            int j = intersectionInverseRelation[i];
            splitActions[j] = intersectionActions[i];
        }

        int newFrontIndexCount = 0, newBackIndexCount = 0;

        foreach (SplitAction sa in splitActions)
        {
            if ((sa.flags & SplitAction.TO_FRONT) == SplitAction.TO_FRONT)
            {
                newFrontIndex[newFrontIndexCount] = sa.realIndex;
                newFrontIndexCount++;
            }
            if ((sa.flags & SplitAction.TO_BACK) == SplitAction.TO_BACK)
            {
                newBackIndex[newBackIndexCount] = sa.realIndex;
                newBackIndexCount++;
            }
        }

        //Now we need to triangulate sets of quads.
        //We recorded earlier whether we're looking at triangles or quads – in order. So we have a pattern like TTQTTQQTTTQ, and
        //we can expect these vertices to match up perfectly to what the above section of code dumped out.

        int startIndex = 0;

        int[] _indices3 = new int[3];
        int[] _indices4 = new int[6];

        foreach (short s in frontVertexCount)
        {
            if (s == 3)
            {
                _indices3[0] = newFrontIndex[startIndex];
                _indices3[1] = newFrontIndex[startIndex + 1];
                _indices3[2] = newFrontIndex[startIndex + 2];
                frontIndices.AddArray(_indices3);
            }
            else if (s == 4)
            {
                _indices4[0] = newFrontIndex[startIndex];
                _indices4[1] = newFrontIndex[startIndex + 1];
                _indices4[2] = newFrontIndex[startIndex + 3];
                _indices4[3] = newFrontIndex[startIndex + 1];
                _indices4[4] = newFrontIndex[startIndex + 2];
                _indices4[5] = newFrontIndex[startIndex + 3];
                frontIndices.AddArray(_indices4);
            }
            startIndex += s;
        }

        startIndex = 0;

        foreach (short s in backVertexCount)
        {
            if (s == 3)
            {
                _indices3[0] = newBackIndex[startIndex];
                _indices3[1] = newBackIndex[startIndex + 1];
                _indices3[2] = newBackIndex[startIndex + 2];
                backIndices.AddArray(_indices3);
            }
            else if (s == 4)
            {
                _indices4[0] = newBackIndex[startIndex];
                _indices4[1] = newBackIndex[startIndex + 1];
                _indices4[2] = newBackIndex[startIndex + 3];
                _indices4[3] = newBackIndex[startIndex + 1];
                _indices4[4] = newBackIndex[startIndex + 2];
                _indices4[5] = newBackIndex[startIndex + 3];
                backIndices.AddArray(_indices4);
            }
            startIndex += s;
        }

        //Let's add this shiznit in!

        meshCache.vertices.AddArray(newVertices);
        meshCache.normals.AddArray(newNormals);
        meshCache.UVs.AddArray(newUVs);
        meshCache.weights.AddArray(newWeights);

        //Now we need to fill in the slice hole. There are TWO infillers; the Sloppy and Meticulous.

        //The sloppy infiller will find a point in the middle of all slice vertices and produce a triangle fan.
        //It can work fast, but will have issues with non-roundish cross sections or cross sections with multiple holes.

        //The meticulous infill can distinguish between polygons and accurately fill multiple holes, but is more sensitive to
        //geometrical oddities. It may fail when slicing certain joints because of the way that not all geometry is sliced.
        //It is transferred from Turbo Slicer, where it is a key part of the product, but it is not most appropriate here.
        //Nevertheless, it is here in case it is needed.

        if (doInfill && infillMode == Infill.Sloppy)
        {
            VectorAccumulator centerVertex = new VectorAccumulator();
            VectorAccumulator centerUV     = new VectorAccumulator();
            VectorAccumulator centerNormal = new VectorAccumulator();

            Dictionary <int, float> weightsByBone = new Dictionary <int, float>();

            int sliceVertexCount = newGeometryEstimate / 3;

            for (int i = 0; i < sliceVertexCount; i++)
            {
                centerVertex.addFigure(newVertices[i]);
                centerUV.addFigure(newUVs[i]);
                centerNormal.addFigure(newNormals[i]);

                BoneWeight bw = newWeights[i];

                if (weightsByBone.ContainsKey(bw.boneIndex0))
                {
                    weightsByBone[bw.boneIndex0] += bw.weight0;
                }
                else
                {
                    weightsByBone[bw.boneIndex0] = bw.weight0;
                }

                /*if(weightsByBone.ContainsKey(bw.boneIndex1))
                 *      weightsByBone[bw.boneIndex1] += bw.weight1;
                 * else
                 *      weightsByBone[bw.boneIndex1] = bw.weight1;
                 *
                 * if(weightsByBone.ContainsKey(bw.boneIndex2))
                 *      weightsByBone[bw.boneIndex2] += bw.weight2;
                 * else
                 *      weightsByBone[bw.boneIndex2] = bw.weight2;
                 *
                 * if(weightsByBone.ContainsKey(bw.boneIndex3))
                 *      weightsByBone[bw.boneIndex3] += bw.weight3;
                 * else
                 *      weightsByBone[bw.boneIndex3] = bw.weight3;*/
            }

            List <KeyValuePair <int, float> > orderedWeights = new List <KeyValuePair <int, float> >(weightsByBone);

            orderedWeights.Sort((firstPair, nextPair) =>
            {
                return(-firstPair.Value.CompareTo(nextPair.Value));
            }
                                );

            BoneWeight centerWeight     = new BoneWeight();
            Vector4    weightNormalizer = Vector4.zero;

            if (orderedWeights.Count > 0)
            {
                centerWeight.boneIndex0 = orderedWeights[0].Key;
                weightNormalizer.x      = 1f;
            }

            weightNormalizer.Normalize();

            centerWeight.weight0 = weightNormalizer.x;
            centerWeight.weight1 = weightNormalizer.y;
            centerWeight.weight2 = weightNormalizer.z;
            centerWeight.weight3 = weightNormalizer.w;

            int centerIndex = meshCache.vertices.Count;

            meshCache.vertices.Count++;
            meshCache.normals.Count++;
            meshCache.UVs.Count++;
            meshCache.weights.Count++;

            meshCache.vertices.array[centerIndex] = centerVertex.mean;
            meshCache.UVs.array[centerIndex]      = centerUV.mean;
            meshCache.normals.array[centerIndex]  = centerNormal.mean;
            meshCache.weights.array[centerIndex]  = centerWeight;

            Vector2 transformedCenter = Vector2.zero;
            foreach (Vector2 v in transformedVertices)
            {
                transformedCenter += v;
            }
            transformedCenter /= transformedVertices.Length;

            Dictionary <int, float> angleByIndex = new Dictionary <int, float>();
            for (int i = 0; i < transformedVertices.Length; i++)
            {
                Vector2 delta = transformedVertices[i] - transformedCenter;
                angleByIndex[i] = Mathf.Atan2(delta.y, delta.x);
            }

            List <KeyValuePair <int, float> > orderedVertices = new List <KeyValuePair <int, float> >(angleByIndex);

            orderedVertices.Sort((firstPair, nextPair) =>
            {
                return(firstPair.Value.CompareTo(nextPair.Value));
            }
                                 );

            for (int i = 0; i < orderedVertices.Count; i++)
            {
                bool atEnd = i == orderedVertices.Count - 1;
                int  iNext = atEnd ? 0 : i + 1;

                int index0 = orderedVertices[i].Key;
                int index1 = orderedVertices[iNext].Key;

                int[] frontInfillIndices = { centerIndex, index1 + infillFrontOffset + newIndexStartsAt, index0 + infillFrontOffset + newIndexStartsAt };
                frontInfill.AddArray(frontInfillIndices);

                int[] backInfillIndices = { centerIndex, index0 + infillBackOffset + newIndexStartsAt, index1 + infillBackOffset + newIndexStartsAt };
                backInfill.AddArray(backInfillIndices);
            }
        }
        else if (doInfill && infillMode == Infill.Meticulous)
        {
            //If that fails, one can use the more accurate but more delicate "meticulous" infiller.

            //We need to find the POLYGON[s] representing the slice hole[s]. There may be more than one.
            //Then we need to TRIANGULATE these polygons and write them out.

            //Above we've built the data necessary to pull this off. We have:

            // - Geometry for the polygon around the edges in Vertex3 / Normal / UV format, already added
            //to the geometry setup.
            // - Geometry for the polygon in Vertex2 format in matching order, aligned to the slice plane.
            // - A collection of all data points and 1:1 hashes representing their physical location.

            //In this mess of data here may be 0 or non-zero CLOSED POLYGONS. We need to walk the list and
            //identify each CLOSED POLYGON (there may be none, or multiples). Then, each of these must be
            //triangulated separately.

            //Vertices connected to each other in a closed polygon can be found to associate with each other
            //in two ways. Envision a triangle strip that forms a circular ribbon – and that we slice through
            //the middle of this ribbon. Slice vertices come in two kinds of pairs; there are pairs that COME FROM
            //the SAME triangle, and pairs that come from ADJACENT TRIANGLES. The whole chain is formed from
            //alternating pair-types.

            //So for example vertex A comes from the same triangle as vertex B, which in turn matches the position
            //of the NEXT triangle's vertex A.

            //The data is prepared for us to be able to identify both kinds of associations. First,
            //association by parent triangle is encoded in the ORDERING. Every PAIR from index 0 shares a parent
            //triangle; so indices 0-1, 2-3, 4-5 and so on are each a pair from a common parent triangle.

            //Meanwhile, vertices generated from the common edge of two different triangles will have the SAME
            //POSITION in three-space.

            //We don't have to compare Vector3s, however; this has already been done. Uniques were eliminated above.
            //What we have is a table; localIndexByIntersection. This list describes ALL SLICE VERTICES in terms
            //of which VERTEX (in the array – identified by index) represents that slice vertex. So if we see that
            //localIndexByIntersection[0] == localIndexByIntersection[4], than we know that slice vertices 0 and 4
            //share the same position in three space.

            //With that in mind, we're going to go through the list in circles building chains out of these
            //connections.

            List <int>         currentWorkingPoly = new List <int>();
            List <int>         currentTargetPoly  = new List <int>();
            List <List <int> > allPolys           = new List <List <int> >();
            List <int>         claimed            = new List <int>();

            int lastAdded = -1;

            //ASSUMPTION: Every element will be claimed into some kind of chain by the end whether correlated or not.
            do
            {
                for (int i = 0; i < localIndexByIntersection.Length; i++)
                {
                    bool go = false, fail = false, startNewChain = false;

                    //If we didn't just add one, we're looking to start a chain. That means we have to find one that
                    //isn't already claimed.
                    if (lastAdded < 0)
                    {
                        go = claimed.Contains(i) == false;
                    }
                    else if (lastAdded == i)
                    {
                        //We've gone through twice without finding a match. This means there isn't one, or something.

                        fail = true;
                    }
                    else
                    {
                        //Otherwise, we're trying to find the next-in-chain.
                        //A valid next-in-chain is connected by geometry which, as discussed, means it's connected
                        //by having matching parent indices (index0, index1).

                        bool match = localIndexByIntersection[i] == localIndexByIntersection[lastAdded];

                        //But there's a special case about the match; it's possible that we've closed the loop!
                        //How do we know we've closed the loop? There are multiple ways but the simplest is that
                        //the chain already contains the element in question.

                        bool loopComplete = match && currentWorkingPoly.Contains(i);

                        if (loopComplete)
                        {
                            allPolys.Add(currentTargetPoly);
                            startNewChain = true;
                        }
                        else
                        {
                            go = match;
                        }
                    }

                    if (go)
                    {
                        int partnerByParent = i % 2 == 1 ? i - 1 : i + 1;

                        int[] pair = { i, partnerByParent };

                        currentWorkingPoly.AddRange(pair);
                        claimed.AddRange(pair);

                        currentTargetPoly.Add(partnerByParent);

                        lastAdded = partnerByParent;

                        //Skip ahead and resume the search _from_ here, so that we don't step into it
                        //again from within this loop walk.
                        i = partnerByParent;
                    }
                    else if (fail)
                    {
                        //We want to start a fresh poly without adding this to the valid polys.
                        startNewChain = true;

                        //Debug.Log("[fail]");
                    }

                    if (startNewChain)
                    {
                        currentWorkingPoly.Clear();
                        currentTargetPoly = new List <int>();
                        lastAdded         = -1;
                    }
                }
            }while(currentWorkingPoly.Count > 0);

            //Now we go through each poly and triangulate it.

            foreach (List <int> _poly in allPolys)
            {
                Vector2[] poly = new Vector2[_poly.Count];

                for (int i = 0; i < poly.Length; i++)
                {
                    int j = localIndexByIntersection[_poly[i]];
                    poly[i] = transformedVertices[j];
                }

                int[] result;

                if (Triangulation.triangulate(poly, out result))
                {
                    int[] front = new int[result.Length];
                    int[] back  = new int[result.Length];

                    for (int i = 0; i < result.Length; i++)
                    {
                        int p     = _poly[result[i]];
                        int local = localIndexByIntersection[p];
                        front[i] = local + infillFrontOffset + newIndexStartsAt;
                        back[i]  = local + infillBackOffset + newIndexStartsAt;
                    }

                    for (int i = 0; i < result.Length; i += 3)
                    {
                        int j = front[i];
                        front[i]     = front[i + 2];
                        front[i + 2] = j;
                    }

                    frontInfill.AddArray(front);
                    backInfill.AddArray(back);
                }
            }
        }
    }
Ejemplo n.º 19
0
        public static ModelRoot AddTINMesh(ModelRoot model, HeightMap hMap, double precision, SharpGltfService gltf, PBRTexture textures, int srid)
        {
            Triangulation triangulation = GenerateTIN(hMap, precision, srid);

            return(gltf.AddTerrainMesh(model, triangulation, textures, doubleSided: true));
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Generate a triangle mesh from supplied height map, triangulating and optionaly mapping UVs
        /// and generate sides and bottom (like a box where the top is the triangulated height map)
        /// </summary>
        /// <param name="heightMap">Height map.</param>
        /// <param name="thickness">Determines how box height will be calculated</param>
        /// <param name="zValue">Z value to apply for box calculation</param>
        /// <returns></returns>
        public MeshPrimitive GenerateTriangleMesh_Boxed(HeightMap heightMap, BoxBaseThickness thickness = BoxBaseThickness.FixedElevation, float zValue = 0f)
        {
            Triangulation triangulation = _meshService.GenerateTriangleMesh_Boxed(heightMap, thickness, zValue);

            return(GenerateTriangleMesh(triangulation));
        }
Ejemplo n.º 21
0
        static TimeSpan TestNDDelau(int dim, int numVert, double size)
        {
            var vertices = CreateRandomVertices(dim, numVert, size);

            return(RunComputation(() => Triangulation.CreateDelaunay(vertices)));
        }
Ejemplo n.º 22
0
    public static List <List <Vector3> > GetPolygons(List <List <Entity> > loops, ref List <List <Id> > ids)
    {
        if (ids != null)
        {
            ids.Clear();
        }
        var result = new List <List <Vector3> >();

        if (loops == null)
        {
            return(result);
        }

        foreach (var loop in loops)
        {
            var       polygon   = new List <Vector3>();
            List <Id> idPolygon = null;
            if (ids != null)
            {
                idPolygon = new List <Id>();
            }

            Action <IEnumerable <Vector3>, Entity> AddToPolygon = (points, entity) => {
                polygon.AddRange(points);
                if (idPolygon != null)
                {
                    idPolygon.AddRange(Enumerable.Repeat(entity.guid, points.Count()));
                }
            };

            for (int i = 0; i < loop.Count; i++)
            {
                if (loop[i] is ISegmentaryEntity)
                {
                    var cur  = loop[i] as ISegmentaryEntity;
                    var next = loop[(i + 1) % loop.Count] as ISegmentaryEntity;
                    if (!next.begin.IsCoincidentWith(cur.begin) && !next.end.IsCoincidentWith(cur.begin))
                    {
                        AddToPolygon(cur.segmentPoints, loop[i]);
                    }
                    else
                    if (!next.begin.IsCoincidentWith(cur.end) && !next.end.IsCoincidentWith(cur.end))
                    {
                        AddToPolygon(cur.segmentPoints.Reverse(), loop[i]);
                    }
                    else if (next.begin.IsCoincidentWith(cur.end))
                    {
                        AddToPolygon(cur.segmentPoints, loop[i]);
                    }
                    else
                    if (i % 2 == 0)
                    {
                        AddToPolygon(cur.segmentPoints, loop[i]);
                    }
                    else
                    {
                        AddToPolygon(cur.segmentPoints.Reverse(), loop[i]);
                    }
                }
                else
                if (loop[i] is ILoopEntity)
                {
                    var cur = loop[i] as ILoopEntity;
                    AddToPolygon(cur.loopPoints, loop[i]);
                }
                else
                {
                    continue;
                }
                if (polygon.Count > 0)
                {
                    polygon.RemoveAt(polygon.Count - 1);
                    if (idPolygon != null)
                    {
                        idPolygon.RemoveAt(idPolygon.Count - 1);
                    }
                }
            }
            if (polygon.Count < 3)
            {
                continue;
            }
            if (!Triangulation.IsClockwise(polygon))
            {
                polygon.Reverse();
                if (idPolygon != null)
                {
                    idPolygon.Reverse();
                }
            }
            result.Add(polygon);
            if (ids != null)
            {
                ids.Add(idPolygon);
            }
        }
        return(result);
    }
    public static Mesh Triangulate3D(Polygon2D polygon, float z, Vector2 UVScale, Vector2 UVOffset, Triangulation triangulation)
    {
        Mesh result = null;

        switch (triangulation)
        {
        case Triangulation.Advanced:
            Slicer2DProfiler.IncAdvancedTriangulation();

            Polygon2D newPolygon = polygon;

            List <Vector3> sideVertices  = new List <Vector3>();
            List <int>     sideTriangles = new List <int>();
            int            vCount        = 0;

            foreach (Pair2D pair in Pair2D.GetList(polygon.pointsList))
            {
                Vector3 pointA = new Vector3((float)pair.A.x, (float)pair.A.y, 0);
                Vector3 pointB = new Vector3((float)pair.B.x, (float)pair.B.y, 0);
                Vector3 pointC = new Vector3((float)pair.B.x, (float)pair.B.y, 1);
                Vector3 pointD = new Vector3((float)pair.A.x, (float)pair.A.y, 1);

                sideVertices.Add(pointA);
                sideVertices.Add(pointB);
                sideVertices.Add(pointC);
                sideVertices.Add(pointD);

                sideTriangles.Add(vCount + 2);
                sideTriangles.Add(vCount + 1);
                sideTriangles.Add(vCount + 0);

                sideTriangles.Add(vCount + 0);
                sideTriangles.Add(vCount + 3);
                sideTriangles.Add(vCount + 2);

                vCount += 4;
            }

            Mesh meshA = PerformTriangulation(newPolygon, UVScale, UVOffset);

            Mesh           meshB     = new Mesh();
            List <Vector3> verticesB = new List <Vector3>();
            foreach (Vector3 v in meshA.vertices)
            {
                verticesB.Add(new Vector3(v.x, v.y, v.z + z));
            }
            meshB.vertices  = verticesB.ToArray();
            meshB.triangles = meshA.triangles.Reverse().ToArray();

            Mesh mesh = new Mesh();
            mesh.SetVertices(sideVertices);
            mesh.SetTriangles(sideTriangles, 0);

            List <Vector3> vertices = new List <Vector3>();
            foreach (Vector3 v in meshA.vertices)
            {
                vertices.Add(v);
            }
            foreach (Vector3 v in meshB.vertices)
            {
                vertices.Add(v);
            }
            foreach (Vector3 v in sideVertices)
            {
                vertices.Add(v);
            }
            mesh.vertices = vertices.ToArray();

            List <int> triangles = new List <int>();
            foreach (int p in meshA.triangles)
            {
                triangles.Add(p);
            }
            int count = meshA.vertices.Count();
            foreach (int p in meshB.triangles)
            {
                triangles.Add(p + count);
            }
            count = meshA.vertices.Count() + meshB.vertices.Count();
            foreach (int p in sideTriangles)
            {
                triangles.Add(p + count);
            }
            mesh.triangles = triangles.ToArray();

            mesh.RecalculateNormals();
            mesh.RecalculateBounds();

            result = mesh;

            break;
        }

        return(result);
    }
Ejemplo n.º 24
0
    public static Solid CreateSolidExtrusion(List <List <Entity> > entitiyLoops, float extrude, UnityEngine.Matrix4x4 tf, IdPath feature)
    {
        var  ids      = new List <List <Id> >();
        var  polygons = Sketch.GetPolygons(entitiyLoops, ref ids);
        bool inversed = extrude < 0f;

        var polys = new List <Polygon>();

        Func <Vector3, Vector3> TF = v => tf.MultiplyPoint(v);

        for (int pi = 0; pi < polygons.Count; pi++)
        {
            var p         = polygons[pi];
            var pid       = ids[pi];
            var pv        = new List <Vector3>(p);
            var triangles = Triangulation.Triangulate(pv);

            IdPath polyId        = feature.With(new Id(-1));
            bool   invComp       = true;
            var    shift         = Vector3.zero;
            var    extrudeVector = Vector3.forward * extrude;

            for (int side = 0; side < 2; side++)
            {
                for (int i = 0; i < triangles.Count / 3; i++)
                {
                    var polygonVertices = new List <Vertex>();
                    for (int j = 0; j < 3; j++)
                    {
                        polygonVertices.Add(TF(triangles[i * 3 + j] + shift).ToVertex());
                    }
                    if (inversed == invComp)
                    {
                        polygonVertices.Reverse();
                    }
                    polys.Add(new Polygon(polygonVertices, polyId));
                }
                polyId  = feature.With(new Id(-2));
                invComp = false;
                shift   = extrudeVector;
            }

            Dictionary <Id, IdPath> paths = new Dictionary <Id, IdPath>();
            for (int i = 0; i < p.Count; i++)
            {
                var polygonVertices = new List <Vertex>();
                polygonVertices.Add(TF(p[i]).ToVertex());
                polygonVertices.Add(TF(p[(i + 1) % p.Count]).ToVertex());
                polygonVertices.Add(TF((p[(i + 1) % p.Count] + extrudeVector)).ToVertex());
                polygonVertices.Add(TF((p[i] + extrudeVector)).ToVertex());
                if (!inversed)
                {
                    polygonVertices.Reverse();
                }
                IdPath curPath = null;
                if (!paths.ContainsKey(pid[i]))
                {
                    curPath = feature.With(pid[i]);
                    paths.Add(pid[i], curPath);
                }
                else
                {
                    curPath = paths[pid[i]];
                }
                polys.Add(new Polygon(polygonVertices, curPath));
            }
        }
        return(Solid.FromPolygons(polys));
    }
Ejemplo n.º 25
0
        public static Mesh Triangulate2D(Polygon2D polygon, Vector2 UVScale, Vector2 UVOffset, Triangulation triangulation)
        {
            polygon.Normalize();

            Mesh result = null;

            switch (triangulation)
            {
            case Triangulation.Advanced:
                Slicer2D.Profiler.IncAdvancedTriangulation();

                float GC = Slicer2D.Settings.GetGarbageCollector();
                if (GC > 0 & polygon.GetArea() < GC)
                {
                    Debug.LogWarning("Smart Utility 2D: Slice was removed because it was too small");

                    return(null);
                }

                Polygon2D newPolygon = new Polygon2D(PreparePolygon.Get(polygon));

                if (newPolygon.pointsList.Count < 3)
                {
                    Debug.LogWarning("Smart Utility 2D: Mesh is too small for advanced triangulation, using simplified triangulations instead (size: " + polygon.GetArea() + ")");

                    result = PerformTriangulation(polygon, UVScale, UVOffset);

                    return(result);
                }

                foreach (Polygon2D hole in polygon.holesList)
                {
                    newPolygon.AddHole(new Polygon2D(PreparePolygon.Get(hole, -1)));
                }

                result = PerformTriangulation(newPolygon, UVScale, UVOffset);

                break;

            case Triangulation.Legacy:
                Slicer2D.Profiler.IncLegacyTriangulation();

                List <Vector2> list = new List <Vector2>();
                foreach (Vector2D p in polygon.pointsList)
                {
                    list.Add(p.ToVector2());
                }

                result = Triangulator.Create(list.ToArray(), UVScale, UVOffset);

                return(result);
            }

            return(result);
        }
Ejemplo n.º 26
0
    public static void CreateMeshExtrusion(List <List <Vector3> > polygons, float extrude, ref Mesh mesh)
    {
        var  capacity = polygons.Sum(p => (p.Count - 2) * 3 + p.Count) * 2;
        var  vertices = new List <Vector3>(capacity);
        var  indices  = new List <int>(capacity);
        bool inversed = extrude < 0f;

        foreach (var p in polygons)
        {
            var pv        = new List <Vector3>(p);
            var triangles = Triangulation.Triangulate(pv);
            var start     = vertices.Count;
            vertices.AddRange(triangles);
            if (!inversed)
            {
                for (int i = 0; i < triangles.Count; i++)
                {
                    indices.Add(i + start);
                }
            }
            else
            {
                for (int i = 0; i < triangles.Count / 3; i++)
                {
                    indices.Add(start + i * 3 + 0);
                    indices.Add(start + i * 3 + 2);
                    indices.Add(start + i * 3 + 1);
                }
            }
            var extrudeVector = Vector3.forward * extrude;
            var striangles    = triangles.Select(pt => pt + extrudeVector).ToList();
            start = vertices.Count;
            vertices.AddRange(striangles);
            if (inversed)
            {
                for (int i = 0; i < striangles.Count; i++)
                {
                    indices.Add(i + start);
                }
            }
            else
            {
                for (int i = 0; i < striangles.Count / 3; i++)
                {
                    indices.Add(start + i * 3 + 0);
                    indices.Add(start + i * 3 + 2);
                    indices.Add(start + i * 3 + 1);
                }
            }

            start = vertices.Count();

            if (inversed)
            {
                for (int i = 0; i < p.Count; i++)
                {
                    vertices.Add(p[i]);
                    vertices.Add(p[(i + 1) % p.Count]);
                    vertices.Add(p[i] + extrudeVector);

                    vertices.Add(p[(i + 1) % p.Count]);
                    vertices.Add(p[(i + 1) % p.Count] + extrudeVector);
                    vertices.Add(p[i] + extrudeVector);
                }
            }
            else
            {
                for (int i = 0; i < p.Count; i++)
                {
                    vertices.Add(p[i]);
                    vertices.Add(p[i] + extrudeVector);
                    vertices.Add(p[(i + 1) % p.Count]);

                    vertices.Add(p[(i + 1) % p.Count]);
                    vertices.Add(p[i] + extrudeVector);
                    vertices.Add(p[(i + 1) % p.Count] + extrudeVector);
                }
            }
            for (int i = 0; i < p.Count * 6; i++)
            {
                indices.Add(start + i);
            }
        }
        mesh.Clear();
        mesh.name = "extrusion";
        mesh.SetVertices(vertices);
        mesh.SetIndices(indices.ToArray(), MeshTopology.Triangles, 0);
        mesh.RecalculateBounds();
        mesh.RecalculateNormals();
        mesh.RecalculateTangents();
    }
Ejemplo n.º 27
0
        public static Mesh Triangulate3D(Polygon2D polygon, float z, Vector2 UVScale, Vector2 UVOffset, float UVRotation, Triangulation triangulation)
        {
            polygon.Normalize();

            Mesh result = null;

            switch (triangulation)
            {
            case Triangulation.Advanced:
                List <Vector2> uvs           = new List <Vector2>();
                List <Vector3> sideVertices  = new List <Vector3>();
                List <int>     sideTriangles = new List <int>();

                Vector3 pointA = new Vector3(0, 0, 0);
                Vector3 pointB = new Vector3(0, 0, 0);
                Vector3 pointC = new Vector3(0, 0, z);
                Vector3 pointD = new Vector3(0, 0, z);

                Vector2 uv0 = new Vector2();
                Vector2 uv1 = new Vector2();

                float a, b;

                int vCount = 0;

                Pair2D pair = new Pair2D(new Vector2D(polygon.pointsList.Last()), null);
                foreach (Vector2D p in polygon.pointsList)
                {
                    pair.B = p;

                    pointA.x = (float)pair.A.x;
                    pointA.y = (float)pair.A.y;
                    pointB.x = (float)pair.B.x;
                    pointB.y = (float)pair.B.y;
                    pointC.x = (float)pair.B.x;
                    pointC.y = (float)pair.B.y;
                    pointD.x = (float)pair.A.x;
                    pointD.y = (float)pair.A.y;

                    sideVertices.Add(pointA);
                    sideVertices.Add(pointB);
                    sideVertices.Add(pointC);
                    sideVertices.Add(pointD);

                    a = ((float)pair.A.x + 25f) / 50 + UVOffset.x / 2;
                    b = ((float)pair.B.x + 25f) / 50 + UVOffset.x / 2;

                    uv0.x = a;
                    uv0.y = a;
                    uv1.x = b;
                    uv1.y = b;

                    uvs.Add(new Vector2(0, 0));
                    uvs.Add(new Vector2(0, 1));
                    uvs.Add(new Vector2(1, 1));
                    uvs.Add(new Vector2(1, 0));

                    sideTriangles.Add(vCount + 2);
                    sideTriangles.Add(vCount + 1);
                    sideTriangles.Add(vCount + 0);

                    sideTriangles.Add(vCount + 0);
                    sideTriangles.Add(vCount + 3);
                    sideTriangles.Add(vCount + 2);

                    vCount += 4;

                    pair.A = pair.B;
                }

                Mesh mainMesh = new Mesh();
                mainMesh.subMeshCount = 2;

                Mesh surfaceMesh = PerformTriangulationAdvanced(polygon, UVScale, UVOffset, UVRotation);

                ///// UVS  /////
                foreach (Vector2 p in surfaceMesh.uv)
                {
                    uvs.Add(p);
                }
                foreach (Vector2 p in surfaceMesh.uv)
                {
                    uvs.Add(p);
                }

                surfaceMesh.triangles = surfaceMesh.triangles.Reverse().ToArray();

                ///// VERTICES ////

                List <Vector3> vertices = new List <Vector3>();
                foreach (Vector3 v in sideVertices)
                {
                    vertices.Add(v);
                }

                foreach (Vector3 v in surfaceMesh.vertices)
                {
                    Vector3 nV = v;
                    nV.z = z;
                    vertices.Add(nV);
                }

                foreach (Vector3 v in surfaceMesh.vertices)
                {
                    vertices.Add(v);
                }

                mainMesh.SetVertices(vertices);

                ///// TRIANGLES /////

                List <int> triangles = new List <int>();
                foreach (int p in sideTriangles)
                {
                    triangles.Add(p);
                }
                mainMesh.SetTriangles(triangles, 0);

                triangles.Clear();
                int count = sideVertices.Count();
                foreach (int p in surfaceMesh.triangles)
                {
                    triangles.Add(p + count);
                }

                int   trisCount = surfaceMesh.triangles.Count() / 3;
                int[] tris      = surfaceMesh.triangles;

                for (var i = 0; i < trisCount; i++)
                {
                    var tmp = tris[i * 3];
                    tris[i * 3]     = tris[i * 3 + 1];
                    tris[i * 3 + 1] = tmp;
                }

                count += surfaceMesh.vertices.Count();
                foreach (int p in tris)
                {
                    triangles.Add(p + count);
                }

                mainMesh.SetTriangles(triangles, 1);

                ///// LEFTOVERS /////

                mainMesh.uv = uvs.ToArray();
                mainMesh.RecalculateNormals();
                mainMesh.RecalculateBounds();

                result = mainMesh;

                break;
            }

            return(result);
        }
Ejemplo n.º 28
0
    void Start()
    {
        // create a new game object (as a child) and add required components
        GameObject go = new GameObject();

        go.transform.parent = this.transform;
        go.name             = "Cross";
        MeshFilter   mf = go.AddComponent <MeshFilter>();
        MeshCollider mc = go.AddComponent <MeshCollider>();
        MeshRenderer mr = go.AddComponent <MeshRenderer>();

        mr.material = new Material(Shader.Find("Standard"));

        // create collections of input vectors and indices representing the (hard-coded) cross and its hole
        List <Vector2>         points   = new List <Vector2>();
        List <List <Vector2> > holes    = new List <List <Vector2> >();
        List <int>             indices  = null;
        List <Vector3>         vertices = null;

        // manually hard-coded 2D vectors representing the cross
        points.Add(new Vector2(10, 0));
        points.Add(new Vector2(20, 0));
        points.Add(new Vector2(20, 10));
        points.Add(new Vector2(30, 10));
        points.Add(new Vector2(30, 20));
        points.Add(new Vector2(20, 20));
        points.Add(new Vector2(20, 30));
        points.Add(new Vector2(10, 30));
        points.Add(new Vector2(10, 20));
        points.Add(new Vector2(0, 20));
        points.Add(new Vector2(0, 10));
        points.Add(new Vector2(10, 10));

        // manually hard-coded 2D vectors representing the square-shaped hole inside the cross
        List <Vector2> hole = new List <Vector2>();

        hole.Add(new Vector2(12, 12));
        hole.Add(new Vector2(18, 12));
        hole.Add(new Vector2(18, 18));
        hole.Add(new Vector2(12, 18));
        holes.Add(hole);

        // perform TRIANGULATION
        Triangulation.triangulate(points, holes, 0.0f, out indices, out vertices);

        // create mesh instance and assign indices and vertices representing the (newly triangulated) mesh
        Mesh mesh = mf.mesh;

        mesh.Clear();
        mesh.vertices  = vertices.ToArray();
        mesh.triangles = indices.ToArray();
        mesh.RecalculateNormals();

        // reset mesh collider after (re-)creation
        go.GetComponent <MeshCollider>().sharedMesh = mesh;

        // generate simple UV map
        Vector2[] uvs = new Vector2[mesh.vertices.Length];
        for (int i = 0; i < uvs.Length; i++)
        {
            uvs[i] = new Vector2(mesh.vertices[i].x, mesh.vertices[i].y);
        }
        mesh.uv = uvs;
    }
Ejemplo n.º 29
0
        public ModelRoot AddTerrainMesh(ModelRoot model, HeightMap heightMap, PBRTexture textures)
        {
            Triangulation triangulation = _meshService.TriangulateHeightMap(heightMap);

            return(AddTerrainMesh(model, triangulation, textures));
        }