diff --git a/clockface.go b/clockface.go
index 642b645..00b9b97 100644
--- a/clockface.go
+++ b/clockface.go
@@ -14,9 +14,21 @@ func secondsInRadians(t time.Time) float64 {
return math.Pi / (30 / (float64(t.Second())))
}
-func secondHandPoint(t time.Time) Point {
- angle := secondsInRadians(t)
+func minutesInRadians(t time.Time) float64 {
+ return secondsInRadians(t)/60 + math.Pi/(30/(float64(t.Minute())))
+}
+
+func angleToPoint(angle float64) Point {
x := math.Sin(angle)
y := math.Cos(angle)
+
return Point{x, y}
}
+
+func secondHandPoint(t time.Time) Point {
+ return angleToPoint(secondsInRadians(t))
+}
+
+func minuteHandPoint(t time.Time) Point {
+ return angleToPoint(minutesInRadians(t))
+}
diff --git a/clockface_acceptance_test.go b/clockface_acceptance_test.go
index 24e804b..ea89920 100644
--- a/clockface_acceptance_test.go
+++ b/clockface_acceptance_test.go
@@ -7,49 +7,84 @@ import (
"time"
)
-type Svg struct {
+type SVG struct {
XMLName xml.Name `xml:"svg"`
- Text string `xml:",chardata"`
Xmlns string `xml:"xmlns,attr"`
Width string `xml:"width,attr"`
Height string `xml:"height,attr"`
ViewBox string `xml:"viewBox,attr"`
Version string `xml:"version,attr"`
- Circle struct {
- Text string `xml:",chardata"`
- Cx string `xml:"cx,attr"`
- Cy string `xml:"cy,attr"`
- R string `xml:"r,attr"`
- Style string `xml:"style,attr"`
- } `xml:"circle"`
- Line []struct {
- Text string `xml:",chardata"`
- X1 string `xml:"x1,attr"`
- Y1 string `xml:"y1,attr"`
- X2 string `xml:"x2,attr"`
- Y2 string `xml:"y2,attr"`
- Style string `xml:"style,attr"`
- } `xml:"line"`
+ Circle Circle `xml:"circle"`
+ Line []Line `xml:"line"`
+}
+
+type Circle struct {
+ Cx float64 `xml:"cx,attr"`
+ Cy float64 `xml:"cy,attr"`
+ R float64 `xml:"r,attr"`
+}
+
+type Line struct {
+ X1 float64 `xml:"x1,attr"`
+ Y1 float64 `xml:"y1,attr"`
+ X2 float64 `xml:"x2,attr"`
+ Y2 float64 `xml:"y2,attr"`
+}
+
+func containsLine(l Line, ls []Line) bool {
+ for _, line := range ls {
+ if line == l {
+ return true
+ }
+ }
+ return false
}
func TestSVGWriterAtMidnight(t *testing.T) {
- tm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)
- b := bytes.Buffer{}
-
- SVGWriter(&b, tm)
-
- svg := Svg{}
-
- xml.Unmarshal(b.Bytes(), &svg)
-
- x2 := "150.000"
- y2 := "60.000"
-
- for _, line := range svg.Line {
- if line.X2 == x2 && line.Y2 == y2 {
- return
- }
+ cases := []struct {
+ time time.Time
+ line Line
+ }{
+ {simpleTime(0, 0, 0),
+ Line{150, 150, 150, 60}},
+ {simpleTime(0, 0, 30),
+ Line{150, 150, 150, 240}},
}
- t.Errorf("Expected to find the second hand with x2 of %v and y2 of %v, in the SVG output %v", x2, y2, b.String())
+ 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 second hand line %+v, in the SVG lines %+v", c.line, svg.Line)
+ }
+ })
+ }
+}
+func TestSVGWriterMinuteHand(t *testing.T) {
+ cases := []struct {
+ time time.Time
+ line Line
+ }{
+ {simpleTime(0, 0, 0),
+ Line{150, 150, 150, 70}},
+ }
+
+ 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 second hand line %+v, in the SVG lines %+v", c.line, svg.Line)
+ }
+ })
+ }
}
diff --git a/clockface_test.go b/clockface_test.go
index 6bed993..47beb94 100644
--- a/clockface_test.go
+++ b/clockface_test.go
@@ -27,6 +27,25 @@ func TestSecondsInRadians(t *testing.T) {
}
}
+func TestMinutesInRadians(t *testing.T) {
+ cases := []struct {
+ time time.Time
+ angle float64
+ }{
+ {simpleTime(0, 30, 0), math.Pi},
+ {simpleTime(0, 0, 7), 7 * (math.Pi / (30 * 60))},
+ }
+
+ for _, c := range cases {
+ t.Run(testName(c.time), func(t *testing.T) {
+ got := minutesInRadians(c.time)
+ if got != c.angle {
+ t.Fatalf("Wanted %v radians, but got %v", c.angle, got)
+ }
+ })
+ }
+}
+
func TestSecondHandPoint(t *testing.T) {
cases := []struct {
time time.Time
@@ -46,6 +65,25 @@ func TestSecondHandPoint(t *testing.T) {
}
}
+func TestMinuteHandPoint(t *testing.T) {
+ cases := []struct {
+ time time.Time
+ point Point
+ }{
+ {simpleTime(0, 30, 0), Point{0, -1}},
+ {simpleTime(0, 45, 0), Point{-1, 0}},
+ }
+
+ for _, c := range cases {
+ t.Run(testName(c.time), func(t *testing.T) {
+ got := minuteHandPoint(c.time)
+ if !roughlyEqualPoint(got, c.point) {
+ t.Fatalf("Wanted %v Point, but got %v", c.point, got)
+ }
+ })
+ }
+}
+
func roughlyEqualFloat64(a, b float64) bool {
const equalityThreshold = 1e-7
return math.Abs(a-b) < equalityThreshold
diff --git a/svgWriter.go b/svgWriter.go
index 5143bf6..dc697c0 100644
--- a/svgWriter.go
+++ b/svgWriter.go
@@ -7,6 +7,7 @@ import (
)
const secondHandLength = 90
+const minuteHandLength = 80
const clockCentreX = 150
const clockCentreY = 150
@@ -27,13 +28,22 @@ func SVGWriter(w io.Writer, t time.Time) {
io.WriteString(w, svgStart)
io.WriteString(w, bezel)
SecondHand(w, t)
+ MinuteHand(w, t)
io.WriteString(w, svgEnd)
}
+func makeHand(p Point, length float64) Point {
+ p = Point{p.X * length, p.Y * length}
+ p = Point{p.X, -p.Y}
+ return Point{p.X + clockCentreX, p.Y + clockCentreY}
+}
+
func SecondHand(w io.Writer, t time.Time) {
- p := secondHandPoint(t)
- p = Point{p.X * secondHandLength, p.Y * secondHandLength} //scale
- p = Point{p.X, -p.Y} //flip
- p = Point{p.X + clockCentreX, p.Y + clockCentreY} //translate
+ p := makeHand(secondHandPoint(t), secondHandLength)
fmt.Fprintf(w, ``, p.X, p.Y)
}
+
+func MinuteHand(w io.Writer, t time.Time) {
+ p := makeHand(minuteHandPoint(t), minuteHandLength)
+ fmt.Fprintf(w, ``, p.X, p.Y)
+}