/// <summary> /// A class that generates colors for clusters. /// </summary> /// <param name="cs">A clustering.</param> /// <param name="degreeStart">The lowest allowable hue, in degrees.</param> /// <param name="degreeEnd">The highest allowable hue, in degrees.</param> /// <param name="offset">Shift, in degrees. E.g., if degreeStart = 0 and /// degreeEnd = 360 and offset = 45, the effective degreeStart is 45 mod 360 and /// the effective degreeEnd is 405 mod 360.</param> public ClusterColorer(Clustering cs, double degreeStart, double degreeEnd, double offset) { // sort clusters so that repainting on subsequent // runs produces a stable coloring var cSorted = cs.OrderBy(c => c.OrderBy(a => new Tuple <int, int>(a.X, a.Y)).ToArray()[0]).ToArray(); // init address-to-cluster lookup // address-to-cluster lookup var cdict = new Dictionary <AST.Address, HashSet <AST.Address> >(); foreach (Cluster c in cSorted) { foreach (AST.Address addr in c) { cdict.Add(addr, c); } } // init cluster neighbor map var cNeighbors = new Dictionary <HashSet <AST.Address>, HashSet <HashSet <AST.Address> > >(); foreach (Cluster c in cSorted) { cNeighbors.Add(c, new HashSet <Cluster>()); var neighbors = AdjacentCells(c); foreach (Cluster c2 in cSorted) { // append if c is adjacent to c2 if (neighbors.Intersect(c2).Count() > 0) { cNeighbors[c].Add(c2); } } } // rank clusters by their degree Cluster[] csSorted2 = cSorted.OrderByDescending(c => cNeighbors[c].Count).ToArray(); // greedily assign colors by degree, largest first; // aka Welsh-Powell heuristic // init angle generator var angles = new AngleGenerator(degreeStart, degreeEnd); foreach (Cluster c in csSorted2) { // get neighbor colors var ns = cNeighbors[c].ToArray(); var nscs = new HashSet <Color>(); foreach (Cluster n in ns) { if (assignedColors.ContainsKey(n)) { nscs.Add(assignedColors[n]); } } // color getter Func <Color> colorf = () => HSLtoColor( new HSL( mod( angles.NextAngle() + offset, degreeEnd - degreeStart ), SATURATION, LUMINOSITY ) ); // get initial color var color = colorf(); while (nscs.Contains(color)) { // get next color color = colorf(); } // save color assignedColors[c] = color; } }
/// <summary> /// A class that generates colors for clusters. /// </summary> /// <param name="clustering">A clustering.</param> /// <param name="degreeStart">The lowest allowable hue, in degrees.</param> /// <param name="degreeEnd">The highest allowable hue, in degrees.</param> /// <param name="offset">Shift, in degrees. E.g., if degreeStart = 0 and /// degreeEnd = 360 and offset = 45, the effective degreeStart is 45 mod 360 and /// the effective degreeEnd is 405 mod 360.</param> /// <param name="ih">An InvertedHistogram so that coloring is fingerprint-sensitive.</param> public ClusterColorer(Clustering clustering, double degreeStart, double degreeEnd, double offset, ROInvertedHistogram ih, Graph g) { // merge clusters by fingerprint var x = MergeClustersByFingerprint(clustering, ih); var cs = x.Item1; var ccdict = x.Item2; // get neighbors var neighbors = Neighbors(cs); // rank clusters by their degree and break ties using fingerprint (if given fingerprints) // also sort clusters so that repainting on subsequent // runs produces a stable coloring //Cluster[] csSorted = // cs.OrderByDescending(c => neighbors[c].Count) // .ThenBy(c => ih[c.First()].Item3.ToString()).ToArray(); // size of biggest cluster var maxsz = cs.Select(c => c.Count).Max(); var csSorted = cs.OrderBy(hs => { var first = hs.First(); if (!g.isFormula(first)) { return(0); } else { return(maxsz - hs.Count); } }).ThenBy(c => ih[c.First()].Item3.ToString()).ToArray(); // greedily assign colors by degree, largest first; // aka Welsh-Powell heuristic // init angle generator var angles = new AngleGenerator(degreeStart, degreeEnd); // color getter Func <Color> colorf = () => HSLtoColor( new HSL( mod( angles.NextAngle() + offset, degreeEnd - degreeStart ), SATURATION, LUMINOSITY ) ); foreach (Cluster c in csSorted) { // get neighbor colors var ns = neighbors[c].ToArray(); var nscs = new HashSet <Color>(); foreach (Cluster n in ns) { if (assignedColors.ContainsKey(n)) { nscs.Add(assignedColors[n]); } } // get initial color var color = colorf(); while (nscs.Contains(color)) { // if we've already chosen this one, get next color color = colorf(); } // save this color for each _original_ cluster in c foreach (Cluster corig in ccdict[c]) { assignedColors[corig] = color; } } }