// not currently being used due to crashes
    private void ClusterSplit()
    {
        // --- Split the cluster ---

        /* Theres an issue with cluster lengths > 50 and crashing
         * clusters are graphically split, but spatially still the same.
         */
        if (cluster_size > 50)
        {
            // calculate amount of clusters needed to split
            float num             = cluster_size / 50;
            int   cluster_rounded = Convert.ToInt32(num);
            // for each cluster needed create and add node length
            for (int i = 0; i < cluster_rounded; ++i)
            {
                // Create new cluster object
                GameObject cluster = new GameObject();
                // setup meshfilters for child objects
                MeshFilter[] meshFilters = new MeshFilter[50];
                // combine instance mesh array
                CombineInstance[] combine = new CombineInstance[meshFilters.Length];
                // Array for new cluster
                ArrayList new_nodes = new ArrayList();
                for (int k = 50 * i; k < k + 50; ++k)
                {
                    if (k < nodes.Count)
                    {
                        new_nodes.Add(nodes[k]);
                    }
                }
                // set the parent of the cluster to visualization object
                cluster.transform.parent = this.transform;
                // set the name of the cluster
                cluster.transform.name = transform.name + " cluster " + i;
                // add meshfilter to new cluster
                cluster.AddComponent <MeshFilter>();
                cluster.AddComponent <MeshRenderer>();
                cluster.GetComponent <MeshFilter>().mesh = new Mesh();
                // combine all the meshes in the mesh array "combine"
                cluster.GetComponent <MeshFilter>().mesh.CombineMeshes(combine);
                cluster.GetComponent <MeshRenderer>().material = GetComponent <MeshRenderer>().material;
                // assign random color to cluster
                cluster.GetComponent <MeshRenderer>().material.color = UnityEngine.Random.ColorHSV(0, 1f, 0.5f, 1f, 0.5f, 1f);
                // add clustercode
                cluster.AddComponent <ClusterBehaviour>();
                ClusterBehaviour c = cluster.GetComponent <ClusterBehaviour>();
                c.setArrayList(new_nodes);
                // lastly, add collider
                cluster.AddComponent <MeshCollider>();
            }
            // delete gameobject
        }
    }
    private void ShowLaser(RaycastHit hit)
    {
        //laserTransform.position = Vector3.Lerp(trackedObj.transform.position, hitPoint, .5f);


        //laserTransform.position = trackedObj.transform.position;
        //laserTransform.LookAt(hitPoint);
        //laserTransform.localScale = new Vector3(laserTransform.localScale.x, laserTransform.localScale.y,
        //  laserDist);
        if (hit.transform.name != "Bounds")
        {
            if (hit.transform.tag == "Node" || hit.transform.tag == "StudyNode")
            {
                if (Controller.GetPress(SteamVR_Controller.ButtonMask.Grip) && studyState == 0 || studyState == 2 && Controller.GetPress(SteamVR_Controller.ButtonMask.Trigger))
                {
                    Camera_System camera_sys       = Camera.main.GetComponent <Camera_System>();
                    Vector3       teleportPosition = hit.point;
                    camera_sys.FadeOutIn(teleportPosition);
                }
                if (lasered_node == null && laser_t <= 0 && userStudyRunning == false)
                {
                    lasered_node = hit.transform.GetComponent <VisualNode>();
                    lasered_node.OnLaserOver();
                    if (laser_t <= 0)
                    {
                        laser_t = 1f;
                    }
                }
                else
                {
                    if (lasered_node != null)
                    {
                        if (hit.transform == lasered_node.transform && laser_t < 0.5f && laser_t > 0.25f && lasered_node != null)
                        {
                            lasered_node.OnLaserExit();
                            lasered_node = null;
                        }
                        else
                        {
                            lasered_node.OnLaserExit();
                            lasered_node = hit.transform.GetComponent <VisualNode>();
                            lasered_node.OnLaserOver();
                            if (laser_t <= 0)
                            {
                                laser_t = 1f;
                            }
                        }
                    }
                }
            }

            if (Controller.GetPressDown(SteamVR_Controller.ButtonMask.Trigger))
            {
                hitTransform = hit.transform;
                if (hitTransform.tag != "cluster")
                {
                    // controller move right
                    if (velocity.x > 0.1f)
                    {
                        //hitTransform.position += new Vector3(0.1f, 0, 0);
                    }
                    // controller move left
                    if (velocity.x < -0.1f)
                    {
                        //hitTransform.position += new Vector3(-0.1f, 0, 0);
                    }
                }
                if (hitTransform.tag == "cluster" && userStudyRunning == false)
                {
                    if (c == null)
                    {
                        c = hit.transform.GetComponent <ClusterBehaviour>();
                        c.Highlight();
                    }
                    else
                    {
                        c.ExitHighlight();
                        print("exit_highlight");
                        c = null;
                        c = hit.transform.GetComponent <ClusterBehaviour>();
                        c.Highlight();
                    }
                }
            }
            else
            {
            }
        }
        else
        {
        }
    }
    void KMeansClustering()
    {
        print("k-means started..");

        // calculate centroid points
        int k_means = k_clusters;

        int[] k_calculations = new int[k_means];
        for (int i = 0; i < k_means; i++)
        {
            decimal f        = Decimal.Divide(i, k_means);
            float   division = (float)f;
            k_calculations[i] = Mathf.RoundToInt(colombs_nodes.Count * (division));
            print("k means calculation: " + f + " = " + i + " / " + k_means);
        }
        print("step 0, k_means = " + k_means);
        int k_num = 0;

        foreach (int num in k_calculations)
        {
            //print(k_num +" : "+num);
            k_num++;
        }

        // Assigning centroids
        GameObject[] centroids    = new GameObject[k_means];
        VisualNode[] vs_c         = new VisualNode[k_means];
        ArrayList[]  clusternodes = new ArrayList[k_means];
        for (int i = 0; i < k_means; i++)
        {
            centroids[i]          = (GameObject)colombs_nodes[k_calculations[i]];
            vs_c[i]               = centroids[i].GetComponent <VisualNode>();
            vs_c[i].distinct_node = true;
            Vector3 localScale = vs_c[i].transform.localScale;
            if (transform.localScale.x < 2)
            {
                //vs_c[i].transform.localScale = new Vector3(localScale.x * 2, localScale.y * 2, localScale.z * 2);
            }
            clusternodes[i] = new ArrayList();
            clusternodes[i].Add(vs_c[i]);
        }
        print("step 1");

        // Assigning nodes to centroids on Euclidean Distance
        foreach (GameObject g in colombs_nodes)
        {
            if (centroids.Contains(g) == false)
            {
                VisualNode n          = g.GetComponent <VisualNode>();
                float[]    dist_array = new float[k_means];
                for (int i = 0; i < dist_array.Length; i++)
                {
                    dist_array[i] = Vector3.Distance(g.transform.localPosition, centroids[i].transform.localPosition);
                }
                float smallest_dist  = dist_array.Min();
                int   smallest_index = 0;
                for (int i = 0; i < dist_array.Length; i++)
                {
                    if (dist_array[i] == smallest_dist)
                    {
                        smallest_index = i;
                    }
                }
                clusternodes[smallest_index].Add(n);
            }
        }
        print("step 2");

        // Create Cluster Objects
        GameObject[] clusterObjects = new GameObject[k_means];
        for (int i = 0; i < k_means; i++)
        {
            clusterObjects[i] = new GameObject();
            clusterObjects[i].AddComponent <ClusterBehaviour>();
            clusterObjects[i].GetComponent <ClusterBehaviour>().setArrayList(clusternodes[i]);
            clusterObjects[i].GetComponent <ClusterBehaviour>().setClusterCentre(centroids[i]);
        }
        print("step 3");

        int cluster_index = 0;

        // Go through the three centroids and generate the clusters
        foreach (GameObject cluster_object in clusterObjects)
        {
            ClusterBehaviour clu = cluster_object.GetComponent <ClusterBehaviour>();
            // return nodes of cluster c
            ArrayList cluster_array = clu.returnClusterNodes();
            // setup meshfilters for child objects
            MeshFilter[] meshFilters = new MeshFilter[clu.returnClusterNodes().Count + 1];
            // combine instance mesh array
            CombineInstance[] combine = new CombineInstance[meshFilters.Length];
            // Color of cluster
            Color col = UnityEngine.Random.ColorHSV(0.1f, 0.8f, 0.7f, 1f, 0.5f, 1f);


            // go through each child node and add to meshFilter array
            // first cluster node
            GameObject first_node = clu.gameObject;
            meshFilters[0]       = centroids[cluster_index].GetComponent <MeshFilter>();
            combine[0].mesh      = meshFilters[0].sharedMesh;
            combine[0].transform = meshFilters[0].transform.localToWorldMatrix;
            meshFilters[0].gameObject.SetActive(false);
            nodes_already_clustered.Add(first_node.GetComponent <VisualNode>());
            // add boxcollider for each node
            BoxCollider b = cluster_object.AddComponent <BoxCollider>();
            b.size      = new Vector3(0.05f, 0.05f, 0.05f);
            b.center    = clu.transform.position;
            b.isTrigger = true;

            /*
             * ArrayList edge = n.getAllocatedEdges();
             * if (edge.Count > 0) {
             *  GameObject index0 = edge[0] as GameObject;
             *  foreach (GameObject ga in edge) {
             *      LineRenderer l = ga.GetComponent<LineRenderer>();
             *      l.material.SetColor("_TintColor", new Color(col.r, col.g, col.b, 0.01f));
             *  }
             * }
             */

            int k = 1;
            foreach (VisualNode v in clu.returnClusterNodes())
            {
                if (nodes_already_clustered.Contains(v) == false)
                {
                    GameObject g = v.gameObject;

                    meshFilters[k]       = g.GetComponent <MeshFilter>();
                    combine[k].mesh      = meshFilters[k].sharedMesh;
                    combine[k].transform = meshFilters[k].transform.localToWorldMatrix;
                    meshFilters[k].gameObject.SetActive(false);
                    //nodes.Add(v);
                    nodes_already_clustered.Add(v);
                    // add boxcollider for each node
                    BoxCollider b1 = cluster_object.AddComponent <BoxCollider>();
                    b1.size      = new Vector3(0.05f, 0.05f, 0.05f);
                    b1.center    = g.transform.position;
                    b1.isTrigger = true;
                    ArrayList edge1 = v.getAllocatedEdges();
                    v.transform.parent = cluster_object.transform;
                    vs_c[cluster_index].addClusterNode(v);

                    if (edge1.Count > 0)
                    {
                        GameObject index0 = edge1[0] as GameObject;

                        foreach (GameObject ga in edge1)
                        {
                            //LineRenderer l = ga.GetComponent<LineRenderer>();
                            //l.material.SetColor("_TintColor", new Color(col.r, col.g, col.b, 0.01f));
                        }
                    }
                }
                else
                {
                }
                k++;
            }

            // set the parent of the cluster to visualization object
            cluster_object.transform.parent = this.transform;
            // set the name of the cluster
            //cluster_object.transform.name = first_node.transform.name + " cluster";

            // add meshfilter to new cluster
            cluster_object.AddComponent <MeshFilter>();
            cluster_object.AddComponent <MeshRenderer>();
            cluster_object.GetComponent <MeshFilter>().mesh = new Mesh();
            // combine all the meshes in the mesh array "combine"
            cluster_object.GetComponent <MeshFilter>().mesh.CombineMeshes(combine);
            cluster_object.GetComponent <MeshRenderer>().material = nodeMat;
            // assign random color to cluster
            cluster_object.GetComponent <MeshRenderer>().material.color = col;
            clusters.Add(clu);
            cluster_index++;
        }
        EdgeManager edgeManager = Camera.main.GetComponent <EdgeManager> ();
        ArrayList   glEdges     = edgeManager.getEdges();

        print(glEdges.Count + "glEdge count");
        for (int i = 0; i < glEdges.Count; i++)
        {
            Edge e = (Edge)glEdges[i];
            e.setColor(e.getTransformRef().parent.GetComponent <MeshRenderer>().material.color);
        }
        print("step 4 DONE");
        clusterAllNodes         = true;
        graph_layout_recognized = false;
    }
    //Optimisation Test
    void CombineMeshes()
    {
        // go through each clusterNode parent
        foreach (VisualNode n in clusterNodes)
        {
            // arraylist to be added to cluster late
            ArrayList nodes = new ArrayList();
            // setup meshfilters for child objects
            MeshFilter[] meshFilters = new MeshFilter[n.getClusterNodes().Count + 1];
            // combine instance mesh array
            CombineInstance[] combine = new CombineInstance[meshFilters.Length];
            //stop center from being combined
            nodes_already_clustered.Add(n);
            // Create a new cluster gameobject
            GameObject newCluster = new GameObject();

            // Color of cluster
            Color col = UnityEngine.Random.ColorHSV(0.1f, 0.8f, 0.7f, 1f, 0.5f, 1f);


            // go through each child node and add to meshFilter array

            // first cluster node
            GameObject c1 = n.gameObject;
            meshFilters[0]       = c1.GetComponent <MeshFilter>();
            combine[0].mesh      = meshFilters[0].sharedMesh;
            combine[0].transform = meshFilters[0].transform.localToWorldMatrix;
            meshFilters[0].gameObject.SetActive(false);
            nodes.Add(n);
            nodes_already_clustered.Add(n);
            // add boxcollider for each node
            //BoxCollider b = newCluster.AddComponent<BoxCollider>();
            //b.size = new Vector3(0.05f, 0.05f, 0.05f);
            //b.center = c1.transform.position;
            //b.isTrigger = true;
            ArrayList edge = n.getAllocatedEdges();
            if (edge.Count > 0)
            {
                GameObject index0 = edge[0] as GameObject;
                foreach (GameObject ga in edge)
                {
                    LineRenderer l = ga.GetComponent <LineRenderer>();
                    l.material.SetColor("_TintColor", new Color(col.r, col.g, col.b, 0.01f));
                }
            }

            int k = 1;

            foreach (VisualNode v in n.getClusterNodes())
            {
                if (nodes_already_clustered.Contains(v) == false)
                {
                    GameObject g = v.gameObject;
                    meshFilters[k]       = g.GetComponent <MeshFilter>();
                    combine[k].mesh      = meshFilters[k].sharedMesh;
                    combine[k].transform = meshFilters[k].transform.localToWorldMatrix;
                    meshFilters[k].gameObject.SetActive(false);
                    nodes.Add(v);
                    nodes_already_clustered.Add(v);
                    // add boxcollider for each node
                    //BoxCollider b1 = newCluster.AddComponent<BoxCollider>();
                    //b1.size = new Vector3(0.05f, 0.05f, 0.05f);
                    //b1.center = g.transform.position;
                    //b1.isTrigger = true;
                    v.transform.parent = n.transform;
                    ArrayList edge1 = v.getAllocatedEdges();
                    if (edge1.Count > 0)
                    {
                        GameObject index0 = edge1[0] as GameObject;
                        foreach (GameObject ga in edge1)
                        {
                            LineRenderer l = ga.GetComponent <LineRenderer>();
                            l.material.SetColor("_TintColor", new Color(col.r, col.g, col.b, 0.01f));
                        }
                    }
                }
                else
                {
                }
                k++;
            }

            // set the parent of the cluster to visualization object
            newCluster.transform.parent = this.transform;
            // set the name of the cluster
            newCluster.transform.name = n.transform.name + " cluster";

            // add meshfilter to new cluster
            newCluster.AddComponent <MeshFilter>();
            newCluster.AddComponent <MeshRenderer>();
            newCluster.GetComponent <MeshFilter>().mesh = new Mesh();
            // combine all the meshes in the mesh array "combine"
            newCluster.GetComponent <MeshFilter>().mesh.CombineMeshes(combine);
            newCluster.GetComponent <MeshRenderer>().material = nodeMat;
            // assign random color to cluster
            newCluster.GetComponent <MeshRenderer>().material.color = col;
            // add clustercode
            newCluster.AddComponent <ClusterBehaviour>();
            ClusterBehaviour c = newCluster.GetComponent <ClusterBehaviour>();
            n.transform.parent = c.transform;
            c.setArrayList(nodes);
            c.setClusterCentre(n.gameObject);


            // lastly, add collider (trying box colliders)
            //newCluster.AddComponent<MeshCollider>();
        }
        EdgeManager edgeManager = Camera.main.GetComponent <EdgeManager> ();
        ArrayList   glEdges     = edgeManager.getEdges();

//		print (glEdges.Count + "glEdge count");
        for (int i = 0; i < glEdges.Count; i++)
        {
            Edge e = (Edge)glEdges[i];
            e.setColor(e.getTransformRef().parent.parent.GetComponent <MeshRenderer>().material.color);
        }

        clusterAllNodes         = true;
        graph_layout_recognized = false;

        Camera.main.cullingMask = cullingmask;
    }
    // on interacting with button
    void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.tag == "Controller")
        {
            if (d.getVisualNode().hiding() == true)
            {
                off = 1;
                gameObject.GetComponent <Image>().color = Color.black;
                print("on");
            }
            if (d.getVisualNode().hiding() == false)
            {
                off = 0;
                gameObject.GetComponent <Image>().color = Color.white;
                print("off");
            }
            // unhighlight previous node
            if (visualnode != null && d.getVisualNode() != visualnode)
            {
                visualnode.transform.parent.GetComponent <ClusterBehaviour>().ExitHighlight();
            }


            // set UI to white

            // Get cluster nodes and cluster class

            ClusterBehaviour c            = d.getVisualNode().transform.parent.GetComponent <ClusterBehaviour>();
            ArrayList        clusterNodes = c.returnClusterNodes();
            // if filtering cluster nodes
            if (off == 0)
            {
                d.getVisualNode().setHiding(true);
                foreach (VisualNode v in clusterNodes)
                {
                    if (v != d.getVisualNode())
                    {
                        graph.addIgnoreNode(v);
                    }
                }
                foreach (VisualNode v in clusterNodes)
                {
                    if (v != d.getVisualNode())
                    {
                        v.OnHideEdges();
                    }
                }
            }
            // if unfiltering cluster nodes
            if (off == 1)
            {
                d.getVisualNode().setHiding(false);
                foreach (VisualNode v in clusterNodes)
                {
                    if (v != d.getVisualNode())
                    {
                        graph.removeIgnoreNode(v);
                    }
                }
                foreach (VisualNode v in clusterNodes)
                {
                    if (v != d.getVisualNode())
                    {
                        v.UnHideEdges();
                    }
                }
            }
            off++;
            // reset back to 0 if greater than 1
            if (off > 1)
            {
                off = 0;
            }
            c.RecreateCluster();
            visualnode = d.getVisualNode();
        }
    }