2024-09-23 14:49:29 +00:00
|
|
|
package clockface
|
|
|
|
|
|
|
|
import (
|
2024-09-23 15:24:40 +00:00
|
|
|
"fmt"
|
|
|
|
"io"
|
2024-09-23 14:49:29 +00:00
|
|
|
"math"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Point represents a two-dimentional Cartesian coordinate
|
|
|
|
type Point struct {
|
|
|
|
X float64
|
|
|
|
Y float64
|
|
|
|
}
|
|
|
|
|
2024-09-23 15:24:40 +00:00
|
|
|
const (
|
2024-09-23 20:31:05 +00:00
|
|
|
hourHandLength = 50
|
2024-09-23 20:16:06 +00:00
|
|
|
minuteHandLength = 80
|
2024-09-23 15:24:40 +00:00
|
|
|
secondHandLength = 90
|
|
|
|
clockCentreX = 150
|
|
|
|
clockCentreY = 150
|
|
|
|
)
|
|
|
|
|
2024-09-23 20:40:35 +00:00
|
|
|
const (
|
|
|
|
secondsInHalfClock = 30
|
|
|
|
secondsInClock = 2 * secondsInHalfClock
|
|
|
|
minutesInHalfClock = 30
|
|
|
|
minutesInClock = 2 * minutesInHalfClock
|
|
|
|
hoursInHalfClock = 6
|
|
|
|
hoursInClock = 2 * hoursInHalfClock
|
|
|
|
)
|
|
|
|
|
2024-09-23 15:24:40 +00:00
|
|
|
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">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg"
|
|
|
|
width="100%"
|
|
|
|
height="100%"
|
|
|
|
viewBox="0 0 300 300"
|
|
|
|
version="2.0">`
|
|
|
|
|
|
|
|
const bezel = `<circle cx="150" cy="150" r="100" style="fill:#fff;stroke:#000;stroke-width:5px;"/>`
|
|
|
|
|
|
|
|
const svgEnd = `</svg>`
|
|
|
|
|
|
|
|
func SVGWriter(w io.Writer, t time.Time) {
|
|
|
|
io.WriteString(w, svgStart)
|
|
|
|
io.WriteString(w, bezel)
|
2024-09-23 20:16:06 +00:00
|
|
|
secondHand(w, t)
|
|
|
|
minuteHand(w, t)
|
2024-09-23 20:31:05 +00:00
|
|
|
hourHand(w, t)
|
2024-09-23 15:24:40 +00:00
|
|
|
io.WriteString(w, svgEnd)
|
|
|
|
}
|
|
|
|
|
2024-09-23 14:49:29 +00:00
|
|
|
// SecondHand is the unit vector of the second hand of an analogue clock at the time `t` represented as a Point
|
2024-09-23 20:16:06 +00:00
|
|
|
func secondHand(w io.Writer, t time.Time) {
|
2024-09-23 14:49:29 +00:00
|
|
|
p := secondHandPoint(t)
|
2024-09-23 20:18:46 +00:00
|
|
|
makeHand(w, secondHandLength, p)
|
2024-09-23 14:49:29 +00:00
|
|
|
}
|
|
|
|
|
2024-09-23 20:16:06 +00:00
|
|
|
// MinuteHand is the unit vector of the minute hand of an analogue clock at the time `t` represented as a Point
|
|
|
|
func minuteHand(w io.Writer, t time.Time) {
|
|
|
|
p := minuteHandPoint(t)
|
2024-09-23 20:18:46 +00:00
|
|
|
makeHand(w, minuteHandLength, p)
|
|
|
|
}
|
|
|
|
|
2024-09-23 20:31:05 +00:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2024-09-23 20:18:46 +00:00
|
|
|
func makeHand(w io.Writer, length float64, p Point) {
|
|
|
|
p = Point{p.X * length, p.Y * length} // scale
|
|
|
|
p = Point{p.X, -p.Y} // flip
|
|
|
|
p = Point{p.X + clockCentreX, p.Y + clockCentreY} // translate
|
2024-09-23 20:16:06 +00:00
|
|
|
fmt.Fprintf(
|
|
|
|
w,
|
|
|
|
`<line x1="150" y1="150" x2="%f" y2="%f" style="fill:none;stroke:#f00;stroke-width:3px;"/>`,
|
|
|
|
p.X,
|
|
|
|
p.Y,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2024-09-23 20:31:05 +00:00
|
|
|
func hoursInRadians(t time.Time) float64 {
|
2024-09-23 20:40:35 +00:00
|
|
|
return (minutesInRadians(t) / hoursInClock) + math.Pi/(hoursInHalfClock/float64(t.Hour()%hoursInClock))
|
2024-09-23 20:31:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func hourHandPoint(t time.Time) Point {
|
|
|
|
return angleToPoint(hoursInRadians(t))
|
|
|
|
}
|
|
|
|
|
2024-09-23 20:09:10 +00:00
|
|
|
func minutesInRadians(t time.Time) float64 {
|
2024-09-23 20:40:35 +00:00
|
|
|
return (secondsInRadians(t) / minutesInClock) + math.Pi/(minutesInHalfClock/float64(t.Minute()))
|
2024-09-23 20:09:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func minuteHandPoint(t time.Time) Point {
|
2024-09-23 20:16:06 +00:00
|
|
|
return angleToPoint(minutesInRadians(t))
|
2024-09-23 20:09:10 +00:00
|
|
|
}
|
|
|
|
|
2024-09-23 14:49:29 +00:00
|
|
|
func secondsInRadians(t time.Time) float64 {
|
2024-09-23 20:40:35 +00:00
|
|
|
return math.Pi / (secondsInHalfClock / float64(t.Second()))
|
2024-09-23 14:49:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func secondHandPoint(t time.Time) Point {
|
2024-09-23 20:16:06 +00:00
|
|
|
return angleToPoint(secondsInRadians(t))
|
|
|
|
}
|
|
|
|
|
|
|
|
func angleToPoint(angle float64) Point {
|
2024-09-23 14:49:29 +00:00
|
|
|
x := math.Sin(angle)
|
|
|
|
y := math.Cos(angle)
|
|
|
|
|
|
|
|
return Point{x, y}
|
|
|
|
}
|