clockface: add hours
This commit is contained in:
parent
c6ef156d88
commit
6837cd9fc1
@ -14,6 +14,7 @@ type Point struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
hourHandLength = 50
|
||||||
minuteHandLength = 80
|
minuteHandLength = 80
|
||||||
secondHandLength = 90
|
secondHandLength = 90
|
||||||
clockCentreX = 150
|
clockCentreX = 150
|
||||||
@ -37,6 +38,7 @@ func SVGWriter(w io.Writer, t time.Time) {
|
|||||||
io.WriteString(w, bezel)
|
io.WriteString(w, bezel)
|
||||||
secondHand(w, t)
|
secondHand(w, t)
|
||||||
minuteHand(w, t)
|
minuteHand(w, t)
|
||||||
|
hourHand(w, t)
|
||||||
io.WriteString(w, svgEnd)
|
io.WriteString(w, svgEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +54,12 @@ func minuteHand(w io.Writer, t time.Time) {
|
|||||||
makeHand(w, minuteHandLength, p)
|
makeHand(w, minuteHandLength, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MinuteHand is the unit vector of the minute hand of an analogue clock at the time `t` represented as a Point
|
||||||
|
func hourHand(w io.Writer, t time.Time) {
|
||||||
|
p := hourHandPoint(t)
|
||||||
|
makeHand(w, hourHandLength, p)
|
||||||
|
}
|
||||||
|
|
||||||
func makeHand(w io.Writer, length float64, p Point) {
|
func makeHand(w io.Writer, length float64, p Point) {
|
||||||
p = Point{p.X * length, p.Y * length} // scale
|
p = Point{p.X * length, p.Y * length} // scale
|
||||||
p = Point{p.X, -p.Y} // flip
|
p = Point{p.X, -p.Y} // flip
|
||||||
@ -64,6 +72,14 @@ func makeHand(w io.Writer, length float64, p Point) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hoursInRadians(t time.Time) float64 {
|
||||||
|
return (minutesInRadians(t) / 12) + math.Pi/(6/float64(t.Hour()%12))
|
||||||
|
}
|
||||||
|
|
||||||
|
func hourHandPoint(t time.Time) Point {
|
||||||
|
return angleToPoint(hoursInRadians(t))
|
||||||
|
}
|
||||||
|
|
||||||
func minutesInRadians(t time.Time) float64 {
|
func minutesInRadians(t time.Time) float64 {
|
||||||
return (secondsInRadians(t) / 60) + math.Pi/(30/float64(t.Minute()))
|
return (secondsInRadians(t) / 60) + math.Pi/(30/float64(t.Minute()))
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,6 @@ func TestSVGWriterMinuteHand(t *testing.T) {
|
|||||||
line Line
|
line Line
|
||||||
}{
|
}{
|
||||||
{simpleTime(0, 0, 0), Line{150, 150, 150, 70}},
|
{simpleTime(0, 0, 0), Line{150, 150, 150, 70}},
|
||||||
// {simpleTime(0, 0, 30), Line{150, 150, 150, 240}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
@ -87,6 +86,33 @@ func TestSVGWriterMinuteHand(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSVGWriterHourHand(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
time time.Time
|
||||||
|
line Line
|
||||||
|
}{
|
||||||
|
{simpleTime(6, 0, 0), Line{150, 150, 150, 200}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(testName(c.time), func(t *testing.T) {
|
||||||
|
b := bytes.Buffer{}
|
||||||
|
SVGWriter(&b, c.time)
|
||||||
|
|
||||||
|
svg := Svg{}
|
||||||
|
xml.Unmarshal(b.Bytes(), &svg)
|
||||||
|
|
||||||
|
if !containsLine(c.line, svg.Line) {
|
||||||
|
t.Errorf(
|
||||||
|
"Expected to find the hour hand line %+v in the SVG lines %v",
|
||||||
|
c.line,
|
||||||
|
svg.Line,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func containsLine(l Line, ls []Line) bool {
|
func containsLine(l Line, ls []Line) bool {
|
||||||
for _, line := range ls {
|
for _, line := range ls {
|
||||||
if line == l {
|
if line == l {
|
||||||
|
@ -6,6 +6,47 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestHoursInRadians(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
time time.Time
|
||||||
|
angle float64
|
||||||
|
}{
|
||||||
|
{simpleTime(6, 0, 0), math.Pi},
|
||||||
|
{simpleTime(0, 0, 0), 0},
|
||||||
|
{simpleTime(21, 0, 0), math.Pi * 1.5},
|
||||||
|
{simpleTime(0, 1, 30), math.Pi / ((6 * 60 * 60) / 90)},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(testName(c.time), func(t *testing.T) {
|
||||||
|
got := hoursInRadians(c.time)
|
||||||
|
|
||||||
|
if !roughlyEqualFloat64(c.angle, got) {
|
||||||
|
t.Fatalf("Wanted %v radians, but got %v", c.angle, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHourHandPoint(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
time time.Time
|
||||||
|
point Point
|
||||||
|
}{
|
||||||
|
{simpleTime(6, 0, 0), Point{0, -1}},
|
||||||
|
{simpleTime(9, 0, 0), Point{-1, 0}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(testName(c.time), func(t *testing.T) {
|
||||||
|
got := hourHandPoint(c.time)
|
||||||
|
if !roughlyEqualPoint(got, c.point) {
|
||||||
|
t.Fatalf("Wanted %v Point, but got %v", c.point, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMinutesInRadians(t *testing.T) {
|
func TestMinutesInRadians(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
time time.Time
|
time time.Time
|
||||||
@ -26,28 +67,6 @@ func TestMinutesInRadians(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 TestMinuteHandPoint(t *testing.T) {
|
func TestMinuteHandPoint(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
time time.Time
|
time time.Time
|
||||||
@ -67,6 +86,28 @@ func TestMinuteHandPoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
func TestSecondHandPoint(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
time time.Time
|
time time.Time
|
||||||
|
Loading…
Reference in New Issue
Block a user