Added hour hand
This commit is contained in:
parent
03e76a5a25
commit
92c45828d9
21
clockface.go
21
clockface.go
@ -5,17 +5,30 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
secondsInHalfClock = 30
|
||||||
|
secondsInClock = 2 * secondsInHalfClock
|
||||||
|
minutesInHalfClock = 30
|
||||||
|
minutesInClock = 2 * minutesInHalfClock
|
||||||
|
hoursInHalfClock = 6
|
||||||
|
hoursInClock = 2 * hoursInHalfClock
|
||||||
|
)
|
||||||
|
|
||||||
type Point struct {
|
type Point struct {
|
||||||
X float64
|
X float64
|
||||||
Y float64
|
Y float64
|
||||||
}
|
}
|
||||||
|
|
||||||
func secondsInRadians(t time.Time) float64 {
|
func secondsInRadians(t time.Time) float64 {
|
||||||
return math.Pi / (30 / (float64(t.Second())))
|
return math.Pi / (secondsInHalfClock / (float64(t.Second())))
|
||||||
}
|
}
|
||||||
|
|
||||||
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)/minutesInClock + math.Pi/(minutesInHalfClock/(float64(t.Minute())))
|
||||||
|
}
|
||||||
|
|
||||||
|
func hoursInRadians(t time.Time) float64 {
|
||||||
|
return (minutesInRadians(t) / hoursInClock) + (math.Pi / (hoursInHalfClock / float64(t.Hour()%hoursInClock)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func angleToPoint(angle float64) Point {
|
func angleToPoint(angle float64) Point {
|
||||||
@ -32,3 +45,7 @@ func secondHandPoint(t time.Time) Point {
|
|||||||
func minuteHandPoint(t time.Time) Point {
|
func minuteHandPoint(t time.Time) Point {
|
||||||
return angleToPoint(minutesInRadians(t))
|
return angleToPoint(minutesInRadians(t))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hourHandPoint(t time.Time) Point {
|
||||||
|
return angleToPoint(hoursInRadians(t))
|
||||||
|
}
|
||||||
|
@ -88,3 +88,27 @@ 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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,7 +20,7 @@ func TestSecondsInRadians(t *testing.T) {
|
|||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(testName(c.time), func(t *testing.T) {
|
t.Run(testName(c.time), func(t *testing.T) {
|
||||||
got := secondsInRadians(c.time)
|
got := secondsInRadians(c.time)
|
||||||
if got != c.angle {
|
if !roughlyEqualFloat64(got, c.angle) {
|
||||||
t.Fatalf("Wanted %v radians, but got %v", c.angle, got)
|
t.Fatalf("Wanted %v radians, but got %v", c.angle, got)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -39,13 +39,34 @@ func TestMinutesInRadians(t *testing.T) {
|
|||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(testName(c.time), func(t *testing.T) {
|
t.Run(testName(c.time), func(t *testing.T) {
|
||||||
got := minutesInRadians(c.time)
|
got := minutesInRadians(c.time)
|
||||||
if got != c.angle {
|
if !roughlyEqualFloat64(got, c.angle) {
|
||||||
t.Fatalf("Wanted %v radians, but got %v", c.angle, got)
|
t.Fatalf("Wanted %v radians, but got %v", c.angle, got)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(got, c.angle) {
|
||||||
|
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
|
||||||
@ -84,6 +105,26 @@ func TestMinuteHandPoint(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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}},
|
||||||
|
{simpleTime(21, 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 roughlyEqualFloat64(a, b float64) bool {
|
func roughlyEqualFloat64(a, b float64) bool {
|
||||||
const equalityThreshold = 1e-7
|
const equalityThreshold = 1e-7
|
||||||
return math.Abs(a-b) < equalityThreshold
|
return math.Abs(a-b) < equalityThreshold
|
||||||
|
17
svgWriter.go
17
svgWriter.go
@ -6,10 +6,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const secondHandLength = 90
|
const (
|
||||||
const minuteHandLength = 80
|
hourHandLength = 50
|
||||||
const clockCentreX = 150
|
secondHandLength = 90
|
||||||
const clockCentreY = 150
|
minuteHandLength = 80
|
||||||
|
clockCentreX = 150
|
||||||
|
clockCentreY = 150
|
||||||
|
)
|
||||||
|
|
||||||
const svgStart = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
const svgStart = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
@ -29,6 +32,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,3 +51,8 @@ func MinuteHand(w io.Writer, t time.Time) {
|
|||||||
p := makeHand(minuteHandPoint(t), minuteHandLength)
|
p := makeHand(minuteHandPoint(t), minuteHandLength)
|
||||||
fmt.Fprintf(w, `<line x1="150" y1="150" x2="%.3f" y2="%.3f" style="fill:none;stroke:#000;stroke-width:3px;"/>`, p.X, p.Y)
|
fmt.Fprintf(w, `<line x1="150" y1="150" x2="%.3f" y2="%.3f" style="fill:none;stroke:#000;stroke-width:3px;"/>`, p.X, p.Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HourHand(w io.Writer, t time.Time) {
|
||||||
|
p := makeHand(hourHandPoint(t), hourHandLength)
|
||||||
|
fmt.Fprintf(w, `<line x1="150" y1="150" x2="%.3f" y2="%.3f" style="fill:none;stroke:#000;stroke-width:3px;"/>`, p.X, p.Y)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user