clockface: xml

This commit is contained in:
vinchent 2024-09-23 17:24:40 +02:00
parent 423c9e9567
commit 7b3fb0f91c
3 changed files with 107 additions and 48 deletions

View File

@ -1,6 +1,8 @@
package clockface package clockface
import ( import (
"fmt"
"io"
"math" "math"
"time" "time"
) )
@ -11,13 +13,43 @@ type Point struct {
Y float64 Y float64
} }
const (
secondHandLength = 90
clockCentreX = 150
clockCentreY = 150
)
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)
SecondHand(w, t)
io.WriteString(w, svgEnd)
}
// SecondHand is the unit vector of the second hand of an analogue clock at the time `t` represented as a Point // SecondHand is the unit vector of the second hand of an analogue clock at the time `t` represented as a Point
func SecondHand(t time.Time) Point { func SecondHand(w io.Writer, t time.Time) {
p := secondHandPoint(t) p := secondHandPoint(t)
p = Point{p.X * 90, p.Y * 90} // scale p = Point{p.X * secondHandLength, p.Y * secondHandLength} // scale
p = Point{p.X, -p.Y} // flip p = Point{p.X, -p.Y} // flip
p = Point{p.X + 150, p.Y + 150} // translate p = Point{p.X + clockCentreX, p.Y + clockCentreY} // translate
return p fmt.Fprintf(
w,
`<line x1="150" y1="150" x2="%f" y2="%f" style="fill:none;stroke:#f00;stroke-width:3px;"/>`,
p.X,
p.Y,
)
} }
func secondsInRadians(t time.Time) float64 { func secondsInRadians(t time.Time) float64 {

View File

@ -1,28 +1,81 @@
package clockface package clockface
import ( import (
"bytes"
"encoding/xml"
"testing" "testing"
"time" "time"
) )
func TestSecondHandAtMidnight(t *testing.T) { 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"`
}
// func TestSecondHandAtMidnight(t *testing.T) {
// tm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)
//
// want := Point{X: 150, Y: 150 - 90}
// got := SecondHand(tm)
//
// if got != want {
// t.Errorf("Got %v, wanted %v", got, want)
// }
// }
// func TestSecondHandAt30Seconds(t *testing.T) {
// tm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC)
//
// want := Point{X: 150, Y: 150 + 90}
// got := SecondHand(tm)
//
// if got != want {
// t.Errorf("Got %v, wanted %v", got, want)
// }
// }
func TestSVGWriterAtMidnight(t *testing.T) {
tm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC) tm := time.Date(1337, time.January, 1, 0, 0, 0, 0, time.UTC)
want := Point{X: 150, Y: 150 - 90} b := bytes.Buffer{}
got := SecondHand(tm) SVGWriter(&b, tm)
if got != want { svg := Svg{}
t.Errorf("Got %v, wanted %v", got, want) xml.Unmarshal(b.Bytes(), &svg)
x2 := "150.000000"
y2 := "60.000000"
for _, line := range svg.Line {
if line.X2 == x2 && line.Y2 == y2 {
return
} }
} }
func TestSecondHandAt30Seconds(t *testing.T) { t.Errorf(
tm := time.Date(1337, time.January, 1, 0, 0, 30, 0, time.UTC) "Expected to find the second hand with x2 of %+v and y2 of %+v, in the SVG output %v",
x2,
want := Point{X: 150, Y: 150 + 90} y2,
got := SecondHand(tm) b.String(),
)
if got != want {
t.Errorf("Got %v, wanted %v", got, want)
}
} }

28
main.go
View File

@ -1,9 +1,7 @@
package main package main
import ( import (
"fmt"
"gobytest/clockface" "gobytest/clockface"
"io"
"os" "os"
"time" "time"
) )
@ -18,29 +16,5 @@ func main() {
// clockface // clockface
t := time.Now() t := time.Now()
sh := clockface.SecondHand(t) clockface.SVGWriter(os.Stdout, t)
io.WriteString(os.Stdout, svgStart)
io.WriteString(os.Stdout, bezel)
io.WriteString(os.Stdout, secondHandTag(sh))
io.WriteString(os.Stdout, svgEnd)
} }
func secondHandTag(p clockface.Point) string {
return fmt.Sprintf(
`<line x1="150" y1="150" x2="%f" y2="%f" style="fill:none;stroke:#f00;stroke-width:3px;"/>`,
p.X,
p.Y,
)
}
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>`