diff --git a/charts.go b/charts.go
index 6a41bd3..c73f7a2 100644
--- a/charts.go
+++ b/charts.go
@@ -12,7 +12,8 @@ const labelFontSize = 10
const smallLabelFontSize = 8
const defaultDotWidth = 2.0
const defaultStrokeWidth = 2.0
-const defaultYAxisLabelCount = 10
+const defaultYAxisLabelCountHigh = 10
+const defaultYAxisLabelCountLow = 3
var defaultChartWidth = 600
var defaultChartHeight = 400
@@ -43,6 +44,15 @@ func GetNullValue() float64 {
return nullValue
}
+func defaultYAxisLabelCount(span float64, decimalData bool) int {
+ result := math.Min(math.Max(span+1, defaultYAxisLabelCountLow), defaultYAxisLabelCountHigh)
+ if decimalData {
+ // if there is a decimal, we double our labels to provide more detailed labels
+ result = math.Min(result*2, defaultYAxisLabelCountHigh)
+ }
+ return int(result)
+}
+
type Renderer interface {
Render() (Box, error)
}
@@ -169,6 +179,7 @@ func defaultRender(p *Painter, opt defaultRenderOption) (*defaultRenderResult, e
maxPadRange = *yAxisOption.RangeValuePaddingScale
}
min, max := opt.SeriesList.GetMinMax(index)
+ decimalData := (max - min) != math.Floor(max-min)
if yAxisOption.Min != nil && *yAxisOption.Min < min {
min = *yAxisOption.Min
minPadRange = 0.0
@@ -195,7 +206,7 @@ func defaultRender(p *Painter, opt defaultRenderOption) (*defaultRenderResult, e
if yAxisOption.Unit > 0 {
padLabelCount = int((max-min)/yAxisOption.Unit) + 1
} else {
- padLabelCount = defaultYAxisLabelCount
+ padLabelCount = defaultYAxisLabelCount(max-min, decimalData)
}
}
// we call padRange directly because we need to do this padding before we can calculate the final labelCount for the axisRange
@@ -207,7 +218,7 @@ func defaultRender(p *Painter, opt defaultRenderOption) (*defaultRenderResult, e
}
labelCount = int((max-min)/yAxisOption.Unit) + 1
} else {
- labelCount = defaultYAxisLabelCount
+ labelCount = defaultYAxisLabelCount(max-min, decimalData)
}
yAxisOption.LabelCount = labelCount
}
diff --git a/line_chart_test.go b/line_chart_test.go
index 2bff822..5550bc6 100644
--- a/line_chart_test.go
+++ b/line_chart_test.go
@@ -355,6 +355,34 @@ func TestLineChart(t *testing.T) {
},
result: "",
},
+ {
+ name: "ZeroData",
+ defaultTheme: true,
+ makeOptions: func() LineChartOption {
+ opt := makeMinimalLineChartOption()
+ values := [][]float64{
+ {0, 0, 0, 0, 0, 0, 0},
+ {0, 0, 0, 0, 0, 0, 0},
+ }
+ opt.SeriesList = NewSeriesListDataFromValues(values)
+ return opt
+ },
+ result: "",
+ },
+ {
+ name: "TinyRange",
+ defaultTheme: true,
+ makeOptions: func() LineChartOption {
+ opt := makeMinimalLineChartOption()
+ values := [][]float64{
+ {0.1, 0.2, 0.1, 0.2, 0.4, 0.2, 0.1},
+ {0.2, 0.4, 0.8, 0.4, 0.2, 0.1, 0.2},
+ }
+ opt.SeriesList = NewSeriesListDataFromValues(values)
+ return opt
+ },
+ result: "",
+ },
}
for _, tt := range tests {
diff --git a/mark_line_test.go b/mark_line_test.go
index 3ac4e5b..6f82b78 100644
--- a/mark_line_test.go
+++ b/mark_line_test.go
@@ -29,14 +29,14 @@ func TestMarkLine(t *testing.T) {
FontColor: drawing.ColorBlack,
StrokeColor: drawing.ColorBlack,
Series: series,
- Range: NewRange(p, p.Height(), 6, 0.0, 5.0, 1.0, 1.0),
+ Range: NewRange(p, p.Height(), 6, 0.0, 5.0, 0.0, 0.0),
})
if _, err := markLine.Render(); err != nil {
return nil, err
}
return p.Bytes()
},
- result: "",
+ result: "",
},
}
for i, tt := range tests {
diff --git a/range.go b/range.go
index 3574d68..1d50a1b 100644
--- a/range.go
+++ b/range.go
@@ -8,6 +8,7 @@ const rangeMinPaddingPercentMin = 0.0 // increasing could result in forced negat
const rangeMinPaddingPercentMax = 20.0
const rangeMaxPaddingPercentMin = 5.0 // set minimum spacing at the top of the graph
const rangeMaxPaddingPercentMax = 20.0
+const zeroSpanAdjustment = 1 // Adjustment
type axisRange struct {
p *Painter
@@ -77,8 +78,17 @@ rootLoop:
minResult = minTrunk // remove possible float multiplication inaccuracies
}
- if maxPaddingScale <= 0.0 {
+ if max == minResult {
+ // no adjustment was made and there is no span, because of that the max calculation below can't function
+ // for that reason we apply a default constant span, still wanting to prefer a zero start if possible
+ if minResult == 0 {
+ return minResult, minResult + (2 * zeroSpanAdjustment)
+ }
+ return minResult - zeroSpanAdjustment, minResult + zeroSpanAdjustment
+ } else if maxPaddingScale <= 0.0 {
return minResult, max
+ } else if math.Abs(max) < 10 {
+ return minResult, math.Ceil(max) + 1
}
// update max to provide ideal padding and human friendly intervals