chart.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package gui
  5. import (
  6. "fmt"
  7. "github.com/g3n/engine/core"
  8. "github.com/g3n/engine/geometry"
  9. "github.com/g3n/engine/gls"
  10. "github.com/g3n/engine/graphic"
  11. "github.com/g3n/engine/material"
  12. "github.com/g3n/engine/math32"
  13. "github.com/g3n/engine/renderer/shader"
  14. "math"
  15. )
  16. func init() {
  17. shader.AddShader("shaderChartVertex", shaderChartVertex)
  18. shader.AddShader("shaderChartFrag", shaderChartFrag)
  19. shader.AddProgram("shaderChart", "shaderChartVertex", "shaderChartFrag")
  20. }
  21. //
  22. // Chart implements a panel which can contain a title, an x scale,
  23. // an y scale and several graphs
  24. //
  25. type Chart struct {
  26. Panel // Embedded panel
  27. left float32 // Left margin in pixels
  28. bottom float32 // Bottom margin in pixels
  29. top float32 // Top margin in pixels
  30. firstX float32 // Value for the first x label
  31. stepX float32 // Step for the next x label
  32. countStepX float32 // Number of values per x step
  33. minY float32 // Minimum Y value
  34. maxY float32 // Maximum Y value
  35. autoY bool // Auto range flag for Y values
  36. formatX string // String format for scale X labels
  37. formatY string // String format for scale Y labels
  38. fontSizeX float64 // X scale label font size
  39. fontSizeY float64 // Y scale label font size
  40. title *Label // Optional title label
  41. scaleX *chartScaleX // X scale panel
  42. scaleY *chartScaleY // Y scale panel
  43. labelsX []*Label // Array of scale X labels
  44. labelsY []*Label // Array of scale Y labels
  45. graphs []*Graph // Array of line graphs
  46. }
  47. const (
  48. deltaLine = 0.001 // Delta in NDC for lines over the boundary
  49. )
  50. // NewChart creates and returns a new chart panel with
  51. // the specified dimensions in pixels.
  52. func NewChart(width, height float32) *Chart {
  53. ch := new(Chart)
  54. ch.Panel.Initialize(width, height)
  55. ch.left = 40
  56. ch.bottom = 20
  57. ch.top = 10
  58. ch.firstX = 0
  59. ch.stepX = 1
  60. ch.countStepX = 1
  61. ch.minY = -10.0
  62. ch.maxY = 10.0
  63. ch.autoY = false
  64. ch.formatX = "%v"
  65. ch.formatY = "%v"
  66. ch.fontSizeX = 14
  67. ch.fontSizeY = 14
  68. return ch
  69. }
  70. // SetTitle sets the chart title text and font size.
  71. // To remove the title pass an empty string
  72. func (ch *Chart) SetTitle(title string, size float64) {
  73. // Remove title
  74. if title == "" {
  75. if ch.title != nil {
  76. ch.Remove(ch.title)
  77. ch.title.Dispose()
  78. ch.title = nil
  79. ch.recalc()
  80. }
  81. return
  82. }
  83. // Sets title
  84. if ch.title == nil {
  85. ch.title = NewLabel(title)
  86. ch.Add(ch.title)
  87. }
  88. ch.title.SetText(title)
  89. ch.title.SetFontSize(size)
  90. ch.recalc()
  91. }
  92. // SetMarginY sets the y scale margin
  93. func (ch *Chart) SetMarginY(left float32) {
  94. ch.left = left
  95. ch.recalc()
  96. }
  97. // SetMarginX sets the x scale margin
  98. func (ch *Chart) SetMarginX(bottom float32) {
  99. ch.bottom = bottom
  100. ch.recalc()
  101. }
  102. // SetFormatX sets the string format of the X scale labels
  103. func (ch *Chart) SetFormatX(format string) {
  104. ch.formatX = format
  105. ch.updateLabelsX()
  106. }
  107. // SetFormatY sets the string format of the Y scale labels
  108. func (ch *Chart) SetFormatY(format string) {
  109. ch.formatY = format
  110. ch.updateLabelsY()
  111. }
  112. // SetFontSizeX sets the font size for the x scale labels
  113. func (ch *Chart) SetFontSizeX(size float64) {
  114. ch.fontSizeX = size
  115. for i := 0; i < len(ch.labelsX); i++ {
  116. ch.labelsX[i].SetFontSize(ch.fontSizeX)
  117. }
  118. }
  119. // SetFontSizeY sets the font size for the y scale labels
  120. func (ch *Chart) SetFontSizeY(size float64) {
  121. ch.fontSizeY = size
  122. for i := 0; i < len(ch.labelsY); i++ {
  123. ch.labelsY[i].SetFontSize(ch.fontSizeY)
  124. }
  125. }
  126. // SetScaleX sets the X scale number of lines, lines color and label font size
  127. func (ch *Chart) SetScaleX(lines int, color *math32.Color) {
  128. if ch.scaleX != nil {
  129. ch.ClearScaleX()
  130. }
  131. // Add scale lines
  132. ch.scaleX = newChartScaleX(ch, lines, color)
  133. ch.Add(ch.scaleX)
  134. // Add scale labels
  135. // The positions of the labels will be set by 'recalc()'
  136. value := ch.firstX
  137. for i := 0; i < lines; i++ {
  138. l := NewLabel(fmt.Sprintf(ch.formatX, value))
  139. l.SetFontSize(ch.fontSizeX)
  140. ch.Add(l)
  141. ch.labelsX = append(ch.labelsX, l)
  142. value += ch.stepX
  143. }
  144. ch.recalc()
  145. }
  146. // ClearScaleX removes the X scale if it was previously set
  147. func (ch *Chart) ClearScaleX() {
  148. if ch.scaleX == nil {
  149. return
  150. }
  151. // Remove and dispose scale lines
  152. ch.Remove(ch.scaleX)
  153. ch.scaleX.Dispose()
  154. // Remove and dispose scale labels
  155. for i := 0; i < len(ch.labelsX); i++ {
  156. label := ch.labelsX[i]
  157. ch.Remove(label)
  158. label.Dispose()
  159. }
  160. ch.labelsX = ch.labelsX[0:0]
  161. ch.scaleX = nil
  162. }
  163. // SetScaleY sets the Y scale number of lines and color
  164. func (ch *Chart) SetScaleY(lines int, color *math32.Color) {
  165. if ch.scaleY != nil {
  166. ch.ClearScaleY()
  167. }
  168. if lines < 2 {
  169. lines = 2
  170. }
  171. // Add scale lines
  172. ch.scaleY = newChartScaleY(ch, lines, color)
  173. ch.Add(ch.scaleY)
  174. // Add scale labels
  175. // The position of the labels will be set by 'recalc()'
  176. value := ch.minY
  177. step := (ch.maxY - ch.minY) / float32(lines-1)
  178. for i := 0; i < lines; i++ {
  179. l := NewLabel(fmt.Sprintf(ch.formatY, value))
  180. l.SetFontSize(ch.fontSizeY)
  181. ch.Add(l)
  182. ch.labelsY = append(ch.labelsY, l)
  183. value += step
  184. }
  185. ch.recalc()
  186. }
  187. // ClearScaleY removes the Y scale if it was previously set
  188. func (ch *Chart) ClearScaleY() {
  189. if ch.scaleY == nil {
  190. return
  191. }
  192. // Remove and dispose scale lines
  193. ch.Remove(ch.scaleY)
  194. ch.scaleY.Dispose()
  195. // Remove and dispose scale labels
  196. for i := 0; i < len(ch.labelsY); i++ {
  197. label := ch.labelsY[i]
  198. ch.Remove(label)
  199. label.Dispose()
  200. }
  201. ch.labelsY = ch.labelsY[0:0]
  202. ch.scaleY = nil
  203. }
  204. // SetRangeX sets the X scale labels and range per step
  205. // firstX is the value of first label of the x scale
  206. // stepX is the step to be added to get the next x scale label
  207. // countStepX is the number of elements of the data buffer for each line step
  208. func (ch *Chart) SetRangeX(firstX float32, stepX float32, countStepX float32) {
  209. ch.firstX = firstX
  210. ch.stepX = stepX
  211. ch.countStepX = countStepX
  212. ch.updateGraphs()
  213. }
  214. // SetRangeY sets the minimum and maximum values of the y scale
  215. func (ch *Chart) SetRangeY(min float32, max float32) {
  216. if ch.autoY {
  217. return
  218. }
  219. ch.minY = min
  220. ch.maxY = max
  221. ch.updateGraphs()
  222. }
  223. // SetRangeYauto sets the state of the auto
  224. func (ch *Chart) SetRangeYauto(auto bool) {
  225. ch.autoY = auto
  226. if !auto {
  227. return
  228. }
  229. ch.updateGraphs()
  230. }
  231. // Returns the current y range
  232. func (ch *Chart) RangeY() (minY, maxY float32) {
  233. return ch.minY, ch.maxY
  234. }
  235. // AddLineGraph adds a line graph to the chart
  236. func (ch *Chart) AddLineGraph(color *math32.Color, data []float32) *Graph {
  237. graph := newGraph(ch, color, data)
  238. ch.graphs = append(ch.graphs, graph)
  239. ch.Add(graph)
  240. ch.recalc()
  241. ch.updateGraphs()
  242. return graph
  243. }
  244. // RemoveGraph removes and disposes of the specified graph from the chart
  245. func (ch *Chart) RemoveGraph(g *Graph) {
  246. ch.Remove(g)
  247. g.Dispose()
  248. for pos, current := range ch.graphs {
  249. if current == g {
  250. copy(ch.graphs[pos:], ch.graphs[pos+1:])
  251. ch.graphs[len(ch.graphs)-1] = nil
  252. ch.graphs = ch.graphs[:len(ch.graphs)-1]
  253. break
  254. }
  255. }
  256. if !ch.autoY {
  257. return
  258. }
  259. ch.updateGraphs()
  260. }
  261. // updateLabelsX updates the X scale labels text
  262. func (ch *Chart) updateLabelsX() {
  263. if ch.scaleX == nil {
  264. return
  265. }
  266. pstep := (ch.ContentWidth() - ch.left) / float32(len(ch.labelsX))
  267. value := ch.firstX
  268. for i := 0; i < len(ch.labelsX); i++ {
  269. label := ch.labelsX[i]
  270. label.SetText(fmt.Sprintf(ch.formatX, value))
  271. px := ch.left + float32(i)*pstep
  272. label.SetPosition(px, ch.ContentHeight()-ch.bottom)
  273. value += ch.stepX
  274. }
  275. }
  276. // updateLabelsY updates the Y scale labels text and positions
  277. func (ch *Chart) updateLabelsY() {
  278. if ch.scaleY == nil {
  279. return
  280. }
  281. th := float32(0)
  282. if ch.title != nil {
  283. th = ch.title.height
  284. }
  285. nlines := ch.scaleY.lines
  286. vstep := (ch.maxY - ch.minY) / float32(nlines-1)
  287. pstep := (ch.ContentHeight() - th - ch.top - ch.bottom) / float32(nlines-1)
  288. value := ch.minY
  289. for i := 0; i < nlines; i++ {
  290. label := ch.labelsY[i]
  291. label.SetText(fmt.Sprintf(ch.formatY, value))
  292. px := ch.left - 4 - label.Width()
  293. if px < 0 {
  294. px = 0
  295. }
  296. py := ch.ContentHeight() - ch.bottom - float32(i)*pstep
  297. label.SetPosition(px, py-label.Height()/2)
  298. value += vstep
  299. }
  300. }
  301. // calcRangeY calculates the minimum and maximum y values for all graphs
  302. func (ch *Chart) calcRangeY() {
  303. if !ch.autoY || len(ch.graphs) == 0 {
  304. return
  305. }
  306. minY := float32(math.MaxFloat32)
  307. maxY := -float32(math.MaxFloat32)
  308. for g := 0; g < len(ch.graphs); g++ {
  309. graph := ch.graphs[g]
  310. for x := 0; x < len(graph.data); x++ {
  311. vy := graph.data[x]
  312. if vy < minY {
  313. minY = vy
  314. }
  315. if vy > maxY {
  316. maxY = vy
  317. }
  318. }
  319. }
  320. ch.minY = minY
  321. ch.maxY = maxY
  322. }
  323. // updateGraphs should be called when the range the scales change or
  324. // any graph data changes
  325. func (ch *Chart) updateGraphs() {
  326. ch.calcRangeY()
  327. ch.updateLabelsX()
  328. ch.updateLabelsY()
  329. for i := 0; i < len(ch.graphs); i++ {
  330. g := ch.graphs[i]
  331. g.updateData()
  332. }
  333. }
  334. // recalc recalculates the positions of the inner panels
  335. func (ch *Chart) recalc() {
  336. // Center title position
  337. if ch.title != nil {
  338. xpos := (ch.ContentWidth() - ch.title.width) / 2
  339. ch.title.SetPositionX(xpos)
  340. }
  341. // Recalc scale X and its labels
  342. if ch.scaleX != nil {
  343. ch.scaleX.recalc()
  344. ch.updateLabelsX()
  345. }
  346. // Recalc scale Y and its labels
  347. if ch.scaleY != nil {
  348. ch.scaleY.recalc()
  349. ch.updateLabelsY()
  350. }
  351. // Recalc graphs
  352. for i := 0; i < len(ch.graphs); i++ {
  353. g := ch.graphs[i]
  354. g.recalc()
  355. ch.SetTopChild(g)
  356. }
  357. }
  358. //
  359. // chartScaleX is a panel with GL_LINES geometry which draws the chart X horizontal scale axis,
  360. // vertical lines and line labels.
  361. //
  362. type chartScaleX struct {
  363. Panel // Embedded panel
  364. chart *Chart // Container chart
  365. lines int // Number of vertical lines
  366. bounds gls.Uniform4f // Bound uniform in OpenGL window coordinates
  367. mat chartMaterial // Chart material
  368. }
  369. // newChartScaleX creates and returns a pointer to a new chartScaleX for the specified
  370. // chart, number of lines and color
  371. func newChartScaleX(chart *Chart, lines int, color *math32.Color) *chartScaleX {
  372. sx := new(chartScaleX)
  373. sx.chart = chart
  374. sx.lines = lines
  375. sx.bounds.Init("Bounds")
  376. // Appends bottom horizontal line
  377. positions := math32.NewArrayF32(0, 0)
  378. positions.Append(0, -1+deltaLine, 0, 1, -1+deltaLine, 0)
  379. // Appends vertical lines
  380. step := 1 / float32(lines)
  381. for i := 0; i < lines; i++ {
  382. nx := float32(i) * step
  383. if i == 0 {
  384. nx += deltaLine
  385. }
  386. positions.Append(nx, 0, 0, nx, -1, 0)
  387. }
  388. // Creates geometry and adds VBO
  389. geom := geometry.NewGeometry()
  390. geom.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
  391. // Initializes the panel graphic
  392. gr := graphic.NewGraphic(geom, gls.LINES)
  393. sx.mat.Init(color)
  394. gr.AddMaterial(sx, &sx.mat, 0, 0)
  395. sx.Panel.InitializeGraphic(chart.ContentWidth(), chart.ContentHeight(), gr)
  396. sx.recalc()
  397. return sx
  398. }
  399. // recalc recalculates the position and size of this scale inside its parent
  400. func (sx *chartScaleX) recalc() {
  401. py := sx.chart.top
  402. if sx.chart.title != nil {
  403. py += sx.chart.title.Height()
  404. }
  405. sx.SetPosition(sx.chart.left, py)
  406. sx.SetSize(sx.chart.ContentWidth()-sx.chart.left, sx.chart.ContentHeight()-py-sx.chart.bottom)
  407. }
  408. // RenderSetup is called by the renderer before drawing this graphic
  409. // It overrides the original panel RenderSetup
  410. // Calculates the model matrix and transfer to OpenGL.
  411. func (sx *chartScaleX) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  412. // Sets model matrix and transfer to shader
  413. var mm math32.Matrix4
  414. sx.SetModelMatrix(gs, &mm)
  415. sx.modelMatrixUni.SetMatrix4(&mm)
  416. sx.modelMatrixUni.Transfer(gs)
  417. // Sets bounds in OpenGL window coordinates and transfer to shader
  418. _, _, _, height := gs.GetViewport()
  419. sx.bounds.Set(sx.pospix.X, float32(height)-sx.pospix.Y, sx.width, sx.height)
  420. sx.bounds.Transfer(gs)
  421. }
  422. //
  423. // ChartScaleY is a panel with LINE geometry which draws the chart Y vertical scale axis,
  424. // horizontal and labels.
  425. //
  426. type chartScaleY struct {
  427. Panel // Embedded panel
  428. chart *Chart // Container chart
  429. lines int // Number of horizontal lines
  430. bounds gls.Uniform4f // Bound uniform in OpenGL window coordinates
  431. mat chartMaterial // Chart material
  432. }
  433. // newChartScaleY creates and returns a pointer to a new chartScaleY for the specified
  434. // chart, number of lines and color
  435. func newChartScaleY(chart *Chart, lines int, color *math32.Color) *chartScaleY {
  436. if lines < 2 {
  437. lines = 2
  438. }
  439. sy := new(chartScaleY)
  440. sy.chart = chart
  441. sy.lines = lines
  442. sy.bounds.Init("Bounds")
  443. // Appends left vertical line
  444. positions := math32.NewArrayF32(0, 0)
  445. positions.Append(0+deltaLine, 0, 0, 0+deltaLine, -1, 0)
  446. // Appends horizontal lines
  447. step := 1 / float32(lines-1)
  448. for i := 0; i < lines; i++ {
  449. ny := -1 + float32(i)*step
  450. if i == 0 {
  451. ny += deltaLine
  452. }
  453. if i == lines-1 {
  454. ny -= deltaLine
  455. }
  456. positions.Append(0, ny, 0, 1, ny, 0)
  457. }
  458. // Creates geometry and adds VBO
  459. geom := geometry.NewGeometry()
  460. geom.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
  461. // Initializes the panel with this graphic
  462. gr := graphic.NewGraphic(geom, gls.LINES)
  463. sy.mat.Init(color)
  464. gr.AddMaterial(sy, &sy.mat, 0, 0)
  465. sy.Panel.InitializeGraphic(chart.ContentWidth(), chart.ContentHeight(), gr)
  466. sy.recalc()
  467. return sy
  468. }
  469. // recalc recalculates the position and size of this scale inside its parent
  470. func (sy *chartScaleY) recalc() {
  471. py := sy.chart.top
  472. if sy.chart.title != nil {
  473. py += sy.chart.title.Height()
  474. }
  475. sy.SetPosition(sy.chart.left, py)
  476. sy.SetSize(sy.chart.ContentWidth()-sy.chart.left, sy.chart.ContentHeight()-py-sy.chart.bottom)
  477. }
  478. // RenderSetup is called by the renderer before drawing this graphic
  479. // It overrides the original panel RenderSetup
  480. // Calculates the model matrix and transfer to OpenGL.
  481. func (sy *chartScaleY) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  482. // Sets model matrix and transfer to shader
  483. var mm math32.Matrix4
  484. sy.SetModelMatrix(gs, &mm)
  485. sy.modelMatrixUni.SetMatrix4(&mm)
  486. sy.modelMatrixUni.Transfer(gs)
  487. // Sets bounds in OpenGL window coordinates and transfer to shader
  488. _, _, _, height := gs.GetViewport()
  489. sy.bounds.Set(sy.pospix.X, float32(height)-sy.pospix.Y, sy.width, sy.height)
  490. sy.bounds.Transfer(gs)
  491. }
  492. //
  493. // Graph
  494. //
  495. type Graph struct {
  496. Panel // Embedded panel
  497. chart *Chart // Container chart
  498. color math32.Color // Line color
  499. data []float32 // Data y
  500. bounds gls.Uniform4f // Bound uniform in OpenGL window coordinates
  501. mat chartMaterial // Chart material
  502. vbo *gls.VBO
  503. positions math32.ArrayF32
  504. }
  505. // newGraph creates and returns a pointer to a new graph for the specified chart
  506. func newGraph(chart *Chart, color *math32.Color, data []float32) *Graph {
  507. lg := new(Graph)
  508. lg.bounds.Init("Bounds")
  509. lg.chart = chart
  510. lg.color = *color
  511. lg.data = data
  512. // Creates geometry and adds VBO with positions
  513. geom := geometry.NewGeometry()
  514. lg.vbo = gls.NewVBO().AddAttrib("VertexPosition", 3)
  515. lg.positions = math32.NewArrayF32(0, 0)
  516. lg.vbo.SetBuffer(lg.positions)
  517. geom.AddVBO(lg.vbo)
  518. // Initializes the panel with this graphic
  519. gr := graphic.NewGraphic(geom, gls.LINE_STRIP)
  520. lg.mat.Init(&lg.color)
  521. gr.AddMaterial(lg, &lg.mat, 0, 0)
  522. lg.Panel.InitializeGraphic(lg.chart.ContentWidth(), lg.chart.ContentHeight(), gr)
  523. lg.SetData(data)
  524. return lg
  525. }
  526. // SetColor sets the color of the graph
  527. func (lg *Graph) SetColor(color *math32.Color) {
  528. lg.mat.color.SetColor(color)
  529. }
  530. // SetData sets the graph data
  531. func (lg *Graph) SetData(data []float32) {
  532. lg.data = data
  533. lg.updateData()
  534. }
  535. // SetLineWidth sets the graph line width
  536. func (lg *Graph) SetLineWidth(width float32) {
  537. lg.mat.SetLineWidth(width)
  538. }
  539. // updateData regenerates the lines for the current data
  540. func (lg *Graph) updateData() {
  541. lines := 1
  542. if lg.chart.scaleX != nil {
  543. lines = lg.chart.scaleX.lines
  544. }
  545. step := 1.0 / (float32(lines) * lg.chart.countStepX)
  546. positions := math32.NewArrayF32(0, 0)
  547. rangeY := lg.chart.maxY - lg.chart.minY
  548. for i := 0; i < len(lg.data); i++ {
  549. px := float32(i) * step
  550. vy := lg.data[i]
  551. py := -1 + ((vy - lg.chart.minY) / rangeY)
  552. positions.Append(px, py, 0)
  553. }
  554. lg.vbo.SetBuffer(positions)
  555. }
  556. // recalc recalculates the position and width of the this panel
  557. func (lg *Graph) recalc() {
  558. py := lg.chart.top
  559. if lg.chart.title != nil {
  560. py += lg.chart.title.Height()
  561. }
  562. px := lg.chart.left
  563. w := lg.chart.ContentWidth() - lg.chart.left
  564. h := lg.chart.ContentHeight() - py - lg.chart.bottom
  565. lg.SetPosition(px, py)
  566. lg.SetSize(w, h)
  567. }
  568. // RenderSetup is called by the renderer before drawing this graphic
  569. // It overrides the original panel RenderSetup
  570. // Calculates the model matrix and transfer to OpenGL.
  571. func (lg *Graph) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  572. // Sets model matrix and transfer to shader
  573. var mm math32.Matrix4
  574. lg.SetModelMatrix(gs, &mm)
  575. lg.modelMatrixUni.SetMatrix4(&mm)
  576. lg.modelMatrixUni.Transfer(gs)
  577. // Sets bounds in OpenGL window coordinates and transfer to shader
  578. _, _, _, height := gs.GetViewport()
  579. lg.bounds.Set(lg.pospix.X, float32(height)-lg.pospix.Y, lg.width, lg.height)
  580. lg.bounds.Transfer(gs)
  581. }
  582. //
  583. // Chart material
  584. //
  585. type chartMaterial struct {
  586. material.Material // Embedded material
  587. color *gls.Uniform3f // Emissive color uniform
  588. }
  589. func (cm *chartMaterial) Init(color *math32.Color) {
  590. cm.Material.Init()
  591. cm.SetShader("shaderChart")
  592. // Creates uniforms and adds to material
  593. cm.color = gls.NewUniform3f("MatColor")
  594. // Set initial values
  595. cm.color.SetColor(color)
  596. }
  597. func (cm *chartMaterial) RenderSetup(gs *gls.GLS) {
  598. cm.Material.RenderSetup(gs)
  599. cm.color.Transfer(gs)
  600. }
  601. //
  602. // Vertex Shader template
  603. //
  604. const shaderChartVertex = `
  605. #version {{.Version}}
  606. // Vertex attributes
  607. {{template "attributes" .}}
  608. // Input uniforms
  609. uniform mat4 ModelMatrix;
  610. uniform vec3 MatColor;
  611. // Outputs for fragment shader
  612. out vec3 Color;
  613. void main() {
  614. Color = MatColor;
  615. // Set position
  616. vec4 pos = vec4(VertexPosition.xyz, 1);
  617. vec4 posclip = ModelMatrix * pos;
  618. gl_Position = posclip;
  619. }
  620. `
  621. //
  622. // Fragment Shader template
  623. //
  624. const shaderChartFrag = `
  625. #version {{.Version}}
  626. // Input uniforms from vertex shader
  627. in vec3 Color;
  628. // Input uniforms
  629. uniform vec4 Bounds;
  630. // Output
  631. out vec4 FragColor;
  632. void main() {
  633. // Discard fragment outside of the received bounds in OpenGL window pixel coordinates
  634. // Bounds[0] - x
  635. // Bounds[1] - y
  636. // Bounds[2] - width
  637. // Bounds[3] - height
  638. if (gl_FragCoord.x < Bounds[0] || gl_FragCoord.x > Bounds[0] + Bounds[2]) {
  639. discard;
  640. }
  641. if (gl_FragCoord.y > Bounds[1] || gl_FragCoord.y < Bounds[1] - Bounds[3]) {
  642. discard;
  643. }
  644. FragColor = vec4(Color, 1.0);
  645. }
  646. `