diff --git a/internal/area/area.go b/internal/area/area.go index c0b3a30..236b9b7 100644 --- a/internal/area/area.go +++ b/internal/area/area.go @@ -78,6 +78,29 @@ func VSplit(area image.Rectangle, widthPerc int) (left image.Rectangle, right im return left, right, nil } +// VSplitCells returns two new areas created by splitting the provided area +// after the specified amount of cells of its width. The number of cells must +// be a zero or a positive integer. Providing a zero returns left=image.ZR, +// right=area. Providing a number equal or larger to area's width returns +// left=area, right=image.ZR. +func VSplitCells(area image.Rectangle, cells int) (left image.Rectangle, right image.Rectangle, err error) { + if min := 0; cells < min { + return image.ZR, image.ZR, fmt.Errorf("invalid cells %d, must be a positive integer", cells) + } + if cells == 0 { + return image.ZR, area, nil + } + + width := area.Dx() + if cells >= width { + return area, image.ZR, nil + } + + left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+cells, area.Max.Y) + right = image.Rect(area.Min.X+cells, area.Min.Y, area.Max.X, area.Max.Y) + return left, right, nil +} + // ExcludeBorder returns a new area created by subtracting a border around the // provided area. Return the zero area if there isn't enough space to exclude // the border. diff --git a/internal/area/area_test.go b/internal/area/area_test.go index cd92df9..16630cd 100644 --- a/internal/area/area_test.go +++ b/internal/area/area_test.go @@ -282,6 +282,91 @@ func TestVSplit(t *testing.T) { } } +func TestVSplitCells(t *testing.T) { + tests := []struct { + desc string + area image.Rectangle + cells int + wantLeft image.Rectangle + wantRight image.Rectangle + wantErr bool + }{ + { + desc: "fails on negative cells", + area: image.Rect(1, 1, 2, 2), + cells: -1, + wantErr: true, + }, + { + desc: "returns area as left on cells too large", + area: image.Rect(1, 1, 2, 2), + cells: 2, + wantLeft: image.Rect(1, 1, 2, 2), + wantRight: image.ZR, + }, + { + desc: "returns area as left on cells equal area width", + area: image.Rect(1, 1, 2, 2), + cells: 1, + wantLeft: image.Rect(1, 1, 2, 2), + wantRight: image.ZR, + }, + { + desc: "returns area as right on zero cells", + area: image.Rect(1, 1, 2, 2), + cells: 0, + wantRight: image.Rect(1, 1, 2, 2), + wantLeft: image.ZR, + }, + { + desc: "zero area to begin with", + area: image.ZR, + cells: 0, + wantLeft: image.ZR, + wantRight: image.ZR, + }, + { + desc: "splits area with even width", + area: image.Rect(1, 1, 3, 3), + cells: 1, + wantLeft: image.Rect(1, 1, 2, 3), + wantRight: image.Rect(2, 1, 3, 3), + }, + { + desc: "splits area with odd width", + area: image.Rect(1, 1, 4, 4), + cells: 1, + wantLeft: image.Rect(1, 1, 2, 4), + wantRight: image.Rect(2, 1, 4, 4), + }, + { + desc: "splits to unequal areas", + area: image.Rect(0, 0, 4, 4), + cells: 3, + wantLeft: image.Rect(0, 0, 3, 4), + wantRight: image.Rect(3, 0, 4, 4), + }, + } + + for _, tc := range tests { + t.Run(tc.desc, func(t *testing.T) { + gotLeft, gotRight, err := VSplitCells(tc.area, tc.cells) + if (err != nil) != tc.wantErr { + t.Errorf("VSplitCells => unexpected error:%v, wantErr:%v", err, tc.wantErr) + } + if err != nil { + return + } + if diff := pretty.Compare(tc.wantLeft, gotLeft); diff != "" { + t.Errorf("VSplitCells => left value unexpected diff (-want, +got):\n%s", diff) + } + if diff := pretty.Compare(tc.wantRight, gotRight); diff != "" { + t.Errorf("VSplitCells => right value unexpected diff (-want, +got):\n%s", diff) + } + }) + } +} + func TestExcludeBorder(t *testing.T) { tests := []struct { desc string