mirror of
https://github.com/mum4k/termdash.git
synced 2025-04-25 13:48:50 +08:00
472 lines
9.8 KiB
Go
472 lines
9.8 KiB
Go
// Copyright 2019 Google Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package trig
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"testing"
|
|
|
|
"github.com/kylelemons/godebug/pretty"
|
|
)
|
|
|
|
func TestCirclePointAtAngleAndAngle(t *testing.T) {
|
|
tests := []struct {
|
|
degrees int
|
|
mid image.Point
|
|
radius int
|
|
want image.Point
|
|
}{
|
|
{0, image.Point{0, 0}, 1, image.Point{1, 0}},
|
|
{90, image.Point{0, 0}, 1, image.Point{0, -1}},
|
|
{180, image.Point{0, 0}, 1, image.Point{-1, 0}},
|
|
{270, image.Point{0, 0}, 1, image.Point{0, 1}},
|
|
|
|
// Non-zero mid point.
|
|
{0, image.Point{5, 5}, 1, image.Point{6, 5}},
|
|
{90, image.Point{5, 5}, 1, image.Point{5, 4}},
|
|
{180, image.Point{5, 5}, 1, image.Point{4, 5}},
|
|
{270, image.Point{5, 5}, 1, image.Point{5, 6}},
|
|
{0, image.Point{1, 1}, 1, image.Point{2, 1}},
|
|
{90, image.Point{1, 1}, 1, image.Point{1, 0}},
|
|
{180, image.Point{1, 1}, 1, image.Point{0, 1}},
|
|
{270, image.Point{1, 1}, 1, image.Point{1, 2}},
|
|
|
|
// Larger radius.
|
|
{0, image.Point{0, 0}, 11, image.Point{11, 0}},
|
|
{90, image.Point{0, 0}, 11, image.Point{0, -11}},
|
|
{180, image.Point{0, 0}, 11, image.Point{-11, 0}},
|
|
{270, image.Point{0, 0}, 11, image.Point{0, 11}},
|
|
|
|
// Other angles.
|
|
{27, image.Point{0, 0}, 11, image.Point{10, -5}},
|
|
{68, image.Point{0, 0}, 11, image.Point{4, -10}},
|
|
{333, image.Point{2, 2}, 2, image.Point{4, 3}},
|
|
{153, image.Point{2, 2}, 2, image.Point{0, 1}},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(fmt.Sprintf("CirclePointAtAngle %v %v %v", tc.degrees, tc.mid, tc.radius), func(t *testing.T) {
|
|
got := CirclePointAtAngle(tc.degrees, tc.mid, tc.radius)
|
|
if got != tc.want {
|
|
t.Errorf("CirclePointAtAngle(%v, %v, %v) => %v, want %v", tc.degrees, tc.mid, tc.radius, got, tc.want)
|
|
}
|
|
})
|
|
t.Run(fmt.Sprintf("CircleAngleAtPoint %v %v", tc.want, tc.mid), func(t *testing.T) {
|
|
got := CircleAngleAtPoint(tc.want, tc.mid)
|
|
want := tc.degrees
|
|
if got != want {
|
|
t.Errorf("CircleAngleAtPoint(%v, %v) => %v, want %v", tc.want, tc.mid, got, want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestPointIsIn(t *testing.T) {
|
|
tests := []struct {
|
|
desc string
|
|
point image.Point
|
|
shape []image.Point
|
|
want bool
|
|
}{
|
|
{
|
|
desc: "no points provided",
|
|
point: image.Point{0, 0},
|
|
shape: nil,
|
|
want: false,
|
|
},
|
|
{
|
|
desc: "point is on the shape",
|
|
point: image.Point{0, 0},
|
|
shape: []image.Point{
|
|
{0, 0},
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
desc: "point is left of the shape",
|
|
point: image.Point{0, 1},
|
|
shape: []image.Point{
|
|
{1, 0}, {2, 0}, {3, 0},
|
|
{1, 1}, {3, 1},
|
|
{1, 2}, {2, 2}, {3, 2},
|
|
},
|
|
want: false,
|
|
},
|
|
{
|
|
desc: "point is in a shape whose border gets crossed once",
|
|
point: image.Point{2, 1},
|
|
shape: []image.Point{
|
|
{1, 0}, {2, 0}, {3, 0},
|
|
{1, 1}, {3, 1},
|
|
{1, 2}, {2, 2}, {3, 2},
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
desc: "point is in an U shape whose border gets crossed multiple times",
|
|
point: image.Point{1, 1},
|
|
shape: []image.Point{
|
|
{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0},
|
|
{0, 1}, {2, 1}, {4, 1}, {6, 1},
|
|
{0, 2}, {1, 2}, {2, 2}, {3, 2}, {4, 2}, {5, 2},
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
desc: "point is in a triangle",
|
|
point: image.Point{3, 1},
|
|
shape: []image.Point{
|
|
{3, 0},
|
|
{2, 1}, {4, 1},
|
|
{1, 2}, {2, 2}, {3, 2}, {4, 2}, {5, 2},
|
|
},
|
|
want: true,
|
|
},
|
|
{
|
|
desc: "ignores multiple shape points on the same row",
|
|
point: image.Point{2, 1},
|
|
shape: []image.Point{
|
|
{1, 0}, {2, 0}, {3, 0},
|
|
{1, 1}, {3, 1}, {4, 1}, {5, 1},
|
|
{1, 2}, {2, 2}, {3, 2},
|
|
},
|
|
want: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
got := PointIsIn(tc.point, tc.shape)
|
|
if got != tc.want {
|
|
t.Errorf("PointIsIn => %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRangeSize(t *testing.T) {
|
|
tests := []struct {
|
|
desc string
|
|
start int
|
|
end int
|
|
want int
|
|
wantErr bool
|
|
}{
|
|
{
|
|
desc: "invalid start, too small",
|
|
start: MinAngle - 1,
|
|
end: MaxAngle,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid start, too large",
|
|
start: MaxAngle + 1,
|
|
end: MaxAngle,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid end, too small",
|
|
start: MinAngle,
|
|
end: MinAngle - 1,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid end, too large",
|
|
start: MinAngle,
|
|
end: MaxAngle + 1,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "zero range starting at zero",
|
|
start: 0,
|
|
end: 0,
|
|
want: 0,
|
|
},
|
|
{
|
|
desc: "zero range starting at max angle",
|
|
start: 360,
|
|
end: 360,
|
|
want: 0,
|
|
},
|
|
{
|
|
desc: "range with size of one",
|
|
start: 1,
|
|
end: 2,
|
|
want: 1,
|
|
},
|
|
{
|
|
desc: "reverse range with size of 359",
|
|
start: 2,
|
|
end: 1,
|
|
want: 359,
|
|
},
|
|
{
|
|
desc: "range that crosses 360",
|
|
start: 350,
|
|
end: 10,
|
|
want: 20,
|
|
},
|
|
{
|
|
desc: "reverse range that doesn't cross 360",
|
|
start: 10,
|
|
end: 350,
|
|
want: 340,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
got, err := RangeSize(tc.start, tc.end)
|
|
if (err != nil) != tc.wantErr {
|
|
t.Errorf("RangeSize => unexpected error: %v, wantErr: %v", err, tc.wantErr)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if got != tc.want {
|
|
t.Errorf("RangeSize => %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRangeMid(t *testing.T) {
|
|
tests := []struct {
|
|
desc string
|
|
start int
|
|
end int
|
|
want int
|
|
wantErr bool
|
|
}{
|
|
{
|
|
desc: "invalid start, too small",
|
|
start: MinAngle - 1,
|
|
end: MaxAngle,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid start, too large",
|
|
start: MaxAngle + 1,
|
|
end: MaxAngle,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid end, too small",
|
|
start: MinAngle,
|
|
end: MinAngle - 1,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid end, too large",
|
|
start: MinAngle,
|
|
end: MaxAngle + 1,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "zero range",
|
|
start: 0,
|
|
end: 0,
|
|
want: 0,
|
|
},
|
|
{
|
|
desc: "one degree range",
|
|
start: 0,
|
|
end: 1,
|
|
want: 0,
|
|
},
|
|
{
|
|
desc: "three degree range",
|
|
start: 0,
|
|
end: 3,
|
|
want: 1,
|
|
},
|
|
{
|
|
desc: "range that crosses 360, mid isn't 360",
|
|
start: 351,
|
|
end: 11,
|
|
want: 1,
|
|
},
|
|
{
|
|
desc: "range that crosses 360, mid is 360",
|
|
start: 350,
|
|
end: 10,
|
|
want: 0,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
got, err := RangeMid(tc.start, tc.end)
|
|
if (err != nil) != tc.wantErr {
|
|
t.Errorf("RangeMid => unexpected error: %v, wantErr: %v", err, tc.wantErr)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if got != tc.want {
|
|
t.Errorf("RangeMid => %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFilterByAngle(t *testing.T) {
|
|
tests := []struct {
|
|
desc string
|
|
points []image.Point
|
|
mid image.Point
|
|
start int
|
|
end int
|
|
want []image.Point
|
|
wantErr bool
|
|
}{
|
|
{
|
|
desc: "invalid mid, negative X coordinate",
|
|
mid: image.Point{-1, 0},
|
|
start: MinAngle,
|
|
end: MaxAngle,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid mid, negative Y coordinate",
|
|
mid: image.Point{0, -1},
|
|
start: MinAngle,
|
|
end: MaxAngle,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid start, too small",
|
|
start: MinAngle - 1,
|
|
end: MaxAngle,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid start, too large",
|
|
start: MaxAngle + 1,
|
|
end: MaxAngle,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid end, too small",
|
|
start: MinAngle,
|
|
end: MinAngle - 1,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "invalid end, too large",
|
|
start: MinAngle,
|
|
end: MaxAngle + 1,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
desc: "full first quadrant",
|
|
points: []image.Point{
|
|
{0, 0}, {1, 0}, {2, 0},
|
|
{0, 1}, {1, 1}, {2, 1},
|
|
{0, 2}, {1, 2}, {2, 2},
|
|
},
|
|
mid: image.Point{1, 1},
|
|
start: 0,
|
|
end: 90,
|
|
want: []image.Point{
|
|
{1, 0}, {2, 0},
|
|
{1, 1}, {2, 1},
|
|
},
|
|
},
|
|
{
|
|
desc: "partial second quadrant",
|
|
points: []image.Point{
|
|
{0, 0}, {1, 0}, {2, 0},
|
|
{0, 1}, {1, 1}, {2, 1},
|
|
{0, 2}, {1, 2}, {2, 2},
|
|
},
|
|
mid: image.Point{1, 1},
|
|
start: 130,
|
|
end: 140,
|
|
want: []image.Point{
|
|
{0, 0},
|
|
},
|
|
},
|
|
{
|
|
desc: "range crosses 360",
|
|
points: []image.Point{
|
|
{0, 0}, {1, 0}, {2, 0},
|
|
{0, 1}, {1, 1}, {2, 1},
|
|
{0, 2}, {1, 2}, {2, 2},
|
|
},
|
|
mid: image.Point{1, 1},
|
|
start: 310,
|
|
end: 50,
|
|
want: []image.Point{
|
|
{2, 0},
|
|
{1, 1}, {2, 1},
|
|
{2, 2},
|
|
},
|
|
},
|
|
{
|
|
desc: "full circle",
|
|
points: []image.Point{
|
|
{0, 0}, {1, 0}, {2, 0},
|
|
{0, 1}, {1, 1}, {2, 1},
|
|
{0, 2}, {1, 2}, {2, 2},
|
|
},
|
|
mid: image.Point{1, 1},
|
|
start: 0,
|
|
end: 360,
|
|
want: []image.Point{
|
|
{0, 0}, {1, 0}, {2, 0},
|
|
{0, 1}, {1, 1}, {2, 1},
|
|
{0, 2}, {1, 2}, {2, 2},
|
|
},
|
|
},
|
|
{
|
|
desc: "full circle in reverse",
|
|
points: []image.Point{
|
|
{0, 0}, {1, 0}, {2, 0},
|
|
{0, 1}, {1, 1}, {2, 1},
|
|
{0, 2}, {1, 2}, {2, 2},
|
|
},
|
|
mid: image.Point{1, 1},
|
|
start: 360,
|
|
end: 0,
|
|
want: []image.Point{
|
|
{0, 0}, {1, 0}, {2, 0},
|
|
{0, 1}, {1, 1}, {2, 1},
|
|
{0, 2}, {1, 2}, {2, 2},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.desc, func(t *testing.T) {
|
|
got, err := FilterByAngle(tc.points, tc.mid, tc.start, tc.end)
|
|
if (err != nil) != tc.wantErr {
|
|
t.Errorf("FilterByAngle => unexpected error: %v, wantErr: %v", err, tc.wantErr)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if diff := pretty.Compare(tc.want, got); diff != "" {
|
|
t.Errorf("FilterByAngle => unexpected diff (-want, +got):\n%s", diff)
|
|
}
|
|
|
|
})
|
|
}
|
|
}
|