public List <IDisplayAxis> GeneratePossibleConfigurations(float minTextSize, float maxTextSize, List <float> stepSequence) { // first convert the step sequence to a label sequence List <string> labels = new List <string>(); for (int i = 0; i < stepSequence.Count; i++) { T value = DataConverter.FloatToValue(stepSequence[i]); string formattedValue = DataConverter.ValueToString(value); if (!labels.Contains(formattedValue)) // avoid duplicates { labels.Add(formattedValue); } } // now create all possible DisplayAxes from this List <IDisplayAxis> possibilities = new List <IDisplayAxis>(); for (float fontSize = minTextSize; fontSize <= maxTextSize; fontSize += 0.1f) { for (int i = 0; i < 2; i++) { bool horizontalAlignment = (i == 0); DisplayAxis displayAxis = new DisplayAxis(this, labels, fontSize, horizontalAlignment, Horizontal); possibilities.Add(displayAxis); } } return(possibilities); }
/// <summary> /// Performs the Extended Wilkinson algorithm which returns an optimal labeling for numeric numbers based on the given settings /// </summary> /// <param name="availableSpace">States how much space in world units there is available for the axis</param> /// <param name="horizontalAxis">If set to true, the axis is orientated horizontally</param> /// <param name="targetDensity">Specifies how many labels should be placed per world unit</param> /// <param name="dataMin">The minimum of the data range</param> /// <param name="dataMax">The maximum of the dtaa range</param> /// <param name="axisMin">Gives the chosen minimum of the axis labeling</param> /// <param name="axisMax">Gives the chosen maximum of the axis labeling</param> /// <returns>An axis configuration which includes styling and the chosen labels</returns> public static IDisplayAxis PerformExtendedWilkinson(IAxis axis, float availableSpace, float targetDensity, out float axisMin, out float axisMax) { float bestScore = -2; axisMin = axis.NumericDataMin; axisMax = axis.NumericDataMax; if (axisMin == axisMax) { Debug.LogError("Cannot work on same min and max data"); return(null); } IDisplayAxis bestOption = null; for (int j = 1; j < int.MaxValue; j++) // there are break statements which terminate this loop { foreach (float q in Q) // (j,q): determines step size { float sm = SimplicityMax(q, j); if (Vector4.Dot(new Vector4(sm, 1, 1, 1), weights) < bestScore) { // finish the search j = int.MaxValue - 1; break; } for (int k = 2; k < int.MaxValue; k++) // try different numbers of labels { float dm = DensityMax(k / availableSpace, targetDensity); if (Vector4.Dot(new Vector4(sm, 1, dm, 1), weights) < bestScore) { break; } float delta = (axis.NumericDataMax - axis.NumericDataMin) / (k + 1) / (j * q); for (int z = Mathf.CeilToInt(Mathf.Log10(delta)); z < int.MaxValue; z++) // power of 10 multiplier for the step size { float lStep = q * j * Mathf.Pow(10, z); float cm = CoverageMax(axis.NumericDataMin, axis.NumericDataMax, lStep * (k - 1)); if (Vector4.Dot(new Vector4(sm, cm, dm, 1), weights) < bestScore) { break; } for (float start = Mathf.Floor(axis.NumericDataMax / lStep) - (k - 1); start <= Mathf.Ceil(axis.NumericDataMin / lStep); start += 1f / j) // possible start labels { float lmin = start * lStep; float lmax = lmin + (k - 1) * lStep; float s = Simplicity(q, j, lmin, lmax, lStep); float d = Density(k / availableSpace, targetDensity); float c = Coverage(axis.NumericDataMin, axis.NumericDataMax, lmin, lmax); if (Vector4.Dot(new Vector4(s, c, d, 1), weights) < bestScore) { continue; } List <float> stepSequence = Enumerable.Range(0, k).Select(x => lmin + x * lStep).ToList(); //List<string> labels = stepSequence.Select(value => value.ToString("0.##")).ToList(); // optimize legibility List <IDisplayAxis> possibilities = axis.GeneratePossibleConfigurations(minTextSize, maxTextSize, stepSequence); float legibility; IDisplayAxis localBest = DisplayAxis.FindBestLegibility(possibilities, out legibility, minTextSize, targetTextSize, 0.02f, availableSpace); float score = Vector4.Dot(new Vector4(s, c, d, legibility), weights); if (score > bestScore) { bestOption = localBest; bestScore = score; axisMax = lmax; axisMin = lmin; } } } } } } return(bestOption); }