Search Apps Documentation Source Content File Folder Download Copy Actions Download

line_chart.gno

3.59 Kb ยท 142 lines
  1package charts
  2
  3import (
  4	"strings"
  5
  6	"gno.land/p/nt/ufmt"
  7)
  8
  9// GenerateLineChart creates an ASCII line chart in markdown format
 10// values: slice of float values to chart
 11// maxPoints: maximum number of points to display (will normalize if exceeded)
 12// pointSpacing: horizontal spacing between points (in characters)
 13// title: chart title
 14// xAxisTitle: title for the x-axis
 15// yAxisTitle: title for the y-axis
 16// Returns a markdown string representing the chart
 17func GenerateLineChart(values []float64, maxPoints int, pointSpacing int, title, xAxisTitle, yAxisTitle string) string {
 18	if len(values) == 0 {
 19		return "no data to display"
 20	}
 21
 22	if maxPoints <= 0 {
 23		return "maxPoints must be greater than 0"
 24	}
 25
 26	maxVal := findMaxValue(values)
 27	maxValStrLen := calculateMaxValueStringLength(values)
 28
 29	minSpacing := maxValStrLen + 2
 30	if pointSpacing < minSpacing {
 31		return ufmt.Sprintf("pointSpacing must be at least %d to fit value labels", minSpacing)
 32	}
 33	if pointSpacing > 25 {
 34		return ufmt.Sprintf("pointSpacing must be between %d and 25", minSpacing)
 35	}
 36
 37	displayValues := values
 38	if len(values) > maxPoints {
 39		displayValues = normalizeData(values, maxPoints)
 40	}
 41
 42	scale := float64(height) / maxVal
 43
 44	gridWidth := (len(displayValues)-1)*pointSpacing + len(displayValues)
 45	grid := make([][]rune, height+1)
 46	for i := range grid {
 47		grid[i] = make([]rune, gridWidth)
 48		for j := range grid[i] {
 49			grid[i][j] = ' '
 50		}
 51	}
 52
 53	spacing := pointSpacing + 1
 54	for i := 0; i < len(displayValues); i++ {
 55		x := i * spacing
 56		y := int(displayValues[i] * scale)
 57		if y > height {
 58			y = height
 59		}
 60		grid[height-y][x] = '*'
 61
 62		if i < len(displayValues)-1 {
 63			nextX := (i + 1) * spacing
 64			nextY := int(displayValues[i+1] * scale)
 65			if nextY > height {
 66				nextY = height
 67			}
 68			dx := nextX - x
 69			dy := nextY - y
 70			prevInterpY := y
 71			for step := 1; step < dx; step++ {
 72				interpY := y + (dy*step)/dx
 73				startY := prevInterpY
 74				endY := interpY
 75				if startY > endY {
 76					startY, endY = endY, startY
 77				}
 78				for fillY := startY; fillY <= endY; fillY++ {
 79					grid[height-fillY][x+step] = '*'
 80				}
 81				prevInterpY = interpY
 82			}
 83		}
 84	}
 85
 86	output := "\n"
 87	output += formatChartHeader(title)
 88	output += "\n```\n"
 89	output += formatYAxisTitle(yAxisTitle)
 90
 91	maxYValue := ufmt.Sprintf("%.2f", maxVal)
 92	yAxisWidth := len(maxYValue)
 93
 94	for row := 0; row <= height; row++ {
 95		gridRow := grid[row]
 96		gridY := height - row
 97		if gridY%3 == 0 {
 98			output += formatYAxisLabel(float64(gridY), scale, yAxisWidth)
 99		} else {
100			output += formatYAxisSpace(yAxisWidth)
101		}
102		output += string(gridRow)
103		output += "\n"
104	}
105
106	output += strings.Repeat(" ", yAxisWidth) + " +"
107	output += strings.Repeat("-", gridWidth+3)
108	output += "\n"
109
110	output += formatValueLabelsLineChart(displayValues, pointSpacing, yAxisWidth)
111
112	if xAxisTitle != "" {
113		output += formatXAxisTitle(xAxisTitle, gridWidth)
114	}
115
116	output += "```"
117	return output
118}
119
120// formatValueLabelsLineChart formats the value labels for the x-axis for line charts
121func formatValueLabelsLineChart(displayValues []float64, pointSpacing int, yAxisWidth int) string {
122	output := strings.Repeat(" ", yAxisWidth+2)
123	spacing := pointSpacing + 1
124	for i := 0; i < len(displayValues); i++ {
125		x := i * spacing
126		valStr := ufmt.Sprintf("%.2f", displayValues[i])
127		labelPos := x - len(valStr)/2
128		if labelPos < 0 {
129			labelPos = 0
130		}
131		if i == 0 {
132			output += strings.Repeat(" ", labelPos)
133		} else {
134			prevX := (i - 1) * spacing
135			prevValStr := ufmt.Sprintf("%.2f", displayValues[i-1])
136			prevLabelEnd := prevX - len(prevValStr)/2 + len(prevValStr)
137			output += strings.Repeat(" ", labelPos-prevLabelEnd)
138		}
139		output += valStr
140	}
141	return output + "\n"
142}