Merge pull request #373 from spacez320/292-allow-splitfixed-to-set-the-size-of-the-second-container

Allow alternative area explicit sizing in splits
This commit is contained in:
Jakub Sobon 2024-03-10 15:58:11 -04:00 committed by GitHub
commit ce0dd0b666
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 651 additions and 35 deletions

View File

@ -180,14 +180,26 @@ func (c *Container) split() (image.Rectangle, image.Rectangle, error) {
}
if c.opts.splitFixed > DefaultSplitFixed {
if c.opts.split == splitTypeVertical {
if c.opts.splitReversed {
return area.VSplitCellsReversed(ar, c.opts.splitFixed)
}
return area.VSplitCells(ar, c.opts.splitFixed)
}
if c.opts.splitReversed {
return area.HSplitCellsReversed(ar, c.opts.splitFixed)
}
return area.HSplitCells(ar, c.opts.splitFixed)
}
if c.opts.split == splitTypeVertical {
if c.opts.splitReversed {
return area.VSplitReversed(ar, c.opts.splitPercent)
}
return area.VSplit(ar, c.opts.splitPercent)
}
if c.opts.splitReversed {
return area.HSplitReversed(ar, c.opts.splitPercent)
}
return area.HSplit(ar, c.opts.splitPercent)
}

View File

