clockface: part1
This commit is contained in:
parent
8629b537b0
commit
c9c9b9bf7b
37
clockface/clockface.go
Normal file
37
clockface/clockface.go
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package clockface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Point represents a two-dimentional Cartesian coordinate
|
||||||
|
type Point struct {
|
||||||
|
X float64
|
||||||
|
Y float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecondHand is the unit vector of the second hand of an analogue clock at the time `t` represented as a Point
|
||||||
|
func SecondHand(t time.Time) Point {
|
||||||
|
p := secondHandPoint(t)
|
||||||
|
p = Point{p.X * 90, p.Y * 90} // scale
|
||||||
|
p = Point{p.X, -p.Y} // flip
|
||||||
|
p = Point{p.X + 150, p.Y + 150} // translate
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func secondsInRadians(t time.Time) float64 {
|
||||||
|
// XXX:Wanted 3.141592653589793 radians, but got 3.1415926535897936
|
||||||
|
// return float64(t.Second()) * (math.Pi / 30)
|
||||||
|
|
||||||
|
return math.Pi / (30 / float64(t.Second()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func secondHandPoint(t time.Time) Point {
|
||||||
|
angle := secondsInRadians(t)
|
||||||
|
x := math.Sin(angle)
|
||||||
|
y := math.Cos(angle)
|
||||||
|
|
||||||
|
// XXX: Wanted {0 -1} Point, but got {1.2246467991473515e-16 -1}
|
||||||
|
return Point{x, y}
|
||||||
|
}
|
28
clockface/clockface_acceptance_test.go
Normal file
28
clockface/clockface_acceptance_test.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package clockface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSecondHandAtMidnight(t *testing.T) {
|
||||||
|
tm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
|
want := Point{X: 150, Y: 150 - 90}
|
||||||
|
got := SecondHand(tm)
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("Got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecondHandAt30Seconds(t *testing.T) {
|
||||||
|
tm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)
|
||||||
|
|
||||||
|
want := Point{X: 150, Y: 150 + 90}
|
||||||
|
got := SecondHand(tm)
|
||||||
|
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("Got %v, wanted %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
65
clockface/clockface_test.go
Normal file
65
clockface/clockface_test.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package clockface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSecondsInRadians(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
time time.Time
|
||||||
|
angle float64
|
||||||
|
}{
|
||||||
|
{simpleTime(0, 0, 30), math.Pi},
|
||||||
|
{simpleTime(0, 0, 0), 0},
|
||||||
|
{simpleTime(0, 0, 45), (math.Pi / 2) * 3},
|
||||||
|
{simpleTime(0, 0, 7), (math.Pi / 30) * 7},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(testName(c.time), func(t *testing.T) {
|
||||||
|
got := secondsInRadians(c.time)
|
||||||
|
|
||||||
|
if c.angle != got {
|
||||||
|
t.Fatalf("Wanted %v radians, but got %v", c.angle, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecondHandPoint(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
time time.Time
|
||||||
|
point Point
|
||||||
|
}{
|
||||||
|
{simpleTime(0, 0, 30), Point{0, -1}},
|
||||||
|
{simpleTime(0, 0, 45), Point{-1, 0}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(testName(c.time), func(t *testing.T) {
|
||||||
|
got := secondHandPoint(c.time)
|
||||||
|
if !roughlyEqualPoint(got, c.point) {
|
||||||
|
t.Fatalf("Wanted %v Point, but got %v", c.point, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func simpleTime(hours, minutes, seconds int) time.Time {
|
||||||
|
return time.Date(321, time.October, 28, hours, minutes, seconds, 0, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testName(t time.Time) string {
|
||||||
|
return t.Format("15:04:05")
|
||||||
|
}
|
||||||
|
|
||||||
|
func roughlyEqualFloat64(a, b float64) bool {
|
||||||
|
const equalityThreshold = 1e-7
|
||||||
|
return math.Abs(a-b) < equalityThreshold
|
||||||
|
}
|
||||||
|
|
||||||
|
func roughlyEqualPoint(a, b Point) bool {
|
||||||
|
return roughlyEqualFloat64(a.X, b.X) && roughlyEqualFloat64(a.Y, b.Y)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user