...

Source file src/gonum.org/v1/plot/palette/moreland/smooth_test.go

Documentation: gonum.org/v1/plot/palette/moreland

     1  // Copyright ©2016 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package moreland
     6  
     7  import (
     8  	"image/color"
     9  	"math"
    10  	"strings"
    11  	"testing"
    12  
    13  	"golang.org/x/exp/rand"
    14  
    15  	"gonum.org/v1/plot/palette"
    16  )
    17  
    18  // bitTolerance is the precision of a uint8 when
    19  // expressed as a uint32. This tolerance is used in tests
    20  // where precision can be lost when coverting between
    21  // 8-bit and 32-bit values.
    22  const bitTolerance = 1.0 / 256.0 * 65535.0
    23  
    24  func TestInterpolateMSHDiverging(t *testing.T) {
    25  	type test struct {
    26  		start, end                       msh
    27  		convergeM, scalar, convergePoint float64
    28  		result                           msh
    29  	}
    30  	tests := []test{
    31  		{
    32  			start:         msh{M: 80, S: 1.08, H: -1.1},
    33  			end:           msh{M: 80, S: 1.08, H: 0.5},
    34  			convergeM:     88,
    35  			convergePoint: 0.5,
    36  			scalar:        0.125,
    37  			result:        msh{M: 82, S: 0.81, H: -1.2402896406131008},
    38  		},
    39  		{
    40  			start:         msh{M: 80, S: 1.08, H: -1.1},
    41  			end:           msh{M: 80, S: 1.08, H: 0.5},
    42  			convergeM:     88,
    43  			convergePoint: 0.5,
    44  			scalar:        0.5,
    45  			result:        msh{M: 88, S: 0, H: 0},
    46  		},
    47  		{
    48  			start:         msh{M: 80, S: 1.08, H: -1.1},
    49  			end:           msh{M: 80, S: 1.08, H: 0.5},
    50  			convergeM:     88,
    51  			convergePoint: 0.5,
    52  			scalar:        0.75,
    53  			result:        msh{M: 84, S: 0.54, H: 0.7805792812262012},
    54  		},
    55  		{
    56  			start:         msh{M: 80, S: 1.08, H: -1.1},
    57  			end:           msh{M: 80, S: 1.08, H: 0.5},
    58  			convergeM:     88,
    59  			convergePoint: 0.75,
    60  			scalar:        0.7499999999999999,
    61  			result:        msh{M: 88, S: 1.1990408665951691e-16, H: -1.6611585624524023},
    62  		},
    63  		{
    64  			start:         msh{M: 80, S: 1.08, H: -1.1},
    65  			end:           msh{M: 80, S: 1.08, H: 0.5},
    66  			convergeM:     88,
    67  			convergePoint: 0.75,
    68  			scalar:        0.75,
    69  			result:        msh{M: 88, S: 0, H: 0},
    70  		},
    71  		{
    72  			start:         msh{M: 80, S: 1.08, H: -1.1},
    73  			end:           msh{M: 80, S: 1.08, H: 0.5},
    74  			convergeM:     88,
    75  			convergePoint: 0.75,
    76  			scalar:        0.7500000000000001,
    77  			result:        msh{M: 88, S: 2.3980817331903383e-16, H: 1.0611585624524023},
    78  		},
    79  	}
    80  	for i, test := range tests {
    81  		p := newSmoothDiverging(test.start, test.end, test.convergeM)
    82  		p.SetMin(0)
    83  		p.SetMax(1)
    84  		result := p.(*smoothDiverging).interpolateMSHDiverging(test.scalar, test.convergePoint)
    85  		if result != test.result {
    86  			t.Errorf("test %d: expected %v; got %v", i, test.result, result)
    87  		}
    88  	}
    89  }
    90  
    91  func TestSmoothBlueRed(t *testing.T) {
    92  	p := SmoothBlueRed()
    93  	wantP := []color.NRGBA{
    94  		{59, 76, 192, 255},
    95  		{68, 90, 204, 255},
    96  		{77, 104, 215, 255},
    97  		{87, 117, 225, 255},
    98  		{98, 130, 234, 255},
    99  		{108, 142, 241, 255},
   100  		{119, 154, 247, 255},
   101  		{130, 165, 251, 255},
   102  		{141, 176, 254, 255},
   103  		{152, 185, 255, 255},
   104  		{163, 194, 255, 255},
   105  		{174, 201, 253, 255},
   106  		{184, 208, 249, 255},
   107  		{194, 213, 244, 255},
   108  		{204, 217, 238, 255},
   109  		{213, 219, 230, 255},
   110  		{221, 221, 221, 255},
   111  		{229, 216, 209, 255},
   112  		{236, 211, 197, 255},
   113  		{241, 204, 185, 255},
   114  		{245, 196, 173, 255},
   115  		{247, 187, 160, 255},
   116  		{247, 177, 148, 255},
   117  		{247, 166, 135, 255},
   118  		{244, 154, 123, 255},
   119  		{241, 141, 111, 255},
   120  		{236, 127, 99, 255},
   121  		{229, 112, 88, 255},
   122  		{222, 96, 77, 255},
   123  		{213, 80, 66, 255},
   124  		{203, 62, 56, 255},
   125  		{192, 40, 47, 255},
   126  		{180, 4, 38, 255},
   127  	}
   128  	c := p.Palette(33)
   129  	if len(c.Colors()) != len(wantP) {
   130  		t.Errorf("length doesn't match: %d != %d", len(c.Colors()), len(wantP))
   131  	}
   132  	for i, c := range c.Colors() {
   133  		w := wantP[i]
   134  		if !similar(w, c, bitTolerance) {
   135  			t.Errorf("%d: want %+v but have %+v", i, w, c)
   136  		}
   137  	}
   138  }
   139  
   140  func TestSmoothCoolWarm(t *testing.T) {
   141  	type test struct {
   142  		start [3]float64
   143  		f     func(int) palette.Palette
   144  		end   [3]float64
   145  	}
   146  	tests := []test{
   147  		{[3]float64{0.230, 0.299, 0.754}, SmoothBlueRed().Palette, [3]float64{0.706, 0.016, 0.150}},
   148  		{[3]float64{0.436, 0.308, 0.631}, SmoothPurpleOrange().Palette, [3]float64{0.759, 0.334, 0.046}},
   149  		{[3]float64{0.085, 0.532, 0.201}, SmoothGreenPurple().Palette, [3]float64{0.436, 0.308, 0.631}},
   150  		{[3]float64{0.217, 0.525, 0.910}, SmoothBlueTan().Palette, [3]float64{0.677, 0.492, 0.093}},
   151  		{[3]float64{0.085, 0.532, 0.201}, SmoothGreenRed().Palette, [3]float64{0.758, 0.214, 0.233}},
   152  	}
   153  	midPoint := [3]float64{0.865, 0.865, 0.865}
   154  
   155  	for i, test := range tests {
   156  		c := test.f(3).Colors()
   157  		testRGB(t, i, "start", c[0], test.start)
   158  		testRGB(t, i, "mid", c[1], midPoint)
   159  		testRGB(t, i, "end", c[2], test.end)
   160  	}
   161  }
   162  
   163  func fracToByte(v float64) uint8 {
   164  	return uint8(v*255 + 0.5)
   165  }
   166  
   167  func testRGB(t *testing.T, i int, label string, c1 color.Color, c2 [3]float64) {
   168  	c3 := color.NRGBA{
   169  		R: fracToByte(c2[0]),
   170  		G: fracToByte(c2[1]),
   171  		B: fracToByte(c2[2]),
   172  		A: 255,
   173  	}
   174  	if !similar(c1, c3, bitTolerance) {
   175  		t.Errorf("%d %s: want %+v but have %+v", i, label, c1, c3)
   176  	}
   177  }
   178  
   179  func TestSmoothDiverging_At(t *testing.T) {
   180  
   181  	start := msh{M: 80, S: 1.08, H: -1.1}
   182  	end := msh{M: 80, S: 1.08, H: 0.5}
   183  	p := newSmoothDiverging(start, end, 88)
   184  	p.SetMax(2)
   185  	p.SetMin(-1)
   186  	scalar := -1 + 3*0.125
   187  	rgb, err := p.At(scalar)
   188  	if err != nil {
   189  		t.Error(err)
   190  	}
   191  	// The expected output values are from
   192  	// http://www.kennethmoreland.com/color-maps/DivergingColorMapWorkshop.xls
   193  	want := color.NRGBA{R: 98, G: 130, B: 234, A: 255}
   194  	if !similar(want, rgb, bitTolerance) {
   195  		t.Errorf("have %+v, want %+v", rgb, want)
   196  	}
   197  }
   198  
   199  func BenchmarkSmoothDiverging_At(b *testing.B) {
   200  	p := SmoothBlueRed()
   201  	p.SetMax(1.0000000001)
   202  	rand.Seed(1)
   203  	for i := 0; i < b.N; i++ {
   204  		if _, err := p.At(rand.Float64()); err != nil {
   205  			b.Fatal(err)
   206  		}
   207  	}
   208  }
   209  
   210  func TestSmoothDiverging_Range(t *testing.T) {
   211  	p := SmoothBlueRed()
   212  	if _, err := p.At(0); !strings.Contains(err.Error(), "max == min") {
   213  		t.Errorf("should have 'max == min' error")
   214  	}
   215  	p.SetMax(1)
   216  	vals := []float64{-1, 0, 1, 2, math.Inf(1), math.Inf(-1), math.NaN()}
   217  	errs := []error{palette.ErrUnderflow, nil, nil, palette.ErrOverflow,
   218  		palette.ErrOverflow, palette.ErrUnderflow, palette.ErrNaN}
   219  	for i, v := range vals {
   220  		_, err := p.At(v)
   221  		wantErr := errs[i]
   222  		if wantErr == nil && err != nil {
   223  			t.Errorf("val %g: want no error but have %v", v, err)
   224  		} else if wantErr != nil && err == nil {
   225  			t.Errorf("val %g: want error but have no error", v)
   226  		} else if wantErr != err {
   227  			t.Errorf("val %g: want error %v but have %v", v, wantErr, err)
   228  		}
   229  	}
   230  	p.SetMin(2)
   231  	if _, err := p.At(0); !strings.Contains(err.Error(), "< min") {
   232  		t.Errorf("should have 'max < min' error")
   233  	}
   234  }
   235  
   236  // similar compares whether the fields of a and b are within the
   237  // specified tolerance of each other.
   238  func similar(a, b color.Color, tolerance float64) bool {
   239  	aR, aG, aB, aA := a.RGBA()
   240  	bR, bG, bB, bA := b.RGBA()
   241  	if math.Abs(float64(bR)-float64(aR)) > tolerance ||
   242  		math.Abs(float64(bG)-float64(aG)) > tolerance ||
   243  		math.Abs(float64(bB)-float64(aB)) > tolerance ||
   244  		math.Abs(float64(bA)-float64(aA)) > tolerance {
   245  		return false
   246  	}
   247  	return true
   248  }
   249  

View as plain text