talbotLinHanrahan returns an optimal set of approximately want label values for the data range [dMin, dMax], and the step and magnitude of the step between values. containment is specifies are guarantees for label and data range containment, valid values are free, containData and withinData. The opt
(dMin, dMax float64, want int, containment int, Q []float64, w *weights, legibility func(lMin, lMax, lStep float64) float64)
| 47 | // labelling scheme to 1. |
| 48 | // See the paper for an explanation of the function of Q, w and legibility. |
| 49 | func talbotLinHanrahan(dMin, dMax float64, want int, containment int, Q []float64, w *weights, legibility func(lMin, lMax, lStep float64) float64) (values []float64, step, q float64, magnitude int) { |
| 50 | const eps = dlamchP * 100 |
| 51 | |
| 52 | if dMin > dMax { |
| 53 | panic("labelling: invalid data range: min greater than max") |
| 54 | } |
| 55 | |
| 56 | if Q == nil { |
| 57 | Q = []float64{1, 5, 2, 2.5, 4, 3} |
| 58 | } |
| 59 | if w == nil { |
| 60 | w = &weights{ |
| 61 | simplicity: 0.25, |
| 62 | coverage: 0.2, |
| 63 | density: 0.5, |
| 64 | legibility: 0.05, |
| 65 | } |
| 66 | } |
| 67 | if legibility == nil { |
| 68 | legibility = unitLegibility |
| 69 | } |
| 70 | |
| 71 | if r := dMax - dMin; r < eps { |
| 72 | l := make([]float64, want) |
| 73 | step := r / float64(want-1) |
| 74 | for i := range l { |
| 75 | l[i] = dMin + float64(i)*step |
| 76 | } |
| 77 | magnitude = minAbsMag(dMin, dMax) |
| 78 | return l, step, 0, magnitude |
| 79 | } |
| 80 | |
| 81 | type selection struct { |
| 82 | // n is the number of labels selected. |
| 83 | n int |
| 84 | // lMin and lMax are the selected min |
| 85 | // and max label values. lq is the q |
| 86 | // chosen. |
| 87 | lMin, lMax, lStep, lq float64 |
| 88 | // score is the score for the selection. |
| 89 | score float64 |
| 90 | // magnitude is the magnitude of the |
| 91 | // label step distance. |
| 92 | magnitude int |
| 93 | } |
| 94 | best := selection{score: -2} |
| 95 | |
| 96 | outer: |
| 97 | for skip := 1; ; skip++ { |
| 98 | for _, q := range Q { |
| 99 | sm := maxSimplicity(q, Q, skip) |
| 100 | if w.score(sm, 1, 1, 1) < best.score { |
| 101 | break outer |
| 102 | } |
| 103 | |
| 104 | for have := 2; ; have++ { |
| 105 | dm := maxDensity(have, want) |
| 106 | if w.score(sm, 1, dm, 1) < best.score { |