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); } }
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); }