chart.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. package gui
  2. import (
  3. "fmt"
  4. "github.com/g3n/engine/core"
  5. "github.com/g3n/engine/geometry"
  6. "github.com/g3n/engine/gls"
  7. "github.com/g3n/engine/graphic"
  8. "github.com/g3n/engine/material"
  9. "github.com/g3n/engine/math32"
  10. "github.com/g3n/engine/renderer/shader"
  11. )
  12. func init() {
  13. shader.AddShader("shaderChartVertex", shaderChartVertex)
  14. shader.AddShader("shaderChartFrag", shaderChartFrag)
  15. shader.AddProgram("shaderChart", "shaderChartVertex", "shaderChartFrag")
  16. }
  17. // ChartLine implements a panel which can contain several line charts
  18. type ChartLine struct {
  19. Panel // Embedded panel
  20. grid *ChartGrid // Optional chart grid
  21. baseX float32 // NDC x coordinate for y axis
  22. baseY float32 // NDC y coordinate for x axis
  23. scaleX *ChartScaleX // X scale objet
  24. firstX float32 // First value for X label
  25. stepX float32 // Step value for X label
  26. formatX string // Format for X labels
  27. scaleY *ChartScaleY // Y scale objet
  28. graphs []*LineGraph // Array of line graphs
  29. }
  30. // NewChartLine creates and returns a new line chart panel with
  31. // the specified dimensions in pixels.
  32. func NewChartLine(width, height float32) *ChartLine {
  33. cl := new(ChartLine)
  34. cl.Panel.Initialize(width, height)
  35. cl.baseX = 0.1
  36. cl.baseY = -0.9
  37. cl.firstX = 0.0
  38. cl.stepX = 1.0
  39. cl.formatX = "%2.1f"
  40. return cl
  41. }
  42. // SetGrid sets the line chart grid with the specified number of
  43. // grid lines and color
  44. func (cl *ChartLine) SetGrid(xcount, ycount int, color *math32.Color) {
  45. if cl.grid != nil {
  46. cl.Node.Remove(cl.grid)
  47. cl.grid.Dispose()
  48. }
  49. cl.grid = NewChartGrid(&cl.Panel, xcount, ycount, color)
  50. cl.Node.Add(cl.grid)
  51. }
  52. // SetScaleX sets the line chart x scale number of lines and color
  53. func (cl *ChartLine) SetScaleX(lines int, color *math32.Color) {
  54. if cl.scaleX != nil {
  55. cl.Node.Remove(cl.scaleX)
  56. cl.scaleX.Dispose()
  57. }
  58. cl.scaleX = newChartScaleX(cl, lines, color)
  59. cl.Node.Add(cl.scaleX)
  60. }
  61. // SetScaleY sets the line chart y scale number of lines and color
  62. func (cl *ChartLine) SetScaleY(lines int, color *math32.Color) {
  63. if cl.scaleY != nil {
  64. cl.Node.Remove(cl.scaleY)
  65. cl.scaleY.Dispose()
  66. }
  67. cl.scaleY = newChartScaleY(cl, lines, color)
  68. cl.Node.Add(cl.scaleY)
  69. }
  70. // AddLine adds a line graph to the chart
  71. func (cl *ChartLine) AddGraph(name, title string, color *math32.Color, data []float32) {
  72. graph := newLineGraph(&cl.Panel, name, title, color, data)
  73. cl.graphs = append(cl.graphs, graph)
  74. cl.Node.Add(graph)
  75. }
  76. //
  77. //
  78. //
  79. // ChartScaleX
  80. //
  81. //
  82. //
  83. type ChartScaleX struct {
  84. graphic.Graphic // It is a Graphic
  85. chart *ChartLine // Container chart
  86. modelMatrixUni gls.UniformMatrix4f // Model matrix uniform
  87. }
  88. func newChartScaleX(chart *ChartLine, lines int, color *math32.Color) *ChartScaleX {
  89. sx := new(ChartScaleX)
  90. sx.chart = chart
  91. // Generates grid lines using Normalized Device Coordinates and
  92. // considering that the parent panel model coordinates are:
  93. // 0,0,0 1,0,0
  94. // +---------------+
  95. // | |
  96. // | |
  97. // +---------------+
  98. // 0,-1,0 1,-1,0
  99. positions := math32.NewArrayF32(0, 0)
  100. // Appends scaleX bottom horizontal base line
  101. positions.Append(
  102. 0, chart.baseY, 0, color.R, color.G, color.B, // line start vertex and color
  103. 1, chart.baseY, 0, color.R, color.G, color.B, // line end vertex and color
  104. )
  105. // Appends scale X vertical lines
  106. step := 1 / (float32(lines) + 1)
  107. for i := 1; i < lines+1; i++ {
  108. nx := float32(i) * step
  109. positions.Append(
  110. nx, 0, 0, color.R, color.G, color.B, // line start vertex and color
  111. nx, chart.baseY, 0, color.R, color.G, color.B, // line end vertex and color
  112. )
  113. l := NewLabel(fmt.Sprintf(sx.chart.formatX, float32(i)))
  114. px, py := ndc2pix(&sx.chart.Panel, nx, chart.baseY)
  115. l.SetPosition(px, py)
  116. sx.chart.Add(l)
  117. }
  118. // Creates geometry using one interlaced VBO
  119. geom := geometry.NewGeometry()
  120. geom.AddVBO(gls.NewVBO().
  121. AddAttrib("VertexPosition", 3).
  122. AddAttrib("VertexColor", 3).
  123. SetBuffer(positions),
  124. )
  125. // Creates material
  126. mat := material.NewMaterial()
  127. mat.SetLineWidth(1.0)
  128. mat.SetShader("shaderChart")
  129. // Initializes the grid graphic
  130. sx.Graphic.Init(geom, gls.LINES)
  131. sx.AddMaterial(sx, mat, 0, 0)
  132. sx.modelMatrixUni.Init("ModelMatrix")
  133. return sx
  134. }
  135. // Converts panel ndc coordinates to relative pixels inside panel
  136. func ndc2pix(p *Panel, nx, ny float32) (px, py float32) {
  137. w := p.ContentWidth()
  138. h := p.ContentHeight()
  139. return w * nx, -h * ny
  140. }
  141. // RenderSetup is called by the renderer before drawing this graphic
  142. // Calculates the model matrix and transfer to OpenGL.
  143. func (sx *ChartScaleX) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  144. // Set this model matrix the same as the chart panel
  145. var mm math32.Matrix4
  146. sx.chart.SetModelMatrix(gs, &mm)
  147. // Sets and transfer the model matrix uniform
  148. sx.modelMatrixUni.SetMatrix4(&mm)
  149. sx.modelMatrixUni.Transfer(gs)
  150. }
  151. //
  152. //
  153. //
  154. // ChartScaleY
  155. //
  156. //
  157. //
  158. type ChartScaleY struct {
  159. graphic.Graphic // It is a Graphic
  160. chart *ChartLine // Container chart
  161. modelMatrixUni gls.UniformMatrix4f // Model matrix uniform
  162. }
  163. func newChartScaleY(chart *ChartLine, lines int, color *math32.Color) *ChartScaleY {
  164. sy := new(ChartScaleY)
  165. sy.chart = chart
  166. // Generates grid lines using Normalized Device Coordinates and
  167. // considering that the parent panel model coordinates are:
  168. // 0,0,0 1,0,0
  169. // +---------------+
  170. // | |
  171. // | |
  172. // +---------------+
  173. // 0,-1,0 1,-1,0
  174. positions := math32.NewArrayF32(0, 0)
  175. // Appends scaleY left vertical axis line
  176. positions.Append(
  177. chart.baseX, 0, 0, color.R, color.G, color.B, // line start vertex and color
  178. chart.baseX, -1, 0, color.R, color.G, color.B, // line end vertex and color
  179. )
  180. // Appends scale horizontal lines starting from baseX
  181. step := 1 / (float32(lines) + 1)
  182. for i := 1; i < lines+1; i++ {
  183. ny := -float32(i) * step
  184. positions.Append(
  185. chart.baseX, ny, 0, color.R, color.G, color.B, // line start vertex and color
  186. 1, ny, 0, color.R, color.G, color.B, // line end vertex and color
  187. )
  188. // l := NewLabel(fmt.Sprintf(sx.chart.formatX, float32(i)))
  189. // px, py := ndc2pix(&sx.chart.Panel, nx, baseY)
  190. // l.SetPosition(px, py)
  191. // sx.chart.Add(l)
  192. }
  193. // Creates geometry using one interlaced VBO
  194. geom := geometry.NewGeometry()
  195. geom.AddVBO(gls.NewVBO().
  196. AddAttrib("VertexPosition", 3).
  197. AddAttrib("VertexColor", 3).
  198. SetBuffer(positions),
  199. )
  200. // Creates material
  201. mat := material.NewMaterial()
  202. mat.SetLineWidth(1.0)
  203. mat.SetShader("shaderChart")
  204. // Initializes the grid graphic
  205. sy.Graphic.Init(geom, gls.LINES)
  206. sy.AddMaterial(sy, mat, 0, 0)
  207. sy.modelMatrixUni.Init("ModelMatrix")
  208. return sy
  209. }
  210. // RenderSetup is called by the renderer before drawing this graphic
  211. // Calculates the model matrix and transfer to OpenGL.
  212. func (sy *ChartScaleY) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  213. // Set this model matrix the same as the chart panel
  214. var mm math32.Matrix4
  215. sy.chart.SetModelMatrix(gs, &mm)
  216. // Sets and transfer the model matrix uniform
  217. sy.modelMatrixUni.SetMatrix4(&mm)
  218. sy.modelMatrixUni.Transfer(gs)
  219. }
  220. //
  221. //
  222. //
  223. // ChartGrid
  224. //
  225. //
  226. //
  227. // ChartGrid implements a 2D grid used inside charts
  228. type ChartGrid struct {
  229. graphic.Graphic // It is a Graphic
  230. chart *Panel // Container chart
  231. modelMatrixUni gls.UniformMatrix4f // Model matrix uniform
  232. }
  233. // NewChartGrid creates and returns a pointer to a chart grid graphic for the
  234. // specified parent chart and with the specified number of grid lines and color.
  235. func NewChartGrid(chart *Panel, xcount, ycount int, color *math32.Color) *ChartGrid {
  236. cg := new(ChartGrid)
  237. cg.chart = chart
  238. // Generates grid lines using Normalized Device Coordinates and
  239. // considering that the parent panel model coordinates are:
  240. // 0,0,0 1,0,0
  241. // +---------------+
  242. // | |
  243. // | |
  244. // +---------------+
  245. // 0,-1,0 1,-1,0
  246. positions := math32.NewArrayF32(0, 0)
  247. xstep := 1 / (float32(xcount) + 1)
  248. for xi := 1; xi < xcount+1; xi++ {
  249. posx := float32(xi) * xstep
  250. positions.Append(
  251. posx, 0, 0, color.R, color.G, color.B, // line start vertex and color
  252. posx, -1, 0, color.R, color.G, color.B, // line end vertex and color
  253. )
  254. }
  255. ystep := 1 / (float32(ycount) + 1)
  256. for yi := 1; yi < ycount+1; yi++ {
  257. posy := -float32(yi) * ystep
  258. positions.Append(
  259. 0, posy, 0, color.R, color.G, color.B, // line start vertex and color
  260. 1, posy, 0, color.R, color.G, color.B, // line end vertex and color
  261. )
  262. }
  263. // Creates geometry using one interlaced VBO
  264. geom := geometry.NewGeometry()
  265. geom.AddVBO(
  266. gls.NewVBO().
  267. AddAttrib("VertexPosition", 3).
  268. AddAttrib("VertexColor", 3).
  269. SetBuffer(positions),
  270. )
  271. // Creates material
  272. mat := material.NewMaterial()
  273. mat.SetLineWidth(1.0)
  274. mat.SetShader("shaderChart")
  275. // Initializes the grid graphic
  276. cg.Graphic.Init(geom, gls.LINES)
  277. cg.AddMaterial(cg, mat, 0, 0)
  278. cg.modelMatrixUni.Init("ModelMatrix")
  279. return cg
  280. }
  281. // RenderSetup is called by the renderer before drawing this graphic
  282. // Calculates the model matrix and transfer to OpenGL.
  283. func (cg *ChartGrid) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  284. // Set this model matrix the same as the chart panel
  285. var mm math32.Matrix4
  286. cg.chart.SetModelMatrix(gs, &mm)
  287. // Sets and transfer the model matrix uniform
  288. cg.modelMatrixUni.SetMatrix4(&mm)
  289. cg.modelMatrixUni.Transfer(gs)
  290. }
  291. //
  292. //
  293. //
  294. // LineGraph
  295. //
  296. //
  297. //
  298. // LineGraph implemens a 2D line graph
  299. type LineGraph struct {
  300. graphic.Graphic // It is a Graphic
  301. chart *Panel // Container chart
  302. modelMatrixUni gls.UniformMatrix4f // Model matrix uniform
  303. name string // Name id
  304. title string // Title string
  305. color *math32.Color // Line color
  306. data []float32 // Data
  307. }
  308. // newLineGraph creates and returns a pointer to a line graph graphic for the
  309. // specified parent chart
  310. func newLineGraph(chart *Panel, name, title string, color *math32.Color, data []float32) *LineGraph {
  311. lg := new(LineGraph)
  312. lg.chart = chart
  313. lg.name = name
  314. lg.title = title
  315. lg.color = color
  316. lg.data = data
  317. // Generates graph lines using Normalized Device Coordinates and
  318. // considering that the parent panel model coordinates are:
  319. // 0,0,0 1,0,0
  320. // +---------------+
  321. // | |
  322. // | |
  323. // +---------------+
  324. // 0,-1,0 1,-1,0
  325. positions := math32.NewArrayF32(0, 0)
  326. for i := 0; i < len(data); i++ {
  327. px := float32(i) / float32(len(data))
  328. py := -1 + data[i]
  329. positions.Append(px, py, 0, color.R, color.G, color.B)
  330. }
  331. // Creates geometry using one interlaced VBO
  332. geom := geometry.NewGeometry()
  333. geom.AddVBO(gls.NewVBO().
  334. AddAttrib("VertexPosition", 3).
  335. AddAttrib("VertexColor", 3).
  336. SetBuffer(positions),
  337. )
  338. // Creates material
  339. mat := material.NewMaterial()
  340. mat.SetLineWidth(2.5)
  341. mat.SetShader("shaderChart")
  342. // Initializes the graphic
  343. lg.Graphic.Init(geom, gls.LINE_STRIP)
  344. lg.AddMaterial(lg, mat, 0, 0)
  345. lg.modelMatrixUni.Init("ModelMatrix")
  346. return lg
  347. }
  348. // RenderSetup is called by the renderer before drawing this graphic
  349. // Calculates the model matrix and transfer to OpenGL.
  350. func (lg *LineGraph) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  351. // Set this graphic model matrix the same as the container chart panel
  352. var mm math32.Matrix4
  353. lg.chart.SetModelMatrix(gs, &mm)
  354. // Sets and transfer the model matrix uniform
  355. lg.modelMatrixUni.SetMatrix4(&mm)
  356. lg.modelMatrixUni.Transfer(gs)
  357. }
  358. //
  359. // Vertex Shader template
  360. //
  361. const shaderChartVertex = `
  362. #version {{.Version}}
  363. // Vertex attributes
  364. {{template "attributes" .}}
  365. // Input uniforms
  366. uniform mat4 ModelMatrix;
  367. // Outputs for fragment shader
  368. out vec3 Color;
  369. void main() {
  370. Color = VertexColor;
  371. // Set position
  372. vec4 pos = vec4(VertexPosition.xyz, 1);
  373. gl_Position = ModelMatrix * pos;
  374. }
  375. `
  376. //
  377. // Fragment Shader template
  378. //
  379. const shaderChartFrag = `
  380. #version {{.Version}}
  381. in vec3 Color;
  382. out vec4 FragColor;
  383. void main() {
  384. FragColor = vec4(Color, 1.0);
  385. }
  386. `