mirror of https://github.com/tidwall/tile38.git
383 lines
9.9 KiB
Go
383 lines
9.9 KiB
Go
|
// Copyright 2018 Joshua J Baker. All rights reserved.
|
||
|
// Use of this source code is governed by an MIT-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
package geometry
|
||
|
|
||
|
import (
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
func seriesForEachSegment(ring Ring, iter func(seg Segment) bool) {
|
||
|
n := ring.NumSegments()
|
||
|
for i := 0; i < n; i++ {
|
||
|
if !iter(ring.SegmentAt(i)) {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func seriesForEachPoint(ring Ring, iter func(point Point) bool) {
|
||
|
n := ring.NumPoints()
|
||
|
for i := 0; i < n; i++ {
|
||
|
if !iter(ring.PointAt(i)) {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSeriesEmpty(t *testing.T) {
|
||
|
var series *baseSeries
|
||
|
expect(t, series.Empty())
|
||
|
series2 := makeSeries(nil, false, false, 0)
|
||
|
expect(t, series2.Empty())
|
||
|
}
|
||
|
|
||
|
func TestSeriesClockwise(t *testing.T) {
|
||
|
var series baseSeries
|
||
|
series = makeSeries([]Point{
|
||
|
P(0, 0), P(10, 0), P(10, 10), P(0, 10), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, !series.Clockwise())
|
||
|
series = makeSeries([]Point{
|
||
|
P(0, 0), P(10, 0), P(10, 10), P(0, 10),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, !series.Clockwise())
|
||
|
series = makeSeries([]Point{
|
||
|
P(0, 0), P(10, 0), P(10, 10),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, !series.Clockwise())
|
||
|
series = makeSeries([]Point{
|
||
|
P(0, 0), P(0, 10), P(10, 10), P(10, 0), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, series.Clockwise())
|
||
|
series = makeSeries([]Point{
|
||
|
P(0, 0), P(0, 10), P(10, 10), P(10, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, series.Clockwise())
|
||
|
series = makeSeries([]Point{
|
||
|
P(0, 0), P(0, 10), P(10, 10),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, series.Clockwise())
|
||
|
}
|
||
|
|
||
|
func TestSeriesConvex(t *testing.T) {
|
||
|
t.Run("1", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(4, 0), P(4, 4), P(0, 4), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, series.Convex())
|
||
|
})
|
||
|
t.Run("2", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(4, 0), P(4, 4), P(3, 4), P(1, 4), P(0, 4), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, series.Convex())
|
||
|
})
|
||
|
t.Run("3", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(4, 0), P(4, 4), P(2, 5), P(0, 4), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, series.Convex())
|
||
|
})
|
||
|
t.Run("4", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(4, 0), P(4, 4),
|
||
|
P(3, 4), P(2, 5), P(1, 4),
|
||
|
P(0, 4), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, !series.Convex())
|
||
|
})
|
||
|
t.Run("5", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(4, 0), P(4, 4),
|
||
|
P(3, 4), P(2, 3), P(1, 4),
|
||
|
P(0, 4), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, !series.Convex())
|
||
|
})
|
||
|
t.Run("5", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(4, 0), P(4, 4), P(0, 4),
|
||
|
P(-1, 2), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, series.Convex())
|
||
|
})
|
||
|
t.Run("6", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(4, 0), P(4, 4), P(0, 4), P(0, 3),
|
||
|
P(-1, 2), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, !series.Convex())
|
||
|
})
|
||
|
t.Run("6", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(4, 0), P(4, 4), P(0, 4),
|
||
|
P(-1, 2), P(0, 1), P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, !series.Convex())
|
||
|
})
|
||
|
t.Run("7", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(4, 0), P(4, 4),
|
||
|
P(3, 3), P(2, 5), P(1, 3), P(0, 4),
|
||
|
P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, !series.Convex())
|
||
|
})
|
||
|
t.Run("8", func(t *testing.T) {
|
||
|
series := makeSeries([]Point{
|
||
|
P(0, 0), P(0, 4), P(1, 3), P(4, 4), P(2, 5), P(3, 3), P(4, 0),
|
||
|
P(0, 0),
|
||
|
}, true, true, DefaultIndex)
|
||
|
expect(t, !series.Convex())
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func testScanSeries(
|
||
|
t *testing.T, series *baseSeries, index bool,
|
||
|
expectSegmentCount int, expectedEmpty bool,
|
||
|
) []Segment {
|
||
|
t.Helper()
|
||
|
if index {
|
||
|
series.buildTree()
|
||
|
} else {
|
||
|
series.tree = nil
|
||
|
}
|
||
|
var segs1 []Segment
|
||
|
seriesForEachSegment(series, func(seg Segment) bool {
|
||
|
segs1 = append(segs1, seg)
|
||
|
return true
|
||
|
})
|
||
|
var segs2Count int
|
||
|
segs2 := make([]Segment, len(segs1))
|
||
|
series.Search(series.Rect(), func(seg Segment, idx int) bool {
|
||
|
segs2[idx] = seg
|
||
|
segs2Count++
|
||
|
return true
|
||
|
})
|
||
|
expect(t, segs2Count == len(segs2))
|
||
|
expect(t, len(segs1) == len(segs2))
|
||
|
if len(segs1) != 0 {
|
||
|
expect(t, reflect.DeepEqual(segs1, segs2))
|
||
|
}
|
||
|
expect(t, expectSegmentCount == len(segs1))
|
||
|
expect(t, series.Empty() == expectedEmpty)
|
||
|
return segs1
|
||
|
}
|
||
|
|
||
|
func TestSeriesBasic(t *testing.T) {
|
||
|
series := makeSeries(octagon, true, true, DefaultIndex)
|
||
|
expect(t, reflect.DeepEqual(seriesCopyPoints(&series), octagon))
|
||
|
expect(t, series.Convex())
|
||
|
expect(t, series.Rect() == R(0, 0, 10, 10))
|
||
|
expect(t, series.Closed())
|
||
|
series = makeSeries(octagon, false, true, DefaultIndex)
|
||
|
expect(t, reflect.DeepEqual(seriesCopyPoints(&series), octagon))
|
||
|
|
||
|
series = makeSeries(ri, true, true, DefaultIndex)
|
||
|
testScanSeries(t, &series, true, len(ri)-1, false)
|
||
|
testScanSeries(t, &series, false, len(ri)-1, false)
|
||
|
|
||
|
// small lines
|
||
|
series = makeSeries([]Point{}, true, false, DefaultIndex)
|
||
|
testScanSeries(t, &series, true, 0, true)
|
||
|
testScanSeries(t, &series, false, 0, true)
|
||
|
|
||
|
series = makeSeries([]Point{P(5, 5)}, true, false, DefaultIndex)
|
||
|
testScanSeries(t, &series, true, 0, true)
|
||
|
testScanSeries(t, &series, false, 0, true)
|
||
|
|
||
|
series = makeSeries([]Point{P(5, 5), P(10, 10)}, true, false, DefaultIndex)
|
||
|
testScanSeries(t, &series, true, 1, false)
|
||
|
testScanSeries(t, &series, false, 1, false)
|
||
|
|
||
|
// small rings
|
||
|
series = makeSeries([]Point{}, true, true, DefaultIndex)
|
||
|
testScanSeries(t, &series, true, 0, true)
|
||
|
testScanSeries(t, &series, false, 0, true)
|
||
|
|
||
|
series = makeSeries([]Point{P(5, 5)}, true, true, DefaultIndex)
|
||
|
testScanSeries(t, &series, true, 0, true)
|
||
|
testScanSeries(t, &series, false, 0, true)
|
||
|
|
||
|
series = makeSeries([]Point{P(5, 5), P(10, 10)}, true, true, DefaultIndex)
|
||
|
testScanSeries(t, &series, true, 0, true)
|
||
|
testScanSeries(t, &series, false, 0, true)
|
||
|
|
||
|
series = makeSeries([]Point{P(5, 5), P(10, 10), P(10, 5)}, true, true, DefaultIndex)
|
||
|
testScanSeries(t, &series, true, 3, false)
|
||
|
testScanSeries(t, &series, false, 3, false)
|
||
|
|
||
|
}
|
||
|
func TestSeriesSearch(t *testing.T) {
|
||
|
series := makeSeries(octagon, true, true, DefaultIndex)
|
||
|
var segs []Segment
|
||
|
series.Search(R(0, 0, 0, 0), func(seg Segment, _ int) bool {
|
||
|
segs = append(segs, seg)
|
||
|
return true
|
||
|
})
|
||
|
segsExpect := []Segment{
|
||
|
S(0, 3, 3, 0),
|
||
|
}
|
||
|
expect(t, checkSegsDups(segsExpect, segs))
|
||
|
segs = nil
|
||
|
series.Search(R(0, 0, 0, 10), func(seg Segment, _ int) bool {
|
||
|
segs = append(segs, seg)
|
||
|
return true
|
||
|
})
|
||
|
segsExpect = []Segment{
|
||
|
S(3, 10, 0, 7),
|
||
|
S(0, 7, 0, 3),
|
||
|
S(0, 3, 3, 0),
|
||
|
}
|
||
|
expect(t, checkSegsDups(segsExpect, segs))
|
||
|
segs = nil
|
||
|
series.Search(R(0, 0, 5, 10), func(seg Segment, _ int) bool {
|
||
|
segs = append(segs, seg)
|
||
|
return true
|
||
|
})
|
||
|
segsExpect = []Segment{
|
||
|
S(3, 0, 7, 0),
|
||
|
S(7, 10, 3, 10),
|
||
|
S(3, 10, 0, 7),
|
||
|
S(0, 7, 0, 3),
|
||
|
S(0, 3, 3, 0),
|
||
|
}
|
||
|
expect(t, checkSegsDups(segsExpect, segs))
|
||
|
var seg2sA []Segment
|
||
|
series.Search(R(0, 0, 10, 10), func(seg Segment, idx int) bool {
|
||
|
seg2sA = append(seg2sA, seg)
|
||
|
return true
|
||
|
})
|
||
|
|
||
|
var seg2sB []Segment
|
||
|
seriesForEachSegment(&series, func(seg Segment) bool {
|
||
|
seg2sB = append(seg2sB, seg)
|
||
|
return true
|
||
|
})
|
||
|
|
||
|
expect(t, checkSegsDups(seg2sA, seg2sB))
|
||
|
|
||
|
var first Segment
|
||
|
var once bool
|
||
|
series.Search(R(0, 0, 10, 10), func(seg Segment, idx int) bool {
|
||
|
expect(t, !once)
|
||
|
first = seg
|
||
|
once = true
|
||
|
return false
|
||
|
})
|
||
|
expect(t, first == seg2sA[0])
|
||
|
|
||
|
}
|
||
|
func TestSeriesBig(t *testing.T) {
|
||
|
t.Run("Closed", func(t *testing.T) {
|
||
|
// clip off the last point to force an auto closure
|
||
|
series := makeSeries(ri[:len(ri)-1], true, true, DefaultIndex)
|
||
|
var seg2sA []Segment
|
||
|
series.Search(series.Rect(), func(seg Segment, idx int) bool {
|
||
|
seg2sA = append(seg2sA, seg)
|
||
|
return true
|
||
|
})
|
||
|
var seg2sB []Segment
|
||
|
seriesForEachSegment(&series, func(seg Segment) bool {
|
||
|
seg2sB = append(seg2sB, seg)
|
||
|
return true
|
||
|
})
|
||
|
|
||
|
expect(t, checkSegsDups(seg2sA, seg2sB))
|
||
|
|
||
|
// use all points
|
||
|
series2 := makeSeries(ri, true, true, DefaultIndex)
|
||
|
var seg2sC []Segment
|
||
|
seriesForEachSegment(&series2, func(seg Segment) bool {
|
||
|
seg2sC = append(seg2sC, seg)
|
||
|
return true
|
||
|
})
|
||
|
expect(t, checkSegsDups(seg2sA, seg2sC))
|
||
|
|
||
|
var first Segment
|
||
|
var once bool
|
||
|
series.Search(series.Rect(), func(seg Segment, idx int) bool {
|
||
|
expect(t, !once)
|
||
|
first = seg
|
||
|
once = true
|
||
|
return false
|
||
|
})
|
||
|
expect(t, first == seg2sA[0])
|
||
|
})
|
||
|
t.Run("Opened", func(t *testing.T) {
|
||
|
series := makeSeries(az, true, false, DefaultIndex)
|
||
|
var seg2sA []Segment
|
||
|
series.Search(series.Rect(), func(seg Segment, idx int) bool {
|
||
|
seg2sA = append(seg2sA, seg)
|
||
|
return true
|
||
|
})
|
||
|
var seg2sB []Segment
|
||
|
seriesForEachSegment(&series, func(seg Segment) bool {
|
||
|
seg2sB = append(seg2sB, seg)
|
||
|
return true
|
||
|
})
|
||
|
expect(t, checkSegsDups(seg2sA, seg2sB))
|
||
|
})
|
||
|
}
|
||
|
func TestSeriesReverse(t *testing.T) {
|
||
|
shapes := [][]Point{octagon, concave1, concave2, concave3, concave4}
|
||
|
for _, shape := range shapes {
|
||
|
var rev []Point
|
||
|
for i := len(shape) - 1; i >= 0; i-- {
|
||
|
rev = append(rev, shape[i])
|
||
|
}
|
||
|
series := makeSeries(rev, true, true, DefaultIndex)
|
||
|
var seg2sA []Segment
|
||
|
series.Search(series.Rect(), func(seg Segment, idx int) bool {
|
||
|
seg2sA = append(seg2sA, seg)
|
||
|
return true
|
||
|
})
|
||
|
var seg2sB []Segment
|
||
|
seriesForEachSegment(&series, func(seg Segment) bool {
|
||
|
seg2sB = append(seg2sB, seg)
|
||
|
return true
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func checkSegsDups(a, b []Segment) bool {
|
||
|
if len(a) != len(b) {
|
||
|
return false
|
||
|
}
|
||
|
for _, a := range a {
|
||
|
var found bool
|
||
|
for _, b := range b {
|
||
|
if a == b {
|
||
|
found = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !found {
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func TestSeriesMove(t *testing.T) {
|
||
|
shapes := [][]Point{ri, octagon}
|
||
|
for _, shape := range shapes {
|
||
|
series := makeSeries(shape, true, true, DefaultIndex)
|
||
|
series.tree = nil
|
||
|
series2 := series.Move(60, 70)
|
||
|
expect(t, series2.NumPoints() == len(shape))
|
||
|
for i := 0; i < len(shape); i++ {
|
||
|
expect(t, series2.PointAt(i) == shape[i].Move(60, 70))
|
||
|
}
|
||
|
series.buildTree()
|
||
|
series2 = series.Move(60, 70)
|
||
|
expect(t, series2.NumPoints() == len(shape))
|
||
|
for i := 0; i < len(shape); i++ {
|
||
|
expect(t, series2.PointAt(i) == shape[i].Move(60, 70))
|
||
|
}
|
||
|
}
|
||
|
}
|