chart.go 18 KB

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