@ -713,7 +713,33 @@ func TestNew(t *testing.T) {
},
},
{
desc: "horizontal unequal split",
desc: "horizontal, reversed unequal split",
termSize: image.Point{10, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
SplitHorizontal(
Top(
Border(linestyle.Light),
),
Bottom(
Border(linestyle.Light),
),
SplitPercentFromEnd(20),
),
)
},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
testdraw.MustBorder(cvs, image.Rect(0, 0, 10, 16))
testdraw.MustBorder(cvs, image.Rect(0, 16, 10, 20))
testcanvas.MustApply(cvs, ft)
return ft
},
},
{
desc: "horizontal fixed splits",
termSize: image.Point{10, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
@ -738,6 +764,32 @@ func TestNew(t *testing.T) {
return ft
},
},
{
desc: "horizontal, reversed fixed splits",
termSize: image.Point{10, 20},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
SplitHorizontal(
Top(
Border(linestyle.Light),
),
Bottom(
Border(linestyle.Light),
),
SplitFixedFromEnd(4),
),
)
},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
testdraw.MustBorder(cvs, image.Rect(0, 0, 10, 16))
testdraw.MustBorder(cvs, image.Rect(0, 16, 10, 20))
testcanvas.MustApply(cvs, ft)
return ft
},
},
{
desc: "horizontal split, parent and children have borders",
termSize: image.Point{10, 10},
@ -864,6 +916,32 @@ func TestNew(t *testing.T) {
return ft
},
},
{
desc: "vertical, reversed unequal split",
termSize: image.Point{20, 10},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
SplitVertical(
Left(
Border(linestyle.Light),
),
Right(
Border(linestyle.Light),
),
SplitPercentFromEnd(20),
),
)
},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
testdraw.MustBorder(cvs, image.Rect(0, 0, 16, 10))
testdraw.MustBorder(cvs, image.Rect(16, 0, 20, 10))
testcanvas.MustApply(cvs, ft)
return ft
},
},
{
desc: "vertical fixed splits",
termSize: image.Point{20, 10},
@ -890,6 +968,32 @@ func TestNew(t *testing.T) {
return ft
},
},
{
desc: "vertical, reversed fixed splits",
termSize: image.Point{20, 10},
container: func(ft *faketerm.Terminal) (*Container, error) {
return New(
ft,
SplitVertical(
Left(
Border(linestyle.Light),
),
Right(
Border(linestyle.Light),
),
SplitFixedFromEnd(4),
),
)
},
want: func(size image.Point) *faketerm.Terminal {
ft := faketerm.MustNew(size)
cvs := testcanvas.MustNew(ft.Area())
testdraw.MustBorder(cvs, image.Rect(0, 0, 16, 10))
testdraw.MustBorder(cvs, image.Rect(16, 0, 20, 10))
testcanvas.MustApply(cvs, ft)
return ft
},
},
{
desc: "vertical split, parent and children have borders",
termSize: image.Point{10, 10},

View File

@ -108,9 +108,10 @@ type options struct {
inherited inherited
// split identifies how is this container split.
split splitType
splitPercent int
splitFixed int
split splitType
splitReversed bool
splitPercent int
splitFixed int
// widget is the widget in the container.
// A container can have either two sub containers (left and right) or a
@ -247,10 +248,11 @@ func newOptions(parent *options) *options {
inherited: inherited{
focusedColor: cell.ColorYellow,
},
hAlign: align.HorizontalCenter,
vAlign: align.VerticalMiddle,
splitPercent: DefaultSplitPercent,
splitFixed: DefaultSplitFixed,
hAlign: align.HorizontalCenter,
vAlign: align.VerticalMiddle,
splitReversed: DefaultSplitReversed,
splitPercent: DefaultSplitPercent,
splitFixed: DefaultSplitFixed,
}
if parent != nil {
opts.global = parent.global
@ -281,13 +283,17 @@ func (so splitOption) setSplit(opts *options) error {
return so(opts)
}
// DefaultSplitReversed is the default value for the SplitReversed option.
const DefaultSplitReversed = false
// DefaultSplitPercent is the default value for the SplitPercent option.
const DefaultSplitPercent = 50
// DefaultSplitFixed is the default value for the SplitFixed option.
const DefaultSplitFixed = -1
// SplitPercent sets the relative size of the split as percentage of the available space.
// SplitPercent sets the relative size of the split as percentage of the
// available space.
// When using SplitVertical, the provided size is applied to the new left
// container, the new right container gets the reminder of the size.
// When using SplitHorizontal, the provided size is applied to the new top
@ -304,6 +310,25 @@ func SplitPercent(p int) SplitOption {
})
}
// SplitPercentFromEnd sets the relative size of the split as percentage of the
// available space.
// When using SplitVertical, the provided size is applied to the new right
// container, the new left container gets the reminder of the size.
// When using SplitHorizontal, the provided size is applied to the new bottom
// container, the new top container gets the reminder of the size.
// The provided value must be a positive number in the range 0 < p < 100.
// If not provided, defaults to using SplitPercent with DefaultSplitPercent.
func SplitPercentFromEnd(p int) SplitOption {
return splitOption(func(opts *options) error {
if min, max := 0, 100; p <= min || p >= max {
return fmt.Errorf("invalid split percentage %d, must be in range %d < p < %d", p, min, max)
}
opts.splitReversed = true
opts.splitPercent = p
return nil
})
}
// SplitFixed sets the size of the first container to be a fixed value
// and makes the second container take up the remaining space.
// When using SplitVertical, the provided size is applied to the new left
@ -311,8 +336,9 @@ func SplitPercent(p int) SplitOption {
// When using SplitHorizontal, the provided size is applied to the new top
// container, the new bottom container gets the reminder of the size.
// The provided value must be a positive number in the range 0 <= cells.
// If SplitFixed() is not specified, it defaults to SplitPercent() and its given value.
// Only one of SplitFixed() and SplitPercent() can be specified per container.
// If SplitFixed* or SplitPercent* is not specified, it defaults to
// SplitPercent() and its given value.
// Only one SplitFixed* or SplitPercent* may be specified per container.
func SplitFixed(cells int) SplitOption {
return splitOption(func(opts *options) error {
if cells < 0 {
@ -323,6 +349,27 @@ func SplitFixed(cells int) SplitOption {
})
}
// SplitFixedFromEnd sets the size of the second container to be a fixed value
// and makes the first container take up the remaining space.
// When using SplitVertical, the provided size is applied to the new right
// container, the new left container gets the reminder of the size.
// When using SplitHorizontal, the provided size is applied to the new bottom
// container, the new top container gets the reminder of the size.
// The provided value must be a positive number in the range 0 <= cells.
// If SplitFixed* or SplitPercent* is not specified, it defaults to
// SplitPercent() and its given value.
// Only one SplitFixed* or SplitPercent* may be specified per container.
func SplitFixedFromEnd(cells int) SplitOption {
return splitOption(func(opts *options) error {
if cells < 0 {
return fmt.Errorf("invalid fixed value %d, must be in range %d <= cells", cells, 0)
}
opts.splitFixed = cells
opts.splitReversed = true
return nil
})
}
// SplitVertical splits the container along the vertical axis into two sub
// containers. The use of this option removes any widget placed at this
// container, containers with sub containers cannot contain widgets.

View File

@ -38,92 +38,210 @@ func FromSize(size image.Point) (image.Rectangle, error) {
return image.Rect(0, 0, size.X, size.Y), nil
}
// HSplit returns two new areas created by splitting the provided area at the
// specified percentage of its width. The percentage must be in the range
// 0 <= heightPerc <= 100.
// hSplit returns two new areas created by splitting the provided area at the
// specified percentage of its height, applying the percentage to the top or
// bottom area, depending on the reversed flag. The percentage must be in the
// range 0 <= heightPerc <= 100.
// Can return zero size areas.
func HSplit(area image.Rectangle, heightPerc int) (top image.Rectangle, bottom image.Rectangle, err error) {
func hSplit(area image.Rectangle, heightPerc int, reversed bool) (top image.Rectangle, bottom image.Rectangle, err error) {
if min, max := 0, 100; heightPerc < min || heightPerc > max {
return image.ZR, image.ZR, fmt.Errorf("invalid heightPerc %d, must be in range %d <= heightPerc <= %d", heightPerc, min, max)
}
height := area.Dy() * heightPerc / 100
top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+height)
if reversed {
top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Max.Y-height)
bottom = image.Rect(area.Min.X, area.Max.Y-height, area.Max.X, area.Max.Y)
} else {
top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+height)
bottom = image.Rect(area.Min.X, area.Min.Y+height, area.Max.X, area.Max.Y)
}
if top.Dy() == 0 {
top = image.ZR
}
bottom = image.Rect(area.Min.X, area.Min.Y+height, area.Max.X, area.Max.Y)
if bottom.Dy() == 0 {
bottom = image.ZR
}
return top, bottom, nil
}
// VSplit returns two new areas created by splitting the provided area at the
// specified percentage of its width. The percentage must be in the range
// 0 <= widthPerc <= 100.
// HSplit returns two new areas created by splitting the provided area at the
// specified percentage of its height, applying the percentage to the top area.
// The percentage must be in the range 0 <= heightPerc <= 100.
// Can return zero size areas.
func VSplit(area image.Rectangle, widthPerc int) (left image.Rectangle, right image.Rectangle, err error) {
func HSplit(area image.Rectangle, heightPerc int) (top image.Rectangle, bottom image.Rectangle, err error) {
return hSplit(area, heightPerc, false)
}
// HSplitReversed returns two new areas created by splitting the provided area
// at the specified percentage of its height, applying the percentage to the
// bottom area. The percentage must be in the range 0 <= heightPerc <= 100.
// Can return zero size areas.
func HSplitReversed(area image.Rectangle, heightPerc int) (top image.Rectangle, bottom image.Rectangle, err error) {
return hSplit(area, heightPerc, true)
}
// vSplit returns two new areas created by splitting the provided area at the
// specified percentage of its width, applying the percentage to the left or
// right area, depending on the reversed flag. The percentage must be in the
// range 0 <= widthPerc <= 100.
// Can return zero size areas.
func vSplit(area image.Rectangle, widthPerc int, reversed bool) (left image.Rectangle, right image.Rectangle, err error) {
if min, max := 0, 100; widthPerc < min || widthPerc > max {
return image.ZR, image.ZR, fmt.Errorf("invalid widthPerc %d, must be in range %d <= widthPerc <= %d", widthPerc, min, max)
}
width := area.Dx() * widthPerc / 100
left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+width, area.Max.Y)
if reversed {
left = image.Rect(area.Min.X, area.Min.Y, area.Max.X-width, area.Max.Y)
right = image.Rect(area.Max.X-width, area.Min.Y, area.Max.X, area.Max.Y)
} else {
left = image.Rect(area.Min.X, area.Min.Y, area.Min.X+width, area.Max.Y)
right = image.Rect(area.Min.X+width, area.Min.Y, area.Max.X, area.Max.Y)
}
if left.Dx() == 0 {
left = image.ZR
}
right = image.Rect(area.Min.X+width, area.Min.Y, area.Max.X, area.Max.Y)
if right.Dx() == 0 {
right = image.ZR
}
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,
// VSplit returns two new areas created by splitting the provided area at the
// specified percentage of its width, applying the percentage to the left area.
// The percentage must be in the range 0 <= widthPerc <= 100.
// Can return zero size areas.
func VSplit(area image.Rectangle, widthPerc int) (left image.Rectangle, right image.Rectangle, err error) {
return vSplit(area, widthPerc, false)
}
// VSplitReversed returns two new areas created by splitting the provided area
// at the specified percentage of its width, applying the percentage to the
// right area. The percentage must be in the range 0 <= widthPerc <= 100.
// Can return zero size areas.
func VSplitReversed(area image.Rectangle, widthPerc int) (left image.Rectangle, right image.Rectangle, err error) {
return vSplit(area, widthPerc, true)
}
// vSplitCells returns two new areas created by splitting the provided area
// after the specified amount of cells of its width, applied to the left or
// right area, depending on the reversed flag. 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) {
func vSplitCells(area image.Rectangle, cells int, reversed bool) (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 {
if reversed {
return area, image.ZR, nil
}
return image.ZR, area, nil
}
width := area.Dx()
if cells >= width {
if reversed {
return image.ZR, area, nil
}
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)
splitX := area.Min.X
if reversed {
splitX = area.Max.X - cells
} else {
splitX = area.Min.X + cells
}
left = image.Rect(area.Min.X, area.Min.Y, splitX, area.Max.Y)
right = image.Rect(splitX, area.Min.Y, area.Max.X, area.Max.Y)
return left, right, nil
}
// HSplitCells returns two new areas created by splitting the provided area
// after the specified amount of cells of its height. The number of cells must
// be a zero or a positive integer. Providing a zero returns top=image.ZR,
// VSplitCells returns two new areas created by splitting the provided area
// after the specified amount of cells of its width, as applied to the left
// area. 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) {
return vSplitCells(area, cells, false)
}
// VSplitCellsReversed returns two new areas created by splitting the provided
// area after the specified amount of cells of its width, as applied to the
// right area. 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 VSplitCellsReversed(area image.Rectangle, cells int) (left image.Rectangle, right image.Rectangle, err error) {
return vSplitCells(area, cells, true)
}
// hSplitCells returns two new areas created by splitting the provided area
// after the specified amount of cells of its height, applied to the top or
// bottom area, depending on the reversed flag. The number of cells must be a
// zero or a positive integer. Providing a zero returns top=image.ZR,
// bottom=area. Providing a number equal or larger to area's height returns
// top=area, bottom=image.ZR.
func HSplitCells(area image.Rectangle, cells int) (top image.Rectangle, bottom image.Rectangle, err error) {
func hSplitCells(area image.Rectangle, cells int, reversed bool) (top image.Rectangle, bottom 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 {
if reversed {
return area, image.ZR, nil
}
return image.ZR, area, nil
}
height := area.Dy()
if cells >= height {
if reversed {
return image.ZR, area, nil
}
return area, image.ZR, nil
}
top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, area.Min.Y+cells)
bottom = image.Rect(area.Min.X, area.Min.Y+cells, area.Max.X, area.Max.Y)
splitY := area.Min.Y
if reversed {
splitY = area.Max.Y - cells
} else {
splitY = area.Min.Y + cells
}
top = image.Rect(area.Min.X, area.Min.Y, area.Max.X, splitY)
bottom = image.Rect(area.Min.X, splitY, area.Max.X, area.Max.Y)
return top, bottom, nil
}
// HSplitCells returns two new areas created by splitting the provided area
// after the specified amount of cells of its height, as applied to the top
// area. The number of cells must be a zero or a positive integer. Providing a
// zero returns top=image.ZR, bottom=area. Providing a number equal or larger to
// area's height returns top=area, bottom=image.ZR.
func HSplitCells(area image.Rectangle, cells int) (top image.Rectangle, bottom image.Rectangle, err error) {
return hSplitCells(area, cells, false)
}
// HSplitCellsReversed returns two new areas created by splitting the provided
// area after the specified amount of cells of its height, as applied to the
// bottom area. The number of cells must be a zero or a positive integer.
// Providing a zero returns top=area, bottom=image.ZR. Providing a number equal
// or larger to area's height returns top=image.ZR, bottom=area.
func HSplitCellsReversed(area image.Rectangle, cells int) (top image.Rectangle, bottom image.Rectangle, err error) {
return hSplitCells(area, cells, true)
}
// 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.

View File

@ -198,6 +198,87 @@ func TestHSplit(t *testing.T) {
}
}
func TestHSplitReversed(t *testing.T) {
tests := []struct {
desc string
area image.Rectangle
heightPerc int
wantTop image.Rectangle
wantBot image.Rectangle
wantErr bool
}{
{
desc: "fails on heightPerc too small",
area: image.Rect(1, 1, 2, 2),
heightPerc: -1,
wantErr: true,
},
{
desc: "fails on heightPerc too large",
area: image.Rect(1, 1, 2, 2),
heightPerc: 101,
wantErr: true,
},
{
desc: "zero area to begin with",
area: image.ZR,
heightPerc: 50,
wantTop: image.ZR,
wantBot: image.ZR,
},
{
desc: "splitting results in zero height area on the bottom",
area: image.Rect(1, 1, 2, 2),
heightPerc: 0,
wantTop: image.Rect(1, 1, 2, 2),
wantBot: image.ZR,
},
{
desc: "splitting results in 100 height area on the top",
area: image.Rect(1, 1, 2, 2),
heightPerc: 100,
wantTop: image.ZR,
wantBot: image.Rect(1, 1, 2, 2),
},
{
desc: "splits area with even height",
area: image.Rect(1, 1, 3, 3),
heightPerc: 50,
wantTop: image.Rect(1, 1, 3, 2),
wantBot: image.Rect(1, 2, 3, 3),
},
{
desc: "splits area with odd height",
area: image.Rect(1, 1, 4, 4),
heightPerc: 50,
wantTop: image.Rect(1, 1, 4, 3),
wantBot: image.Rect(1, 3, 4, 4),
},
{
desc: "splits to unequal areas",
area: image.Rect(0, 0, 4, 4),
heightPerc: 25,
wantTop: image.Rect(0, 0, 4, 3),
wantBot: image.Rect(0, 3, 4, 4),
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
gotTop, gotBot, err := HSplitReversed(tc.area, tc.heightPerc)
if (err != nil) != tc.wantErr {
t.Errorf("VSplit => unexpected error:%v, wantErr:%v", err, tc.wantErr)
}
if diff := pretty.Compare(tc.wantTop, gotTop); diff != "" {
t.Errorf("HSplit => first value unexpected diff (-want, +got):\n%s", diff)
}
if diff := pretty.Compare(tc.wantBot, gotBot); diff != "" {
t.Errorf("HSplit => second value unexpected diff (-want, +got):\n%s", diff)
}
})
}
}
func TestVSplit(t *testing.T) {
tests := []struct {
desc string
@ -282,6 +363,90 @@ func TestVSplit(t *testing.T) {
}
}
func TestVSplitReversed(t *testing.T) {
tests := []struct {
desc string
area image.Rectangle
widthPerc int
wantLeft image.Rectangle
wantRight image.Rectangle
wantErr bool
}{
{
desc: "fails on widthPerc too small",
area: image.Rect(1, 1, 2, 2),
widthPerc: -1,
wantErr: true,
},
{
desc: "fails on widthPerc too large",
area: image.Rect(1, 1, 2, 2),
widthPerc: 101,
wantErr: true,
},
{
desc: "zero area to begin with",
area: image.ZR,
widthPerc: 50,
wantLeft: image.ZR,
wantRight: image.ZR,
},
{
desc: "splitting results in zero width area on the right",
area: image.Rect(1, 1, 2, 2),
widthPerc: 0,
wantLeft: image.Rect(1, 1, 2, 2),
wantRight: image.ZR,
},
{
desc: "splitting results in zero width area on the left",
area: image.Rect(1, 1, 2, 2),
widthPerc: 100,
wantLeft: image.ZR,
wantRight: image.Rect(1, 1, 2, 2),
},
{
desc: "splits area with even width",
area: image.Rect(1, 1, 3, 3),
widthPerc: 50,
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),
widthPerc: 50,
wantLeft: image.Rect(1, 1, 3, 4),
wantRight: image.Rect(3, 1, 4, 4),
},
{
desc: "splits to unequal areas",
area: image.Rect(0, 0, 4, 4),
widthPerc: 25,
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 := VSplitReversed(tc.area, tc.widthPerc)
if (err != nil) != tc.wantErr {
t.Errorf("VSplit => unexpected error:%v, wantErr:%v", err, tc.wantErr)
}
if err != nil {
return
}
if diff := pretty.Compare(tc.wantLeft, gotLeft); diff != "" {
t.Errorf("VSplit => left value unexpected diff (-want, +got):\n%s", diff)
}
if diff := pretty.Compare(tc.wantRight, gotRight); diff != "" {
t.Errorf("VSplit => right value unexpected diff (-want, +got):\n%s", diff)
}
})
}
}
func TestVSplitCells(t *testing.T) {
tests := []struct {
desc string
@ -367,6 +532,91 @@ func TestVSplitCells(t *testing.T) {
}
}
func TestVSplitCellsReversed(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.ZR,
wantRight: image.Rect(1, 1, 2, 2),
},
{
desc: "returns area as left on cells equal area width",
area: image.Rect(1, 1, 2, 2),
cells: 1,
wantLeft: image.ZR,
wantRight: image.Rect(1, 1, 2, 2),
},
{
desc: "returns area as right on zero cells",
area: image.Rect(1, 1, 2, 2),
cells: 0,
wantRight: image.ZR,
wantLeft: image.Rect(1, 1, 2, 2),
},
{
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, 3, 4),
wantRight: image.Rect(3, 1, 4, 4),
},
{
desc: "splits to unequal areas",
area: image.Rect(0, 0, 4, 4),
cells: 3,
wantLeft: image.Rect(0, 0, 1, 4),
wantRight: image.Rect(1, 0, 4, 4),
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
gotLeft, gotRight, err := VSplitCellsReversed(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 TestHSplitCells(t *testing.T) {
tests := []struct {
desc string
@ -452,6 +702,91 @@ func TestHSplitCells(t *testing.T) {
}
}
func TestHSplitCellsReversed(t *testing.T) {
tests := []struct {
desc string
area image.Rectangle
cells int
wantTop image.Rectangle
wantBottom image.Rectangle
wantErr bool
}{
{
desc: "fails on negative cells",
area: image.Rect(1, 1, 2, 2),
cells: -1,
wantErr: true,
},
{
desc: "returns area as bottom on cells too large",
area: image.Rect(1, 1, 2, 2),
cells: 2,
wantTop: image.ZR,
wantBottom: image.Rect(1, 1, 2, 2),
},
{
desc: "returns area as bottom on cells equal area width",
area: image.Rect(1, 1, 2, 2),
cells: 1,
wantTop: image.ZR,
wantBottom: image.Rect(1, 1, 2, 2),
},
{
desc: "returns area as top on zero cells",
area: image.Rect(1, 1, 2, 2),
cells: 0,
wantBottom: image.ZR,
wantTop: image.Rect(1, 1, 2, 2),
},
{
desc: "zero area to begin with",
area: image.ZR,
cells: 0,
wantTop: image.ZR,
wantBottom: image.ZR,
},
{
desc: "splits area with even height",
area: image.Rect(1, 1, 3, 3),
cells: 1,
wantTop: image.Rect(1, 1, 3, 2),
wantBottom: image.Rect(1, 2, 3, 3),
},
{
desc: "splits area with odd width",
area: image.Rect(1, 1, 4, 4),
cells: 1,
wantTop: image.Rect(1, 1, 4, 3),
wantBottom: image.Rect(1, 3, 4, 4),
},
{
desc: "splits to unequal areas",
area: image.Rect(0, 0, 4, 4),
cells: 3,
wantTop: image.Rect(0, 0, 4, 1),
wantBottom: image.Rect(0, 1, 4, 4),
},
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
gotTop, gotBottom, err := HSplitCellsReversed(tc.area, tc.cells)
if (err != nil) != tc.wantErr {
t.Errorf("HSplitCells => unexpected error:%v, wantErr:%v", err, tc.wantErr)
}
if err != nil {
return
}
if diff := pretty.Compare(tc.wantTop, gotTop); diff != "" {
t.Errorf("HSplitCells => left value unexpected diff (-want, +got):\n%s", diff)
}
if diff := pretty.Compare(tc.wantBottom, gotBottom); diff != "" {
t.Errorf("HSplitCells => right value unexpected diff (-want, +got):\n%s", diff)
}
})
}
}
func TestExcludeBorder(t *testing.T) {
tests := []struct {
desc string