feature; solving issue #121

There is now a way to allow gaps in charts due to missing
measurements. A style setting (StrokeMaxSpanGap) determines
whether a gap is stroked or not.

An example is included in examples/custom_styles.
This commit is contained in:
Joost Helberg 2024-06-24 09:46:32 +02:00
parent c9c9042154
commit 0558d6eb30
4 changed files with 35 additions and 5 deletions

14
draw.go
View file

@ -47,12 +47,26 @@ func (d draw) LineSeries(r Renderer, canvasBox Box, xrange, yrange Range, style
if style.ShouldDrawStroke() { if style.ShouldDrawStroke() {
style.GetStrokeOptions().WriteDrawingOptionsToRenderer(r) style.GetStrokeOptions().WriteDrawingOptionsToRenderer(r)
vxprev := 0.0
if vs.Len() > 0 {
vxprev, _ = vs.GetValues( 1 ) // make sure vxprev starts at the beginning
}
maxspan := style.GetStrokeMaxSpanGap()
r.MoveTo(x0, y0) r.MoveTo(x0, y0)
for i := 1; i < vs.Len(); i++ { for i := 1; i < vs.Len(); i++ {
vx, vy = vs.GetValues(i) vx, vy = vs.GetValues(i)
x = cl + xrange.Translate(vx) x = cl + xrange.Translate(vx)
y = cb - yrange.Translate(vy) y = cb - yrange.Translate(vy)
df := vxprev - vx
if df < 0 {
df *= -1 // make it work, regardless of direction ox X-axis
}
if df <= maxspan { // span the gap i.e. connect the two points
r.LineTo(x, y) r.LineTo(x, y)
} else { // do not span the gap
r.MoveTo(x, y)
}
vxprev = vx
} }
r.Stroke() r.Stroke()
} }

View file

@ -25,9 +25,10 @@ func main() {
Style: chart.Style{ Style: chart.Style{
StrokeColor: drawing.ColorRed, // will supercede defaults StrokeColor: drawing.ColorRed, // will supercede defaults
FillColor: drawing.ColorRed.WithAlpha(64), // will supercede defaults FillColor: drawing.ColorRed.WithAlpha(64), // will supercede defaults
StrokeMaxSpanGap: 2, // do not span gaps more than 2.0 wide
}, },
XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0}, XValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0},
YValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0}, YValues: []float64{1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0},
}, },
}, },
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -51,6 +51,7 @@ type Style struct {
StrokeWidth float64 StrokeWidth float64
StrokeColor drawing.Color StrokeColor drawing.Color
StrokeDashArray []float64 StrokeDashArray []float64
StrokeMaxSpanGap float64 // in xvalue unit, nano second for timeseries
DotColor drawing.Color DotColor drawing.Color
DotWidth float64 DotWidth float64
@ -122,6 +123,12 @@ func (s Style) String() string {
output = append(output, "\"stroke_color\": null") output = append(output, "\"stroke_color\": null")
} }
if s.StrokeMaxSpanGap >= 0 {
output = append(output, fmt.Sprintf("\"stroke_maxspangap\": %.02f", s.StrokeMaxSpanGap))
} else {
output = append(output, "\"stroke_maxspangap\": null")
}
if len(s.StrokeDashArray) > 0 { if len(s.StrokeDashArray) > 0 {
var elements []string var elements []string
for _, v := range s.StrokeDashArray { for _, v := range s.StrokeDashArray {
@ -194,6 +201,11 @@ func (s Style) GetStrokeColor(defaults ...drawing.Color) drawing.Color {
return s.StrokeColor return s.StrokeColor
} }
// GetStrokeMaxSpanGap returns the max span gap, smaller or equal gaps are connected, larger ones are not.
func (s Style) GetStrokeMaxSpanGap() float64 {
return s.StrokeMaxSpanGap
}
// GetFillColor returns the fill color. // GetFillColor returns the fill color.
func (s Style) GetFillColor(defaults ...drawing.Color) drawing.Color { func (s Style) GetFillColor(defaults ...drawing.Color) drawing.Color {
if s.FillColor.IsZero() { if s.FillColor.IsZero() {
@ -388,6 +400,7 @@ func (s Style) InheritFrom(defaults Style) (final Style) {
final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor) final.StrokeColor = s.GetStrokeColor(defaults.StrokeColor)
final.StrokeWidth = s.GetStrokeWidth(defaults.StrokeWidth) final.StrokeWidth = s.GetStrokeWidth(defaults.StrokeWidth)
final.StrokeDashArray = s.GetStrokeDashArray(defaults.StrokeDashArray) final.StrokeDashArray = s.GetStrokeDashArray(defaults.StrokeDashArray)
final.StrokeMaxSpanGap = s.GetStrokeMaxSpanGap()
final.DotColor = s.GetDotColor(defaults.DotColor) final.DotColor = s.GetDotColor(defaults.DotColor)
final.DotWidth = s.GetDotWidth(defaults.DotWidth) final.DotWidth = s.GetDotWidth(defaults.DotWidth)
@ -416,6 +429,7 @@ func (s Style) GetStrokeOptions() Style {
StrokeDashArray: s.StrokeDashArray, StrokeDashArray: s.StrokeDashArray,
StrokeColor: s.StrokeColor, StrokeColor: s.StrokeColor,
StrokeWidth: s.StrokeWidth, StrokeWidth: s.StrokeWidth,
StrokeMaxSpanGap:s.StrokeMaxSpanGap,
} }
} }
@ -443,6 +457,7 @@ func (s Style) GetFillAndStrokeOptions() Style {
return Style{ return Style{
ClassName: s.ClassName, ClassName: s.ClassName,
StrokeDashArray: s.StrokeDashArray, StrokeDashArray: s.StrokeDashArray,
StrokeMaxSpanGap:s.StrokeMaxSpanGap,
FillColor: s.FillColor, FillColor: s.FillColor,
StrokeColor: s.StrokeColor, StrokeColor: s.StrokeColor,
StrokeWidth: s.StrokeWidth, StrokeWidth: s.StrokeWidth,