|
@@ -28,6 +28,7 @@ type ChartLine struct {
|
|
|
title *Label // Optional title label
|
|
title *Label // Optional title label
|
|
|
left float32 // Left margin in pixels
|
|
left float32 // Left margin in pixels
|
|
|
bottom float32 // Bottom margin in pixels
|
|
bottom float32 // Bottom margin in pixels
|
|
|
|
|
+ top float32 // Top margin in pixels
|
|
|
scaleX *ChartScaleX // X scale panel
|
|
scaleX *ChartScaleX // X scale panel
|
|
|
scaleY *ChartScaleY // Y scale panel
|
|
scaleY *ChartScaleY // Y scale panel
|
|
|
startX int // Initial x offset in data buffers
|
|
startX int // Initial x offset in data buffers
|
|
@@ -56,6 +57,7 @@ func NewChartLine(width, height float32) *ChartLine {
|
|
|
cl.Panel.Initialize(width, height)
|
|
cl.Panel.Initialize(width, height)
|
|
|
cl.left = 34
|
|
cl.left = 34
|
|
|
cl.bottom = 20
|
|
cl.bottom = 20
|
|
|
|
|
+ cl.top = 10
|
|
|
cl.startX = 0
|
|
cl.startX = 0
|
|
|
cl.countX = 10
|
|
cl.countX = 10
|
|
|
cl.firstX = 0.0
|
|
cl.firstX = 0.0
|
|
@@ -88,17 +90,19 @@ func (cl *ChartLine) SetTitle(title *Label) {
|
|
|
cl.recalc()
|
|
cl.recalc()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// SetFormatX sets the string format of the X scale labels
|
|
|
func (cl *ChartLine) SetFormatX(format string) {
|
|
func (cl *ChartLine) SetFormatX(format string) {
|
|
|
|
|
|
|
|
cl.formatX = format
|
|
cl.formatX = format
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// SetFormatY sets the string format of the Y scale labels
|
|
|
func (cl *ChartLine) SetFormatY(format string) {
|
|
func (cl *ChartLine) SetFormatY(format string) {
|
|
|
|
|
|
|
|
cl.formatY = format
|
|
cl.formatY = format
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// SetScaleX sets the line chart x scale number of lines and color
|
|
|
|
|
|
|
+// SetScaleX sets the X scale number of lines and color
|
|
|
func (cl *ChartLine) SetScaleX(lines int, color *math32.Color) {
|
|
func (cl *ChartLine) SetScaleX(lines int, color *math32.Color) {
|
|
|
|
|
|
|
|
if cl.scaleX != nil {
|
|
if cl.scaleX != nil {
|
|
@@ -121,7 +125,7 @@ func (cl *ChartLine) SetScaleX(lines int, color *math32.Color) {
|
|
|
cl.recalc()
|
|
cl.recalc()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// ClearScaleX removes the chart x scale if it was previously set
|
|
|
|
|
|
|
+// ClearScaleX removes the X scale if it was previously set
|
|
|
func (cl *ChartLine) ClearScaleX() {
|
|
func (cl *ChartLine) ClearScaleX() {
|
|
|
|
|
|
|
|
if cl.scaleX == nil {
|
|
if cl.scaleX == nil {
|
|
@@ -142,22 +146,25 @@ func (cl *ChartLine) ClearScaleX() {
|
|
|
cl.scaleX = nil
|
|
cl.scaleX = nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// SetScaleY sets the line chart y scale number of lines and color
|
|
|
|
|
|
|
+// SetScaleY sets the Y scale number of lines and color
|
|
|
func (cl *ChartLine) SetScaleY(lines int, color *math32.Color) {
|
|
func (cl *ChartLine) SetScaleY(lines int, color *math32.Color) {
|
|
|
|
|
|
|
|
if cl.scaleY != nil {
|
|
if cl.scaleY != nil {
|
|
|
cl.ClearScaleY()
|
|
cl.ClearScaleY()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ if lines < 2 {
|
|
|
|
|
+ lines = 2
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Add scale lines
|
|
// Add scale lines
|
|
|
cl.scaleY = newChartScaleY(cl, lines, color)
|
|
cl.scaleY = newChartScaleY(cl, lines, color)
|
|
|
cl.Add(cl.scaleY)
|
|
cl.Add(cl.scaleY)
|
|
|
|
|
|
|
|
// Add scale labels
|
|
// Add scale labels
|
|
|
// The position of the labels will be set by 'recalc()'
|
|
// The position of the labels will be set by 'recalc()'
|
|
|
-
|
|
|
|
|
value := cl.minY
|
|
value := cl.minY
|
|
|
- step := (cl.maxY - cl.minY) / float32(lines)
|
|
|
|
|
|
|
+ step := (cl.maxY - cl.minY) / float32(lines-1)
|
|
|
for i := 0; i < lines; i++ {
|
|
for i := 0; i < lines; i++ {
|
|
|
l := NewLabel(fmt.Sprintf(cl.formatY, value))
|
|
l := NewLabel(fmt.Sprintf(cl.formatY, value))
|
|
|
cl.Add(l)
|
|
cl.Add(l)
|
|
@@ -167,7 +174,7 @@ func (cl *ChartLine) SetScaleY(lines int, color *math32.Color) {
|
|
|
cl.recalc()
|
|
cl.recalc()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// ClearScaleY removes the chart x scale if it was previously set
|
|
|
|
|
|
|
+// ClearScaleY removes the Y scale if it was previously set
|
|
|
func (cl *ChartLine) ClearScaleY() {
|
|
func (cl *ChartLine) ClearScaleY() {
|
|
|
|
|
|
|
|
if cl.scaleY == nil {
|
|
if cl.scaleY == nil {
|
|
@@ -271,26 +278,43 @@ func (cl *ChartLine) updateLabelsX() {
|
|
|
if cl.scaleX == nil {
|
|
if cl.scaleX == nil {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
+ pstep := (cl.ContentWidth() - cl.left) / float32(len(cl.labelsX))
|
|
|
value := cl.firstX + float32(cl.startX)*cl.stepX
|
|
value := cl.firstX + float32(cl.startX)*cl.stepX
|
|
|
for i := 0; i < len(cl.labelsX); i++ {
|
|
for i := 0; i < len(cl.labelsX); i++ {
|
|
|
label := cl.labelsX[i]
|
|
label := cl.labelsX[i]
|
|
|
label.SetText(fmt.Sprintf(cl.formatX, value))
|
|
label.SetText(fmt.Sprintf(cl.formatX, value))
|
|
|
|
|
+ px := cl.left + float32(i)*pstep
|
|
|
|
|
+ label.SetPosition(px, cl.ContentHeight()-cl.bottom)
|
|
|
value += cl.stepX
|
|
value += cl.stepX
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// updateLabelsY updates the Y scale labels text
|
|
|
|
|
|
|
+// updateLabelsY updates the Y scale labels text and positions
|
|
|
func (cl *ChartLine) updateLabelsY() {
|
|
func (cl *ChartLine) updateLabelsY() {
|
|
|
|
|
|
|
|
if cl.scaleY == nil {
|
|
if cl.scaleY == nil {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- step := (cl.maxY - cl.minY) / float32(len(cl.labelsY))
|
|
|
|
|
|
|
+
|
|
|
|
|
+ th := float32(0)
|
|
|
|
|
+ if cl.title != nil {
|
|
|
|
|
+ th = cl.title.height
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ nlines := cl.scaleY.lines
|
|
|
|
|
+ vstep := (cl.maxY - cl.minY) / float32(nlines-1)
|
|
|
|
|
+ pstep := (cl.ContentHeight() - th - cl.top - cl.bottom) / float32(nlines-1)
|
|
|
value := cl.minY
|
|
value := cl.minY
|
|
|
- for i := 0; i < len(cl.labelsY); i++ {
|
|
|
|
|
|
|
+ for i := 0; i < nlines; i++ {
|
|
|
label := cl.labelsY[i]
|
|
label := cl.labelsY[i]
|
|
|
label.SetText(fmt.Sprintf(cl.formatY, value))
|
|
label.SetText(fmt.Sprintf(cl.formatY, value))
|
|
|
- value += step
|
|
|
|
|
|
|
+ px := cl.left - 2 - label.Width()
|
|
|
|
|
+ if px < 0 {
|
|
|
|
|
+ px = 0
|
|
|
|
|
+ }
|
|
|
|
|
+ py := cl.ContentHeight() - cl.bottom - float32(i)*pstep
|
|
|
|
|
+ label.SetPosition(px, py-label.Height()/2)
|
|
|
|
|
+ value += vstep
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -325,33 +349,21 @@ func (cl *ChartLine) calcRangeY() {
|
|
|
func (cl *ChartLine) recalc() {
|
|
func (cl *ChartLine) recalc() {
|
|
|
|
|
|
|
|
// Center title position
|
|
// Center title position
|
|
|
- th := float32(0)
|
|
|
|
|
if cl.title != nil {
|
|
if cl.title != nil {
|
|
|
xpos := (cl.ContentWidth() - cl.title.width) / 2
|
|
xpos := (cl.ContentWidth() - cl.title.width) / 2
|
|
|
cl.title.SetPositionX(xpos)
|
|
cl.title.SetPositionX(xpos)
|
|
|
- th = cl.title.height
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Recalc scale X and its labels
|
|
// Recalc scale X and its labels
|
|
|
if cl.scaleX != nil {
|
|
if cl.scaleX != nil {
|
|
|
cl.scaleX.recalc()
|
|
cl.scaleX.recalc()
|
|
|
- step := (cl.ContentWidth() - cl.left) / float32(len(cl.labelsX))
|
|
|
|
|
- for i := 0; i < len(cl.labelsX); i++ {
|
|
|
|
|
- label := cl.labelsX[i]
|
|
|
|
|
- px := cl.left + float32(i)*step
|
|
|
|
|
- label.SetPosition(px, cl.ContentHeight()-cl.bottom)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ cl.updateLabelsX()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Recalc scale Y and its labels
|
|
// Recalc scale Y and its labels
|
|
|
if cl.scaleY != nil {
|
|
if cl.scaleY != nil {
|
|
|
cl.scaleY.recalc()
|
|
cl.scaleY.recalc()
|
|
|
- step := (cl.ContentHeight() - cl.bottom - th) / float32(len(cl.labelsY))
|
|
|
|
|
- for i := 0; i < len(cl.labelsY); i++ {
|
|
|
|
|
- label := cl.labelsY[i]
|
|
|
|
|
- py := cl.ContentHeight() - cl.bottom - float32(i)*step
|
|
|
|
|
- label.SetPosition(0, py-label.Height()/2)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ cl.updateLabelsY()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Recalc graphs
|
|
// Recalc graphs
|
|
@@ -360,11 +372,6 @@ func (cl *ChartLine) recalc() {
|
|
|
g.recalc()
|
|
g.recalc()
|
|
|
cl.SetTopChild(g)
|
|
cl.SetTopChild(g)
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- // Sets the title at the top
|
|
|
|
|
- if cl.title != nil {
|
|
|
|
|
- cl.SetTopChild(cl.title)
|
|
|
|
|
- }
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//
|
|
@@ -425,9 +432,9 @@ func (sx *ChartScaleX) setLabelsText(x []float32) {
|
|
|
// recalc recalculates the position and size of this scale inside its parent
|
|
// recalc recalculates the position and size of this scale inside its parent
|
|
|
func (sx *ChartScaleX) recalc() {
|
|
func (sx *ChartScaleX) recalc() {
|
|
|
|
|
|
|
|
- py := float32(0)
|
|
|
|
|
|
|
+ py := sx.chart.top
|
|
|
if sx.chart.title != nil {
|
|
if sx.chart.title != nil {
|
|
|
- py = sx.chart.title.Height()
|
|
|
|
|
|
|
+ py += sx.chart.title.Height()
|
|
|
}
|
|
}
|
|
|
sx.SetPosition(sx.chart.left, py)
|
|
sx.SetPosition(sx.chart.left, py)
|
|
|
sx.SetSize(sx.chart.ContentWidth()-sx.chart.left, sx.chart.ContentHeight()-py-sx.chart.bottom)
|
|
sx.SetSize(sx.chart.ContentWidth()-sx.chart.left, sx.chart.ContentHeight()-py-sx.chart.bottom)
|
|
@@ -451,10 +458,12 @@ func (sx *ChartScaleX) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
|
|
|
sx.bounds.Transfer(gs)
|
|
sx.bounds.Transfer(gs)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+//
|
|
|
//
|
|
//
|
|
|
// ChartScaleY is a panel with LINE geometry which draws the chart Y vertical scale axis,
|
|
// ChartScaleY is a panel with LINE geometry which draws the chart Y vertical scale axis,
|
|
|
// horizontal and labels.
|
|
// horizontal and labels.
|
|
|
//
|
|
//
|
|
|
|
|
+//
|
|
|
type ChartScaleY struct {
|
|
type ChartScaleY struct {
|
|
|
Panel // Embedded panel
|
|
Panel // Embedded panel
|
|
|
chart *ChartLine // Container chart
|
|
chart *ChartLine // Container chart
|
|
@@ -467,6 +476,9 @@ type ChartScaleY struct {
|
|
|
// chart, number of lines and color
|
|
// chart, number of lines and color
|
|
|
func newChartScaleY(chart *ChartLine, lines int, color *math32.Color) *ChartScaleY {
|
|
func newChartScaleY(chart *ChartLine, lines int, color *math32.Color) *ChartScaleY {
|
|
|
|
|
|
|
|
|
|
+ if lines < 2 {
|
|
|
|
|
+ lines = 2
|
|
|
|
|
+ }
|
|
|
sy := new(ChartScaleY)
|
|
sy := new(ChartScaleY)
|
|
|
sy.chart = chart
|
|
sy.chart = chart
|
|
|
sy.lines = lines
|
|
sy.lines = lines
|
|
@@ -477,12 +489,15 @@ func newChartScaleY(chart *ChartLine, lines int, color *math32.Color) *ChartScal
|
|
|
positions.Append(0+deltaLine, 0, 0, 0+deltaLine, -1, 0)
|
|
positions.Append(0+deltaLine, 0, 0, 0+deltaLine, -1, 0)
|
|
|
|
|
|
|
|
// Appends horizontal lines
|
|
// Appends horizontal lines
|
|
|
- step := 1 / float32(lines)
|
|
|
|
|
|
|
+ step := 1 / float32(lines-1)
|
|
|
for i := 0; i < lines; i++ {
|
|
for i := 0; i < lines; i++ {
|
|
|
ny := -1 + float32(i)*step
|
|
ny := -1 + float32(i)*step
|
|
|
if i == 0 {
|
|
if i == 0 {
|
|
|
ny += deltaLine
|
|
ny += deltaLine
|
|
|
}
|
|
}
|
|
|
|
|
+ if i == lines-1 {
|
|
|
|
|
+ ny -= deltaLine
|
|
|
|
|
+ }
|
|
|
positions.Append(0, ny, 0, 1, ny, 0)
|
|
positions.Append(0, ny, 0, 1, ny, 0)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -503,9 +518,9 @@ func newChartScaleY(chart *ChartLine, lines int, color *math32.Color) *ChartScal
|
|
|
// recalc recalculates the position and size of this scale inside its parent
|
|
// recalc recalculates the position and size of this scale inside its parent
|
|
|
func (sy *ChartScaleY) recalc() {
|
|
func (sy *ChartScaleY) recalc() {
|
|
|
|
|
|
|
|
- py := float32(0)
|
|
|
|
|
|
|
+ py := sy.chart.top
|
|
|
if sy.chart.title != nil {
|
|
if sy.chart.title != nil {
|
|
|
- py = sy.chart.title.Height()
|
|
|
|
|
|
|
+ py += sy.chart.title.Height()
|
|
|
}
|
|
}
|
|
|
sy.SetPosition(sy.chart.left, py)
|
|
sy.SetPosition(sy.chart.left, py)
|
|
|
sy.SetSize(sy.chart.ContentWidth()-sy.chart.left, sy.chart.ContentHeight()-py-sy.chart.bottom)
|
|
sy.SetSize(sy.chart.ContentWidth()-sy.chart.left, sy.chart.ContentHeight()-py-sy.chart.bottom)
|
|
@@ -607,14 +622,13 @@ func (lg *LineGraph) updateData() {
|
|
|
|
|
|
|
|
func (lg *LineGraph) recalc() {
|
|
func (lg *LineGraph) recalc() {
|
|
|
|
|
|
|
|
- px := lg.chart.left
|
|
|
|
|
- py := float32(0)
|
|
|
|
|
- w := lg.chart.ContentWidth() - lg.chart.left
|
|
|
|
|
- h := lg.chart.ContentHeight() - lg.chart.bottom
|
|
|
|
|
|
|
+ py := lg.chart.top
|
|
|
if lg.chart.title != nil {
|
|
if lg.chart.title != nil {
|
|
|
py += lg.chart.title.Height()
|
|
py += lg.chart.title.Height()
|
|
|
- h -= lg.chart.title.Height()
|
|
|
|
|
}
|
|
}
|
|
|
|
|
+ px := lg.chart.left
|
|
|
|
|
+ w := lg.chart.ContentWidth() - lg.chart.left
|
|
|
|
|
+ h := lg.chart.ContentHeight() - py - lg.chart.bottom
|
|
|
lg.SetPosition(px, py)
|
|
lg.SetPosition(px, py)
|
|
|
lg.SetSize(w, h)
|
|
lg.SetSize(w, h)
|
|
|
}
|
|
}
|