Skip to content

Commit 26da2ce

Browse files
committed
✨ (clockface): add hourHand implementation
1 parent 601ef0d commit 26da2ce

File tree

4 files changed

+100
-8
lines changed

4 files changed

+100
-8
lines changed

clockface_sample/clockface/clockface.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@ import (
55
"time"
66
)
77

8+
const (
9+
secondsInHalfClock = 30
10+
secondsInClock = 2 * secondsInHalfClock
11+
minutesInHalfClock = 30
12+
minutesInClock = 2 * minutesInHalfClock
13+
hoursInHalfClock = 6
14+
hoursInClock = 2 * hoursInHalfClock
15+
)
16+
817
// A Point represents a two-dimensional Cartesian coordinate
918
type Point struct {
1019
X float64
1120
Y float64
1221
}
1322

14-
const secondHandLength = 90
15-
const clockCentreX = 150
16-
const clockCentreY = 150
17-
1823
// SecondHand is the unit vector of the second hand of an analogue clock at time `t`
1924
// represented as a Point.
2025
func SecondHand(t time.Time) Point {
@@ -26,16 +31,16 @@ func SecondHand(t time.Time) Point {
2631
}
2732

2833
func secondsInRadians(t time.Time) float64 {
29-
return (math.Pi / (30 / (float64(t.Second()))))
34+
return (math.Pi / (secondsInHalfClock / (float64(t.Second()))))
3035
}
3136

3237
func secondHandPoint(t time.Time) Point {
3338
return angleToPoint(secondsInRadians(t))
3439
}
3540

3641
func minutesInRadians(t time.Time) float64 {
37-
return (secondsInRadians(t) / 60) +
38-
(math.Pi / (30 / float64(t.Minute())))
42+
return (secondsInRadians(t) / minutesInClock) +
43+
(math.Pi / (minutesInHalfClock / float64(t.Minute())))
3944
}
4045

4146
func angleToPoint(angle float64) Point {
@@ -49,4 +54,11 @@ func minuteHandPoint(t time.Time) Point {
4954
return angleToPoint(minutesInRadians(t))
5055
}
5156

52-
const minuteHandLength = 80
57+
func hoursInRadians(t time.Time) float64 {
58+
return (minutesInRadians(t) / hoursInClock) +
59+
(math.Pi / (hoursInHalfClock / float64(t.Hour()%hoursInClock)))
60+
}
61+
62+
func hourHandPoint(t time.Time) Point {
63+
return angleToPoint(hoursInRadians(t))
64+
}

clockface_sample/clockface/clockface_acceptance_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,29 @@ func TestSVGWriterMinuteHand(t *testing.T) {
126126
})
127127
}
128128
}
129+
130+
func TestSVGWriterHourHand(t *testing.T) {
131+
cases := []struct {
132+
time time.Time
133+
line Line
134+
}{
135+
{
136+
simpleTime(6, 0, 0),
137+
Line{150, 150, 150, 200},
138+
},
139+
}
140+
141+
for _, c := range cases {
142+
t.Run(testName(c.time), func(t *testing.T) {
143+
b := bytes.Buffer{}
144+
clockface.SVGWriter(&b, c.time)
145+
146+
svg := SVG{}
147+
xml.Unmarshal(b.Bytes(), &svg)
148+
149+
if !containsLine(c.line, svg.Line) {
150+
t.Errorf("Expected to find the hour hand line %+v, in the SVG lines %+v", c.line, svg.Line)
151+
}
152+
})
153+
}
154+
}

clockface_sample/clockface/clockface_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,43 @@ func TestMinuteHandPoint(t *testing.T) {
100100
})
101101
}
102102
}
103+
104+
func TestHoursInRadians(t *testing.T) {
105+
cases := []struct {
106+
time time.Time
107+
angle float64
108+
}{
109+
{simpleTime(6, 0, 0), math.Pi},
110+
{simpleTime(0, 0, 0), 0},
111+
{simpleTime(21, 0, 0), math.Pi * 1.5},
112+
{simpleTime(0, 1, 30), math.Pi / ((6 * 60 * 60) / 90)},
113+
}
114+
115+
for _, c := range cases {
116+
t.Run(testName(c.time), func(t *testing.T) {
117+
got := hoursInRadians(c.time)
118+
if !roughlyEqualFloat64(got, c.angle) {
119+
t.Fatalf("Wanted %v radians, but got %v", c.angle, got)
120+
}
121+
})
122+
}
123+
}
124+
125+
func TestHourHandPoint(t *testing.T) {
126+
cases := []struct {
127+
time time.Time
128+
point Point
129+
}{
130+
{simpleTime(6, 0, 0), Point{0, -1}},
131+
{simpleTime(21, 0, 0), Point{-1, 0}},
132+
}
133+
134+
for _, c := range cases {
135+
t.Run(testName(c.time), func(t *testing.T) {
136+
got := hourHandPoint(c.time)
137+
if !roughlyEqualPoint(got, c.point) {
138+
t.Fatalf("Wanted %v Point, but got %v", c.point, got)
139+
}
140+
})
141+
}
142+
}

clockface_sample/clockface/svgWriter.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,21 @@ import (
66
"time"
77
)
88

9+
const (
10+
secondHandLength = 90
11+
minuteHandLength = 80
12+
hourHandLength = 50
13+
clockCentreX = 150
14+
clockCentreY = 150
15+
)
16+
917
// SVGWriter writes an SVG representation of an analogue clock, showing the time t, to the writer w.
1018
func SVGWriter(w io.Writer, t time.Time) {
1119
io.WriteString(w, svgStart)
1220
io.WriteString(w, bezel)
1321
secondHand(w, t)
1422
minuteHand(w, t)
23+
hourHand(w, t)
1524
io.WriteString(w, svgEnd)
1625
}
1726

@@ -43,3 +52,8 @@ const svgStart = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
4352
const bezel = `<circle cx="150" cy="150" r="100" style="fill:#fff;stroke:#000;stroke-width:5px;"/>`
4453

4554
const svgEnd = `</svg>`
55+
56+
func hourHand(w io.Writer, t time.Time) {
57+
p := makeHand(hourHandPoint(t), hourHandLength)
58+
fmt.Fprintf(w, `<line x1="150" y1="150" x2="%.3f" y2="%.3f" style="fill:none;stroke:#000;stroke-width:3px;"/>`, p.X, p.Y)
59+
}

0 commit comments

Comments
 (0)