144 lines
5.9 KiB
Go
144 lines
5.9 KiB
Go
|
// CookieJar - A contestant's algorithm toolbox
|
||
|
// Copyright (c) 2013 Peter Szilagyi. All rights reserved.
|
||
|
//
|
||
|
// CookieJar is dual licensed: use of this source code is governed by a BSD
|
||
|
// license that can be found in the LICENSE file. Alternatively, the CookieJar
|
||
|
// toolbox may be used in accordance with the terms and conditions contained
|
||
|
// in a signed written agreement between you and the author(s).
|
||
|
|
||
|
package geometry
|
||
|
|
||
|
import (
|
||
|
"math"
|
||
|
"testing"
|
||
|
)
|
||
|
|
||
|
var nan = math.NaN()
|
||
|
|
||
|
type line2DTest struct {
|
||
|
line *Line2
|
||
|
same *Line2
|
||
|
horiz bool
|
||
|
vert bool
|
||
|
slope float64
|
||
|
intX float64
|
||
|
intY float64
|
||
|
x1 float64
|
||
|
y1 float64
|
||
|
x2 float64
|
||
|
y2 float64
|
||
|
}
|
||
|
|
||
|
var line2DTests = []line2DTest{
|
||
|
// Variations crossing (-2, 0) and (0, 2)
|
||
|
{new(Line2).SetCanon(1, -1, 2), new(Line2).SetCanon(1, -1, 2), false, false, 1, -2, 2, -1, 1, -1, 1},
|
||
|
{new(Line2).SetCanon(1, -1, 2), new(Line2).SetCanon(2, -2, 4), false, false, 1, -2, 2, -1, 1, -1, 1},
|
||
|
{new(Line2).SetCanon(1, -1, 2), new(Line2).SetSlope(1, 2), false, false, 1, -2, 2, -1, 1, -1, 1},
|
||
|
{new(Line2).SetCanon(1, -1, 2), new(Line2).SetPoint(&Point2{-3, -1}, &Point2{1, 3}), false, false, 1, -2, 2, -1, 1, -1, 1},
|
||
|
|
||
|
// Variations crossing (0, 2) and (2, 0)
|
||
|
{new(Line2).SetCanon(1, 1, -2), new(Line2).SetCanon(1, 1, -2), false, false, -1, 2, 2, 1, 1, 1, 1},
|
||
|
{new(Line2).SetCanon(1, 1, -2), new(Line2).SetCanon(2, 2, -4), false, false, -1, 2, 2, 1, 1, 1, 1},
|
||
|
{new(Line2).SetCanon(1, 1, -2), new(Line2).SetSlope(-1, 2), false, false, -1, 2, 2, 1, 1, 1, 1},
|
||
|
{new(Line2).SetCanon(1, 1, -2), new(Line2).SetPoint(&Point2{-1, 3}, &Point2{1, 1}), false, false, -1, 2, 2, 1, 1, 1, 1},
|
||
|
|
||
|
// Horizontal variations
|
||
|
{new(Line2).SetCanon(0, 1, 0), new(Line2).SetCanon(0, 1, 0), true, false, 0, nan, 0, 0, 0, nan, 0},
|
||
|
{new(Line2).SetCanon(0, 1, 0), new(Line2).SetCanon(0, 2, 0), true, false, 0, nan, 0, 0, 0, nan, 0},
|
||
|
{new(Line2).SetCanon(0, 1, 0), new(Line2).SetSlope(0, 0), true, false, 0, nan, 0, 0, 0, nan, 0},
|
||
|
{new(Line2).SetCanon(0, 1, 0), new(Line2).SetPoint(&Point2{-1, 0}, &Point2{1, 0}), true, false, 0, nan, 0, 0, 0, nan, 0},
|
||
|
|
||
|
// Vertical variations
|
||
|
{new(Line2).SetCanon(1, 0, 0), new(Line2).SetCanon(1, 0, 0), false, true, nan, 0, nan, 0, nan, 0, 0},
|
||
|
{new(Line2).SetCanon(1, 0, 0), new(Line2).SetCanon(2, 0, 0), false, true, nan, 0, nan, 0, nan, 0, 0},
|
||
|
{new(Line2).SetCanon(1, 0, 0), new(Line2).SetPoint(&Point2{0, -1}, &Point2{0, 1}), false, true, nan, 0, nan, 0, nan, 0, 0},
|
||
|
}
|
||
|
|
||
|
type intersect2DTest struct {
|
||
|
l1 *Line2
|
||
|
l2 *Line2
|
||
|
par bool
|
||
|
per bool
|
||
|
cross *Point2
|
||
|
}
|
||
|
|
||
|
var intersect2DTests = []intersect2DTest{
|
||
|
// Parallel lines (horizontal, vertical, diagonals)
|
||
|
{new(Line2).SetCanon(0, 1, 0), new(Line2).SetCanon(0, 1, 0), true, false, nil},
|
||
|
{new(Line2).SetCanon(0, 1, 0), new(Line2).SetCanon(0, 2, 0), true, false, nil},
|
||
|
{new(Line2).SetCanon(1, 0, 0), new(Line2).SetCanon(1, 0, 0), true, false, nil},
|
||
|
{new(Line2).SetCanon(1, 0, 0), new(Line2).SetCanon(2, 0, 0), true, false, nil},
|
||
|
{new(Line2).SetSlope(1, 0), new(Line2).SetSlope(1, 0), true, false, nil},
|
||
|
{new(Line2).SetSlope(1, 0), new(Line2).SetSlope(1, 1), true, false, nil},
|
||
|
{new(Line2).SetSlope(-1, 0), new(Line2).SetSlope(-1, 0), true, false, nil},
|
||
|
{new(Line2).SetSlope(-1, 0), new(Line2).SetSlope(-1, 1), true, false, nil},
|
||
|
|
||
|
// Perpendicular lines
|
||
|
{new(Line2).SetPoint(&Point2{-1, 0}, &Point2{1, 0}), new(Line2).SetPoint(&Point2{0, -1}, &Point2{0, 1}), false, true, &Point2{0, 0}},
|
||
|
{new(Line2).SetPoint(&Point2{1, -1}, &Point2{1, 1}), new(Line2).SetPoint(&Point2{-1, 1}, &Point2{1, 1}), false, true, &Point2{1, 1}},
|
||
|
{new(Line2).SetPoint(&Point2{1, 0}, &Point2{5, 4}), new(Line2).SetPoint(&Point2{1, 4}, &Point2{5, 0}), false, true, &Point2{3, 2}},
|
||
|
|
||
|
// Simple lines
|
||
|
{new(Line2).SetPoint(&Point2{0, 0}, &Point2{1, 1}), new(Line2).SetPoint(&Point2{0, 2}, &Point2{1, -1}), false, false, &Point2{0.5, 0.5}},
|
||
|
}
|
||
|
|
||
|
// Slightly modified comparer to allow NaN-NaN comaprisons
|
||
|
func equal(a, b float64) bool {
|
||
|
if math.IsNaN(a) && math.IsNaN(b) {
|
||
|
return true
|
||
|
}
|
||
|
if !math.IsNaN(a) && !math.IsNaN(b) {
|
||
|
return math.Abs(a-b) < eps
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func TestLine2D(t *testing.T) {
|
||
|
for i, tt := range line2DTests {
|
||
|
if !tt.line.Equal(tt.same) {
|
||
|
t.Errorf("test %d: equality mismatch: %v and %v.", i, tt.line, tt.same)
|
||
|
}
|
||
|
if res := tt.line.Horizontal(); res != tt.horiz {
|
||
|
t.Errorf("test %d: failed horizontality check: have %v, want %v.", i, res, tt.horiz)
|
||
|
}
|
||
|
if res := tt.line.Vertical(); res != tt.vert {
|
||
|
t.Errorf("test %d: failed verticality check: have %v, want %v.", i, res, tt.horiz)
|
||
|
}
|
||
|
if res := tt.line.Slope(); !equal(res, tt.slope) {
|
||
|
t.Errorf("test %d: slope mismatch: have %v, want %v.", i, res, tt.slope)
|
||
|
}
|
||
|
if res := tt.line.InterceptX(); !equal(res, tt.intX) {
|
||
|
t.Errorf("test %d: x intercept mismatch: have %v, want %v.", i, res, tt.intX)
|
||
|
}
|
||
|
if res := tt.line.InterceptY(); !equal(res, tt.intY) {
|
||
|
t.Errorf("test %d: y intercept mismatch: have %v, want %v.", i, res, tt.intY)
|
||
|
}
|
||
|
if res := tt.line.Y(tt.x1); !equal(res, tt.y1) {
|
||
|
t.Errorf("test %d: image mismatch: have %v, want %v.", i, res, tt.y1)
|
||
|
}
|
||
|
if res := tt.line.X(tt.y2); !equal(res, tt.x2) {
|
||
|
t.Errorf("test %d: point mismatch: have %v, want %v.", i, res, tt.x2)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestIntersect2D(t *testing.T) {
|
||
|
for i, tt := range intersect2DTests {
|
||
|
if res := tt.l1.Parallel(tt.l2); res != tt.par {
|
||
|
t.Errorf("test %d: parallelism mismatch: have %v, want %v.", i, res, tt.par)
|
||
|
}
|
||
|
if res := tt.l1.Perpendicular(tt.l2); res != tt.per {
|
||
|
t.Errorf("test %d: perpendicularity mismatch: have %v, want %v.", i, res, tt.per)
|
||
|
}
|
||
|
cross := tt.l1.Intersect(tt.l2)
|
||
|
switch {
|
||
|
case cross == nil && tt.cross != nil:
|
||
|
t.Errorf("test %d: intersection not found: have %v, want %v.", i, cross, tt.cross)
|
||
|
case cross != nil && tt.cross == nil:
|
||
|
t.Errorf("test %d: non-exostent intersection: have %v, want %v.", i, cross, tt.cross)
|
||
|
case cross != nil && tt.cross != nil && !cross.Equal(tt.cross):
|
||
|
t.Errorf("test %d: intersection mismatch: have %v, want %v.", i, cross, tt.cross)
|
||
|
}
|
||
|
}
|
||
|
}
|