Example #1
0
        public void BuildClusters(List <GameObject> gos, ProgressUpdateCancelableDelegate progFunc)
        {
            if (gos.Count == 0)
            {
                Debug.LogWarning("No objects to cluster. Add some objects to the list of Objects To Combine.");
                return;
            }
            if (cluster == null)
            {
                cluster = new MB3_AgglomerativeClustering();
            }
            List <MB3_AgglomerativeClustering.item_s> its = new List <MB3_AgglomerativeClustering.item_s>();

            for (int i = 0; i < gos.Count; i++)
            {
                if (gos[i] != null && its.Find(x => x.go == gos[i]) == null)
                {
                    Renderer mr = gos[i].GetComponent <Renderer>();
                    if (mr != null && (mr is MeshRenderer || mr is SkinnedMeshRenderer))
                    {
                        MB3_AgglomerativeClustering.item_s ii = new MB3_AgglomerativeClustering.item_s();
                        ii.go    = gos[i];
                        ii.coord = mr.bounds.center;
                        its.Add(ii);
                    }
                }
            }
            cluster.items = its;
            //yield return cluster.agglomerate();
            cluster.agglomerate(progFunc);
            if (!cluster.wasCanceled)
            {
                float smallest, largest;
                _BuildListOfClustersToDraw(progFunc, out smallest, out largest);
                d.maxDistBetweenClusters = Mathf.Lerp(smallest, largest, .9f);
            }
        }
