11package ui
22
33import (
4+ "fmt"
45 "math"
56
67 "github.com/gotk3/gotk3/cairo"
@@ -12,6 +13,8 @@ import (
1213
1314const useCurvedGraph = false
1415
16+ const timeIndicatorColorName = "hellocontest-timeindicator"
17+
1518type scoreGraphStyle struct {
1619 colorProvider
1720
@@ -21,6 +24,8 @@ type scoreGraphStyle struct {
2124 timeFrameColor style.Color
2225 areaAlpha float64
2326 borderAlpha float64
27+
28+ fontSize float64
2429}
2530
2631func (s * scoreGraphStyle ) Refresh () {
@@ -46,13 +51,12 @@ type scoreGraph struct {
4651 useCurvedGraph bool
4752}
4853
49- const timeIndicatorColorName = "hellocontest-timeindicator"
50-
5154func newScoreGraph (colors colorProvider , clock core.Clock ) * scoreGraph {
5255 style := & scoreGraphStyle {
5356 colorProvider : colors ,
5457 areaAlpha : 0.4 ,
5558 borderAlpha : 0.8 ,
59+ fontSize : 15 ,
5660 }
5761 style .Refresh ()
5862
@@ -123,6 +127,11 @@ type graphLayout struct {
123127 pointsLowZoneHeight float64
124128 multisLowZoneHeight float64
125129 binWidth float64
130+ divX []float64
131+ axisWidth float64
132+ divisionWidth float64
133+ leftLegendWidth float64
134+ timeIndicatorHeight float64
126135}
127136
128137func (g * scoreGraph ) Draw (da * gtk.DrawingArea , cr * cairo.Context ) {
@@ -135,27 +144,10 @@ func (g *scoreGraph) Draw(da *gtk.DrawingArea, cr *cairo.Context) {
135144 if len (g .graphs ) > 0 {
136145 valueCount = len (g .graphs [0 ].DataPoints )
137146 }
138- layout := g .calculateLayout (da , valueCount )
147+ layout := g .calculateLayout (da , cr , valueCount )
139148
140- // the background
141149 g .fillBackground (cr )
142-
143- // the zone
144- cr .SetSourceRGBA (g .style .lowZoneColor .WithAlpha (g .style .areaAlpha ))
145- cr .MoveTo (0 , layout .zeroY - layout .pointsLowZoneHeight )
146- cr .LineTo (layout .width , layout .zeroY - layout .pointsLowZoneHeight )
147- cr .LineTo (layout .width , layout .zeroY + layout .multisLowZoneHeight )
148- cr .LineTo (0 , layout .zeroY + layout .multisLowZoneHeight )
149- cr .ClosePath ()
150- cr .Fill ()
151-
152- cr .SetSourceRGBA (g .style .lowZoneColor .WithAlpha (g .style .borderAlpha ))
153- cr .MoveTo (0 , layout .zeroY - layout .pointsLowZoneHeight )
154- cr .LineTo (layout .width , layout .zeroY - layout .pointsLowZoneHeight )
155- cr .LineTo (layout .width , layout .zeroY + layout .multisLowZoneHeight )
156- cr .LineTo (0 , layout .zeroY + layout .multisLowZoneHeight )
157- cr .ClosePath ()
158- cr .Stroke ()
150+ g .drawLowZone (cr , layout )
159151
160152 // the graph
161153 for i := len (g .graphs ) - 1 ; i >= 0 ; i -- {
@@ -170,34 +162,25 @@ func (g *scoreGraph) Draw(da *gtk.DrawingArea, cr *cairo.Context) {
170162 }
171163 }
172164
173- // the time frame
174- if g .timeFrameIndex >= 0 && valueCount > 1 {
175- startX := float64 (g .timeFrameIndex ) * layout .binWidth
176- endX := float64 (g .timeFrameIndex + 1 ) * layout .binWidth
177- cr .SetSourceRGBA (g .style .timeFrameColor .ToRGBA ())
178- cr .MoveTo (startX , layout .zeroY - layout .maxHeight )
179- cr .LineTo (endX , layout .zeroY - layout .maxHeight )
180- cr .LineTo (endX , layout .zeroY + layout .maxHeight )
181- cr .LineTo (startX , layout .zeroY + layout .maxHeight )
182- cr .ClosePath ()
183- cr .Stroke ()
184- }
185-
186- // the zero line
187- cr .SetSourceRGB (g .style .axisColor .ToRGB ())
188- cr .MoveTo (0 , layout .zeroY )
189- cr .LineTo (layout .width , layout .zeroY )
190- cr .Stroke ()
165+ g .drawTimeDivisions (cr , layout )
166+ g .drawTimeIndicator (cr , layout )
167+ g .drawZeroLine (cr , layout )
191168}
192169
193- func (g * scoreGraph ) calculateLayout (da * gtk.DrawingArea , valueCount int ) graphLayout {
170+ func (g * scoreGraph ) calculateLayout (da * gtk.DrawingArea , cr * cairo. Context , valueCount int ) graphLayout {
194171 result := graphLayout {
195- width : float64 (da .GetAllocatedWidth ()),
196- height : float64 (da .GetAllocatedHeight ()),
197- marginY : 5.0 ,
172+ width : float64 (da .GetAllocatedWidth ()),
173+ height : float64 (da .GetAllocatedHeight ()),
174+ marginY : 10.0 ,
175+ axisWidth : 1.0 ,
176+ divisionWidth : .5 ,
198177 }
199178
200- result .zeroY = result .height / 2.0
179+ cr .SetFontSize (g .style .fontSize )
180+ result .leftLegendWidth = cr .TextExtents ("00:00" ).Width + 2.0
181+ result .timeIndicatorHeight = cr .TextExtents ("Hg" ).Height + 2.0
182+
183+ result .zeroY = (result .height - result .timeIndicatorHeight ) / 2.0
201184 result .maxHeight = result .zeroY - result .marginY
202185 result .pointsLowZoneHeight = math .Min (result .maxHeight / 2.0 , (result .maxHeight / float64 (g .maxPoints ))* g .pointsBinGoal )
203186 result .multisLowZoneHeight = math .Min (result .maxHeight / 2.0 , (result .maxHeight / float64 (g .maxMultis ))* g .multisBinGoal )
@@ -207,6 +190,15 @@ func (g *scoreGraph) calculateLayout(da *gtk.DrawingArea, valueCount int) graphL
207190 result .binWidth = result .width
208191 }
209192
193+ const divCount = 8
194+ if len (result .divX ) != divCount {
195+ result .divX = make ([]float64 , divCount - 1 )
196+ }
197+ divWidth := result .width / float64 (divCount )
198+ for i := range result .divX {
199+ result .divX [i ] = float64 (i + 1 ) * divWidth
200+ }
201+
210202 return result
211203}
212204
@@ -218,6 +210,87 @@ func (g *scoreGraph) fillBackground(cr *cairo.Context) {
218210 cr .Paint ()
219211}
220212
213+ func (g * scoreGraph ) drawZeroLine (cr * cairo.Context , layout graphLayout ) {
214+ cr .SetSourceRGB (g .style .axisColor .ToRGB ())
215+ cr .SetLineWidth (layout .axisWidth )
216+ cr .MoveTo (0 , layout .zeroY )
217+ cr .LineTo (layout .width , layout .zeroY )
218+ cr .Stroke ()
219+ }
220+
221+ func (g * scoreGraph ) drawTimeDivisions (cr * cairo.Context , layout graphLayout ) {
222+ cr .SetSourceRGB (g .style .axisColor .ToRGB ())
223+ cr .SetLineWidth (layout .divisionWidth )
224+ for _ , x := range layout .divX {
225+ cr .MoveTo (x , layout .zeroY - layout .maxHeight )
226+ cr .LineTo (x , layout .zeroY + layout .maxHeight )
227+ cr .Stroke ()
228+ }
229+ }
230+
231+ func (g * scoreGraph ) drawTimeIndicator (cr * cairo.Context , layout graphLayout ) {
232+ if g .timeFrameIndex <= 0 {
233+ return
234+ }
235+ now := g .clock .Now ()
236+
237+ // the time bar
238+ elapsedTimePercent := g .graphs [0 ].ElapsedTimePercent (now )
239+ left := 0.0
240+ right := left + (layout .width - left )* elapsedTimePercent
241+ bottom := layout .height - layout .marginY
242+ top := bottom - layout .timeIndicatorHeight
243+
244+ cr .SetSourceRGBA (g .style .timeFrameColor .ToRGBA ())
245+ cr .MoveTo (left , top )
246+ cr .LineTo (right , top )
247+ cr .LineTo (right , bottom )
248+ cr .LineTo (left , bottom )
249+ cr .ClosePath ()
250+ cr .Fill ()
251+
252+ // the elapsed time
253+ elapsedTime := g .graphs [0 ].ElapsedTime (now )
254+ elapsedTimeText := fmt .Sprintf ("%02d:%02d" , int (elapsedTime .Hours ()), int (elapsedTime .Minutes ())% 60 )
255+
256+ cr .SetSourceRGB (g .style .axisColor .ToRGB ())
257+ cr .SetFontSize (g .style .fontSize )
258+ cr .MoveTo (1 , layout .height - layout .marginY - 1 )
259+ cr .ShowText (elapsedTimeText )
260+
261+ // the old box
262+ startX := float64 (g .timeFrameIndex ) * layout .binWidth
263+ endX := float64 (g .timeFrameIndex + 1 ) * layout .binWidth
264+
265+ cr .SetSourceRGBA (g .style .timeFrameColor .ToRGBA ())
266+ cr .SetLineWidth (layout .divisionWidth )
267+ cr .MoveTo (startX , layout .zeroY - layout .maxHeight )
268+ cr .LineTo (endX , layout .zeroY - layout .maxHeight )
269+ cr .LineTo (endX , layout .zeroY + layout .maxHeight )
270+ cr .LineTo (startX , layout .zeroY + layout .maxHeight )
271+ cr .ClosePath ()
272+ cr .Stroke ()
273+ }
274+
275+ func (g * scoreGraph ) drawLowZone (cr * cairo.Context , layout graphLayout ) {
276+ cr .SetSourceRGBA (g .style .lowZoneColor .WithAlpha (g .style .areaAlpha ))
277+ cr .MoveTo (0 , layout .zeroY - layout .pointsLowZoneHeight )
278+ cr .LineTo (layout .width , layout .zeroY - layout .pointsLowZoneHeight )
279+ cr .LineTo (layout .width , layout .zeroY + layout .multisLowZoneHeight )
280+ cr .LineTo (0 , layout .zeroY + layout .multisLowZoneHeight )
281+ cr .ClosePath ()
282+ cr .Fill ()
283+
284+ cr .SetSourceRGBA (g .style .lowZoneColor .WithAlpha (g .style .borderAlpha ))
285+ cr .SetLineWidth (layout .divisionWidth )
286+ cr .MoveTo (0 , layout .zeroY - layout .pointsLowZoneHeight )
287+ cr .LineTo (layout .width , layout .zeroY - layout .pointsLowZoneHeight )
288+ cr .LineTo (layout .width , layout .zeroY + layout .multisLowZoneHeight )
289+ cr .LineTo (0 , layout .zeroY + layout .multisLowZoneHeight )
290+ cr .ClosePath ()
291+ cr .Stroke ()
292+ }
293+
221294func (g * scoreGraph ) drawDataPointsRectangular (cr * cairo.Context , layout graphLayout , datapoints []core.BandScore ) {
222295 valueCount := len (datapoints )
223296
0 commit comments