about summary refs log tree commit diff
path: root/draw/draw.go
blob: b8d552a87d44959a7d74f1c5688e90aeadaecb95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package draw

import (
	"../structs"
	"github.com/fogleman/gg"
	"math"
)

// initializePlot generates a new plot and returns the plot context
func initializePlot() *gg.Context {
	// Define the image size
	const imageWidth = 8192
	const imageHeight = 8192

	// Initialize the new context
	dc := gg.NewContext(imageWidth, imageHeight)

	// Set the background black
	dc.SetRGB(0, 0, 0)
	dc.Clear()

	// Invert the Y axis (positive values are on the top and right)
	dc.InvertY()

	// Set the coordinate midpoint to the middle of the image
	dc.Translate(imageWidth/2, imageHeight/2)

	return dc
}

// saveImages saves the given context to a png at the given path
func saveImage(dc *gg.Context, path string) {
	dc.SavePNG(path)
}

// drawStar draws the given stars to the given context
func drawStar(dc *gg.Context, star structs.Star) {
	dc.DrawPoint(star.C.X, star.C.Y, 2)
	dc.Fill()
	dc.Stroke()
}

// vectorLength calculates the length of the given vector
func vectorLength(force structs.Force) float64 {
	return math.Sqrt(math.Pow(force.X, 2) + math.Pow(force.Y, 2))
}

func drawForce(dc *gg.Context, star structs.Star) {
	// controll the length of the vector
	var scalingFactor float64 = 40

	// Move the "cursor" to the start position of the vector
	dc.MoveTo(star.C.X, star.C.Y)

	// calculate the length of the vector
	vecLength := vectorLength(star.F)

	// Use a sigmoid function to generate useful values for coloring the vectors according to their
	// strength
	var val = 1.0 / (1.0 + math.Exp(-vecLength/100000))

	// Set the color to a blue / red
	dc.SetRGB(val, 0, 1-val)

	// trace the Vector
	FxUnit := star.F.X / math.Abs(vecLength)
	FyUnit := star.F.Y / math.Abs(vecLength)
	dc.LineTo(star.C.X+(FxUnit*scalingFactor), star.C.Y+(FyUnit*scalingFactor))

	// css
	dc.SetLineWidth(2)

	// And finally: DRAW (stroke) the vector
	dc.Stroke()
}

// drawStars draws all the stars in the given slice to the given context
func drawStars(dc *gg.Context, slice []structs.Star) {
	// draw all the forces in the given slice
	for _, star := range slice {
		drawForce(dc, star)
	}

	dc.SetRGB(1, 1, 1)

	// draw all the stars in the given slice
	for _, star := range slice {
		drawStar(dc, star)
	}
}

// Slice draws the stars and the forces acting on them and saves the result to the given path
func Slice(slice []structs.Star, path string) {

	// initialize the plot
	dc := initializePlot()

	// draw all the stars in the given slice
	drawStars(dc, slice)

	// save the plot to the given path
	saveImage(dc, path)
}