Example #2
0
        void _BuildListOfClustersToDraw(ProgressUpdateCancelableDelegate progFunc, out float smallest, out float largest)
        {
            _clustersToDraw.Clear();
            if (cluster.clusters == null)
            {
                smallest = 1f;
                largest  = 10f;
                return;
            }
            if (progFunc != null)
            {
                progFunc("Building Clusters To Draw A:", 0);
            }
            List <MB3_AgglomerativeClustering.ClusterNode> removeMe = new List <MB3_AgglomerativeClustering.ClusterNode>();

            largest  = 1f;
            smallest = 10e6f;
            for (int i = 0; i < cluster.clusters.Length; i++)
            {
                MB3_AgglomerativeClustering.ClusterNode node = cluster.clusters[i];
                //don't draw clusters that were merged too far apart and only want leaf nodes
                if (node.distToMergedCentroid <= d.maxDistBetweenClusters /*&& node.leaf == null*/)
                {
                    if (d.includeCellsWithOnlyOneRenderer)
                    {
                        _clustersToDraw.Add(node);
                    }
                    else if (node.leaf == null)
                    {
                        _clustersToDraw.Add(node);
                    }
                }
                if (node.distToMergedCentroid > largest)
                {
                    largest = node.distToMergedCentroid;
                }
                if (node.height > 0 && node.distToMergedCentroid < smallest)
                {
                    smallest = node.distToMergedCentroid;
                }
            }
            if (progFunc != null)
            {
                progFunc("Building Clusters To Draw B:", 0);
            }
            for (int i = 0; i < _clustersToDraw.Count; i++)
            {
                removeMe.Add(_clustersToDraw[i].cha);
                removeMe.Add(_clustersToDraw[i].chb);
            }

            for (int i = 0; i < removeMe.Count; i++)
            {
                _clustersToDraw.Remove(removeMe[i]);
            }
            _radii = new float[_clustersToDraw.Count];
            if (progFunc != null)
            {
                progFunc("Building Clusters To Draw C:", 0);
            }
            for (int i = 0; i < _radii.Length; i++)
            {
                MB3_AgglomerativeClustering.ClusterNode n = _clustersToDraw[i];
                Bounds b = new Bounds(n.centroid, Vector3.one);
                for (int j = 0; j < n.leafs.Length; j++)
                {
                    Renderer r = cluster.clusters[n.leafs[j]].leaf.go.GetComponent <Renderer>();
                    if (r != null)
                    {
                        b.Encapsulate(r.bounds);
                    }
                }
                _radii[i] = b.extents.magnitude;
            }
            if (progFunc != null)
            {
                progFunc("Building Clusters To Draw D:", 0);
            }
            _ObjsExtents            = largest + 1f;
            _minDistBetweenClusters = Mathf.Lerp(smallest, 0f, .9f);

            if (_ObjsExtents < 2f)
            {
                _ObjsExtents = 2f;
            }
        }
        public bool agglomerate(ProgressUpdateCancelableDelegate progFunc)
        {
            wasCanceled = true;
            if (progFunc != null)
            {
                wasCanceled = progFunc("Filling Priority Queue:", 0);
            }
            if (items.Count <= 1)
            {
                clusters = new ClusterNode[0];
                return(false);
                //yield break;
            }
            clusters = new ClusterNode[items.Count * 2 - 1];
            for (int i = 0; i < items.Count; i++)
            {
                clusters[i] = new ClusterNode(items[i], i);
            }

            int numClussters = items.Count;
            List <ClusterNode> unclustered = new List <ClusterNode>();

            for (int i = 0; i < numClussters; i++)
            {
                clusters[i].isUnclustered = true;
                unclustered.Add(clusters[i]);
            }

            int height = 0;

            System.Diagnostics.Stopwatch timer = new System.Diagnostics.Stopwatch();
            timer.Start();

            float largestDistInQ = 0;
            long  usedMemory     = GC.GetTotalMemory(false) / 1000000;
            PriorityQueue <float, ClusterDistance> pq = new PriorityQueue <float, ClusterDistance>();
            //largestDistInQ = _RefillPriorityQWithSome(pq, unclustered, clusters /*,null,null*/);
            int numRefills = 0;

            while (unclustered.Count > 1)
            {
                int numToFindClosetPair = 0;
                height++;
                //find closest pair

                if (pq.Count == 0)
                {
                    numRefills++;
                    usedMemory = GC.GetTotalMemory(false) / 1000000;
                    if (progFunc != null)
                    {
                        wasCanceled = progFunc("Refilling Q:" + ((float)(items.Count - unclustered.Count) * 100) / items.Count + " unclustered:" + unclustered.Count + " inQ:" + pq.Count + " usedMem:" + usedMemory,
                                               ((float)(items.Count - unclustered.Count)) / items.Count);
                    }
                    largestDistInQ = _RefillPriorityQWithSome(pq, unclustered, clusters, progFunc);
                    if (pq.Count == 0)
                    {
                        break;
                    }
                }
                KeyValuePair <float, ClusterDistance> closestPair = pq.Dequeue();
                // should only consider unclustered pairs. It is more effecient to discard nodes that have already been clustered as they are popped off the Q
                // than to try to remove them from the Q when they have been clustered.
                while (!closestPair.Value.a.isUnclustered || !closestPair.Value.b.isUnclustered)
                {
                    if (pq.Count == 0)
                    {
                        numRefills++;
                        usedMemory = GC.GetTotalMemory(false) / 1000000;
                        if (progFunc != null)
                        {
                            wasCanceled = progFunc("Creating clusters:" + ((float)(items.Count - unclustered.Count) * 100) / items.Count + " unclustered:" + unclustered.Count + " inQ:" + pq.Count + " usedMem:" + usedMemory,
                                                   ((float)(items.Count - unclustered.Count)) / items.Count);
                        }
                        largestDistInQ = _RefillPriorityQWithSome(pq, unclustered, clusters, progFunc);
                        if (pq.Count == 0)
                        {
                            break;
                        }
                    }
                    closestPair = pq.Dequeue();
                    numToFindClosetPair++;
                }

                //make a new cluster with pair as children set merge height
                numClussters++;
                ClusterNode cn = new ClusterNode(closestPair.Value.a, closestPair.Value.b, numClussters - 1, height, closestPair.Key, clusters);
                //remove children from unclustered
                unclustered.Remove(closestPair.Value.a);
                unclustered.Remove(closestPair.Value.b);


                //We NEED TO REMOVE ALL DISTANCE PAIRS THAT INVOLVE A AND B FROM PRIORITY Q. However searching for all these pairs and removing is very slow.
                // Instead we will leave them in the Queue and flag the clusters as isUnclustered = false and discard them as they are popped from the Q which is O(1) operation.
                closestPair.Value.a.isUnclustered = false;
                closestPair.Value.b.isUnclustered = false;

                //add new cluster to unclustered
                int newIdx = numClussters - 1;
                if (newIdx == clusters.Length)
                {
                    Debug.LogError("how did this happen");
                }
                clusters[newIdx] = cn;
                unclustered.Add(cn);
                cn.isUnclustered = true;
                //update new clusteres distance
                for (int i = 0; i < unclustered.Count - 1; i++)
                {
                    float dist = euclidean_distance(cn.centroid, unclustered[i].centroid);
                    if (dist < largestDistInQ) //avoid cluttering Qwith
                    {
                        pq.Add(new KeyValuePair <float, ClusterDistance>(dist, new ClusterDistance(cn, unclustered[i])));
                    }
                }
                //if (timer.Interval > .2f)
                //{
                //    yield return null;
                //    timer.Start();
                //}
                if (wasCanceled)
                {
                    break;
                }
                usedMemory = GC.GetTotalMemory(false) / 1000000;
                if (progFunc != null)
                {
                    wasCanceled = progFunc("Creating clusters:" + ((float)(items.Count - unclustered.Count) * 100) / items.Count + " unclustered:" + unclustered.Count + " inQ:" + pq.Count + " usedMem:" + usedMemory,
                                           ((float)(items.Count - unclustered.Count)) / items.Count);
                }
            }
            if (progFunc != null)
            {
                wasCanceled = progFunc("Finished clustering:", 100);
            }
            //Debug.Log("Time " + timer.Elapsed);
            if (wasCanceled)
            {
                return(false);
            }
            else
            {
                return(true);
            }
        }
        float _RefillPriorityQWithSome(PriorityQueue <float, ClusterDistance> pq, List <ClusterNode> unclustered, ClusterNode[] clusters, ProgressUpdateCancelableDelegate progFunc)
        {
            //find nthSmallest point of distances between pairs
            List <float> allDist = new List <float>(2048);

            for (int i = 0; i < unclustered.Count; i++)
            {
                for (int j = i + 1; j < unclustered.Count; j++)
                {
                    // if (unclustered[i] == omitA || unclustered[i] == omitB ||
                    //     unclustered[j] == omitA || unclustered[j] == omitB)
                    // {

                    // } else
                    // {

                    allDist.Add(euclidean_distance(unclustered[i].centroid, unclustered[j].centroid));
                    // }
                }
                wasCanceled = progFunc("Refilling Queue Part A:", i / (unclustered.Count * 2f));
                if (wasCanceled)
                {
                    return(10f);
                }
            }

            if (allDist.Count == 0)
            {
                return(10e10f);
            }
            float nthSmallest = NthSmallestElement(allDist, MAX_PRIORITY_Q_SIZE);

            //load up Q with up to nthSmallest distance pairs
            for (int i = 0; i < unclustered.Count; i++)
            {
                for (int j = i + 1; j < unclustered.Count; j++)
                {
                    int   idxa    = unclustered[i].idx;
                    int   idxb    = unclustered[j].idx;
                    float newDist = euclidean_distance(unclustered[i].centroid, unclustered[j].centroid);
                    if (newDist <= nthSmallest)
                    {
                        pq.Add(new KeyValuePair <float, ClusterDistance>(newDist, new ClusterDistance(clusters[idxa], clusters[idxb])));
                    }
                }
                wasCanceled = progFunc("Refilling Queue Part B:", (unclustered.Count + i) / (unclustered.Count * 2f));
                if (wasCanceled)
                {
                    return(10f);
                }
            }
            return(nthSmallest);
        }