First commit
This commit is contained in:
commit
469855f3de
105
.gitignore
vendored
Normal file
105
.gitignore
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/macos,visualstudiocode,terraform,windows
|
||||||
|
# Edit at https://www.gitignore.io/?templates=macos,visualstudiocode,terraform,windows
|
||||||
|
|
||||||
|
### macOS ###
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
### Terraform ###
|
||||||
|
# Local .terraform directories
|
||||||
|
**/.terraform/*
|
||||||
|
|
||||||
|
# .tfstate files
|
||||||
|
*.tfstate
|
||||||
|
*.tfstate.*
|
||||||
|
*.plan
|
||||||
|
|
||||||
|
# Crash log files
|
||||||
|
crash.log
|
||||||
|
|
||||||
|
# Ignore any .tfvars files that are generated automatically for each Terraform run. Most
|
||||||
|
# .tfvars files are managed as part of configuration and so should be included in
|
||||||
|
# version control.
|
||||||
|
#
|
||||||
|
# example.tfvars
|
||||||
|
|
||||||
|
# Ignore override files as they are usually used to override resources locally and so
|
||||||
|
# are not checked in
|
||||||
|
override.tf
|
||||||
|
override.tf.json
|
||||||
|
*_override.tf
|
||||||
|
*_override.tf.json
|
||||||
|
secrets.tfvars
|
||||||
|
|
||||||
|
# Include override files you do wish to add to version control using negated pattern
|
||||||
|
#
|
||||||
|
# !example_override.tf
|
||||||
|
|
||||||
|
### VisualStudioCode ###
|
||||||
|
.vscode/*
|
||||||
|
#!.vscode/settings.json
|
||||||
|
#!.vscode/tasks.json
|
||||||
|
#!.vscode/launch.json
|
||||||
|
#!.vscode/extensions.json
|
||||||
|
|
||||||
|
### VisualStudioCode Patch ###
|
||||||
|
# Ignore all local history of files
|
||||||
|
.history
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/macos,visualstudiocode,terraform,windows
|
||||||
|
|
||||||
|
# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option)
|
||||||
|
.vscode/*
|
||||||
|
|
||||||
|
.idea
|
1
clockface.go
Normal file
1
clockface.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package clockface
|
42
clockface/main.go
Normal file
42
clockface/main.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package clockface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
package clockface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.nerdfortress.dev/mitch/gotdd/clockface"
|
||||||
|
"math"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const secondHandLength = 90
|
||||||
|
const clockCentreX = 150
|
||||||
|
const clockCentreY = 150
|
||||||
|
|
||||||
|
type Point struct {
|
||||||
|
X float64
|
||||||
|
Y float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func SecondHand(t time.Time) Point {
|
||||||
|
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
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func secondsInRadians(t time.Time) float64 {
|
||||||
|
return math.Pi / (30 / (float64(t.Second())))
|
||||||
|
}
|
||||||
|
|
||||||
|
func secondHandPoint(t time.Time) Point {
|
||||||
|
angle := secondsInRadians(t)
|
||||||
|
x := math.Sin(angle)
|
||||||
|
y := math.Cos(angle)
|
||||||
|
return Point{x, y}
|
||||||
|
}
|
35
clockface_acceptance_test.go
Normal file
35
clockface_acceptance_test.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package clockface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
package clockface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
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, want %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)
|
||||||
|
}
|
||||||
|
}
|
73
clockface_test.go
Normal file
73
clockface_test.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package clockface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
package clockface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func simpleTime(hours, minutes, seconds int) time.Time {
|
||||||
|
return time.Date(312, time.October, 28, hours, minutes, seconds, 0, time.UTC)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testName(t time.Time) string {
|
||||||
|
return t.Format("15:04:05")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecondsInRadians(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
time time.Time
|
||||||
|
angle float64
|
||||||
|
}{
|
||||||
|
{simpleTime(0, 0, 30), math.Pi},
|
||||||
|
{simpleTime(0, 0, 0), 0},
|
||||||
|
{simpleTime(0, 0, 45), (math.Pi / 2) * 3},
|
||||||
|
{simpleTime(0, 0, 7), (math.Pi / 30) * 7},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(testName(c.time), func(t *testing.T) {
|
||||||
|
got := secondsInRadians(c.time)
|
||||||
|
if !roughlyEqualFloat64(got, c.angle) {
|
||||||
|
t.Fatalf("Wanted %v radians, but got %v", c.angle, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecondHandVector(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
time time.Time
|
||||||
|
point Point
|
||||||
|
}{
|
||||||
|
{simpleTime(0, 0, 30), Point{0, -1}},
|
||||||
|
{simpleTime(0, 0, 45), Point{-1, 0}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(testName(c.time), func(t *testing.T) {
|
||||||
|
got := secondHandPoint(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
|
||||||
|
}
|
||||||
|
|
||||||
|
func roughlyEqualPoint(a, b Point) bool {
|
||||||
|
return roughlyEqualFloat64(a.X, b.X) &&
|
||||||
|
roughlyEqualFloat64(a.Y, b.Y)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user