github.com/Kintar/etxt@v0.0.0-20221224033739-2fc69f000137/emask/outliner_test.go (about)

     1  //go:build gtxt
     2  
     3  package emask
     4  
     5  import "testing"
     6  
     7  func TestBufferFillConvexQuad(t *testing.T) {
     8  	tests := []struct {
     9  		width  int
    10  		height int
    11  		coords [8]float64 // ax, ay, bx, by, cx, cy, dx, dy (pairs in any order)
    12  		out    []float64
    13  	}{
    14  		{ // one-pixel sized quad
    15  			width: 1, height: 1,
    16  			coords: [8]float64{0, 0, 0, 1, 1, 0, 1, 1},
    17  			out:    []float64{1.0},
    18  		},
    19  		{ // two-pixel sized quad
    20  			width: 2, height: 2,
    21  			coords: [8]float64{0, 0, 0, 2, 2, 0, 2, 2},
    22  			out:    []float64{1.0, 1.0, 1.0, 1.0},
    23  		},
    24  		{ // half-pixel sized rect
    25  			width: 1, height: 1,
    26  			coords: [8]float64{0, 0, 0, 0.5, 1.0, 0, 1, 0.5},
    27  			out:    []float64{0.5},
    28  		},
    29  		{ // half-pixel sized rect (different orientation and shifted)
    30  			width: 1, height: 2,
    31  			coords: [8]float64{0.5, 0.5, 1, 0.5, 0.5, 1.5, 1, 1.5},
    32  			out:    []float64{0.25, 0.25},
    33  		},
    34  		{ // half-pixel triangle
    35  			width: 1, height: 1,
    36  			coords: [8]float64{0, 0, 0, 0, 1, 1, 0, 1},
    37  			out:    []float64{0.5},
    38  		},
    39  		{ // two-pixel triangle
    40  			width: 2, height: 1,
    41  			coords: [8]float64{0, 0, 0, 0, 2, 1, 0, 1},
    42  			out:    []float64{0.75, 0.25},
    43  		},
    44  		{ // trapeze
    45  			width: 3, height: 3,
    46  			coords: [8]float64{1, 2, 3, 2, 0, 3, 3, 3},
    47  			out:    []float64{0, 0, 0, 0, 0, 0, 0.5, 1, 1},
    48  		},
    49  		{ // flat top simple shape
    50  			width: 1, height: 3,
    51  			coords: [8]float64{0, 3, 1, 0, 1, 2, 0, 0},
    52  			out:    []float64{1, 1, 0.5},
    53  		},
    54  		{ // flat bottom simple shape
    55  			width: 1, height: 3,
    56  			coords: [8]float64{1, 0, 1, 3, 0, 3, 0, 1},
    57  			out:    []float64{0.5, 1, 1},
    58  		},
    59  		{ // trapeze with left triangle with flat top
    60  			width: 3, height: 1,
    61  			coords: [8]float64{1, 1, 0, 0, 3, 0, 3, 1},
    62  			out:    []float64{0.5, 1, 1},
    63  		},
    64  		{ // left-pointing isosceles
    65  			width: 2, height: 1,
    66  			coords: [8]float64{2, 0, 2, 1, 0.5, 0.5, 2, 0},
    67  			out:    []float64{0.08333333, 0.666666666},
    68  		},
    69  		{ // right-pointing isosceles
    70  			width: 2, height: 1,
    71  			coords: [8]float64{0, 0, 0, 1, 1.5, 0.5, 0, 0},
    72  			out:    []float64{0.666666666, 0.08333333},
    73  		},
    74  		{ // hard case, tilted trapeze /_/
    75  			width: 3, height: 1,
    76  			coords: [8]float64{0, 1, 2, 0, 3, 0, 1, 1},
    77  			out:    []float64{0.25, 0.5, 0.25},
    78  		},
    79  		{ // hard case, tilted trapeze \_\ (symmetric to previous test)
    80  			width: 3, height: 1,
    81  			coords: [8]float64{0, 0, 2, 1, 3, 1, 1, 0},
    82  			out:    []float64{0.25, 0.5, 0.25},
    83  		},
    84  		{ // diamond shape without flat top or bottom
    85  			width: 2, height: 2,
    86  			coords: [8]float64{1, 0, 0, 1, 2, 1, 1, 2},
    87  			out:    []float64{0.5, 0.5, 0.5, 0.5},
    88  		},
    89  		{ // unaligned diamond shape
    90  			width: 2, height: 2,
    91  			coords: [8]float64{1, 0.5, 0.5, 1, 1.5, 1, 1, 1.5},
    92  			out:    []float64{0.125, 0.125, 0.125, 0.125},
    93  		},
    94  		{ // diagonal shape crossing 4 pixels
    95  			width: 2, height: 2,
    96  			coords: [8]float64{0.15, 0.85, 0.85, 0.15, 1.15, 1.85, 1.85, 1.15},
    97  			out:    []float64{0.455, 0.245, 0.245, 0.455},
    98  		},
    99  	}
   100  
   101  	buffer := &buffer{}
   102  	for n, test := range tests {
   103  		if len(test.out) != test.width*test.height {
   104  			t.Fatalf("malformed test %d", n)
   105  		}
   106  		buffer.Resize(test.width, test.height)
   107  		ax, ay := test.coords[0], test.coords[1]
   108  		bx, by := test.coords[2], test.coords[3]
   109  		cx, cy := test.coords[4], test.coords[5]
   110  		dx, dy := test.coords[6], test.coords[7]
   111  		buffer.FillConvexQuad(ax, ay, bx, by, cx, cy, dx, dy)
   112  		if !similarFloat64Slices(test.out, buffer.Values) {
   113  			t.Fatalf("test#%d, on input %v, expected %v, got %v", n, test.coords, test.out, buffer.Values)
   114  		}
   115  	}
   116  }
   117  
   118  func TestOutlinerCut(t *testing.T) {
   119  	tests := []struct {
   120  		thickness float64
   121  		width     int
   122  		height    int
   123  		coords    []float64 // the first pair is the first MoveTo, and
   124  		// remaining pairs are used for LineTo commands
   125  		out []float64
   126  	}{
   127  		// ---- single lines, axis-aligned ----
   128  		{ // horizontal line
   129  			thickness: 1.0, width: 2, height: 1,
   130  			coords: []float64{0.0, 0.5, 2.0, 0.5},
   131  			out:    []float64{1.0, 1.0},
   132  		},
   133  		{ // vertical line
   134  			thickness: 1.0, width: 1, height: 2,
   135  			coords: []float64{0.5, 0.0, 0.5, 2.0},
   136  			out:    []float64{1.0, 1.0},
   137  		},
   138  		{ // wider horizontal line
   139  			thickness: 2.0, width: 3, height: 2,
   140  			coords: []float64{0.0, 1.0, 3.0, 1.0},
   141  			out:    []float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
   142  		},
   143  		{ // wider vertical line
   144  			thickness: 2.0, width: 2, height: 3,
   145  			coords: []float64{1.0, 0.0, 1.0, 3.0},
   146  			out:    []float64{1.0, 1.0, 1.0, 1.0, 1.0, 1.0},
   147  		},
   148  
   149  		// ---- single diagonal lines ----
   150  		{ // 45 deg line
   151  			thickness: 0.2, width: 1, height: 1,
   152  			coords: []float64{0.1, 0.1, 0.9, 0.9},
   153  			out:    []float64{0.22627},
   154  		},
   155  		{ // some tilted line going through two vertical pixels
   156  			thickness: 0.2, width: 1, height: 2,
   157  			coords: []float64{0.25, 0.5, 0.75, 1.5},
   158  			out:    []float64{0.1118033, 0.1118033}, // total = 0.2236
   159  		},
   160  		{ // like the previous, but flipped 90 degs
   161  			thickness: 0.2, width: 2, height: 1,
   162  			coords: []float64{0.5, 0.25, 1.5, 0.75},
   163  			out:    []float64{0.1118033, 0.1118033}, // total = 0.2236
   164  		},
   165  		{ // diagonal going through 4 pixels
   166  			thickness: 0.5, width: 2, height: 2,
   167  			coords: []float64{0.5, 0.5, 1.5, 1.5},
   168  			out:    []float64{0.29105, 0.0625, 0.0625, 0.29105},
   169  		},
   170  
   171  		// ---- basic shapes ----
   172  		{ // L-like shape
   173  			thickness: 1.0, width: 3, height: 3,
   174  			coords: []float64{0.5, 0.0, 0.5, 2.5, 3.0, 2.5},
   175  			out:    []float64{1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0},
   176  		},
   177  		{ // C-like shape
   178  			thickness: 1.0, width: 3, height: 3,
   179  			coords: []float64{3.0, 0.5, 0.5, 0.5, 0.5, 2.5, 3.0, 2.5},
   180  			out:    []float64{1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 1.0, 1.0},
   181  		},
   182  
   183  		// ---- single lines from multiple segments ----
   184  		{ // horizontal line lousily drawn in two segments
   185  			thickness: 1.0, width: 2, height: 1,
   186  			coords: []float64{0.0, 0.5, 1.0, 0.5, 2.0, 0.5},
   187  			out:    []float64{1.0, 1.0},
   188  		},
   189  		{ // vertical line lousily drawn in two segments
   190  			thickness: 1.0, width: 1, height: 2,
   191  			coords: []float64{0.5, 0.0, 0.5, 1.0, 0.5, 2.0},
   192  			out:    []float64{1.0, 1.0},
   193  		},
   194  		{ // diagonal line lousily drawn in two segments
   195  			thickness: 1.0, width: 2, height: 2,
   196  			coords: []float64{0.5, 0.5, 1.0, 1.0, 1.5, 1.5},
   197  			out:    []float64{0.457, 0.25, 0.25, 0.457},
   198  		},
   199  	}
   200  
   201  	outliner := &outliner{}
   202  	outliner.CurveSegmenter.SetThreshold(1 / 1024)
   203  	outliner.CurveSegmenter.SetMaxSplits(8)
   204  	for n, test := range tests {
   205  		if len(test.out) != test.width*test.height {
   206  			t.Fatalf("malformed test %d", n)
   207  		}
   208  		outliner.Buffer.Resize(test.width, test.height)
   209  		outliner.MoveTo(test.coords[0], test.coords[1])
   210  		outliner.SetThickness(test.thickness)
   211  		for i := 2; i < len(test.coords); i += 2 {
   212  			outliner.LineTo(test.coords[i], test.coords[i+1])
   213  		}
   214  		outliner.CutPath()
   215  
   216  		// clamp values bigger than 1 for proper result comparisons
   217  		// for i := 0; i < len(outliner.Buffer.Values); i++ {
   218  		// 	if outliner.Buffer.Values[i] > 1.0 {
   219  		// 		outliner.Buffer.Values[i] = 1.0
   220  		// 	}
   221  		// }
   222  
   223  		if !similarFloat64Slices(test.out, outliner.Buffer.Values) {
   224  			t.Fatalf("test#%d, on input %v, expected %v, got %v", n, test.coords, test.out, outliner.Buffer.Values)
   225  		}
   226  	}
   227  }