chart.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781
  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. //
  354. // ChartScaleX is a panel with GL_LINES geometry which draws the chart X horizontal scale axis,
  355. // vertical lines and line labels.
  356. //
  357. //
  358. type ChartScaleX struct {
  359. Panel // Embedded panel
  360. chart *Chart // Container chart
  361. lines int // Number of vertical lines
  362. bounds gls.Uniform4f // Bound uniform in OpenGL window coordinates
  363. mat chartMaterial // Chart material
  364. }
  365. // newChartScaleX creates and returns a pointer to a new ChartScaleX for the specified
  366. // chart, number of lines and color
  367. func newChartScaleX(chart *Chart, lines int, color *math32.Color) *ChartScaleX {
  368. sx := new(ChartScaleX)
  369. sx.chart = chart
  370. sx.lines = lines
  371. sx.bounds.Init("Bounds")
  372. // Appends bottom horizontal line
  373. positions := math32.NewArrayF32(0, 0)
  374. positions.Append(0, -1+deltaLine, 0, 1, -1+deltaLine, 0)
  375. // Appends vertical lines
  376. step := 1 / float32(lines)
  377. for i := 0; i < lines; i++ {
  378. nx := float32(i) * step
  379. if i == 0 {
  380. nx += deltaLine
  381. }
  382. positions.Append(nx, 0, 0, nx, -1, 0)
  383. }
  384. // Creates geometry and adds VBO
  385. geom := geometry.NewGeometry()
  386. geom.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
  387. // Initializes the panel graphic
  388. gr := graphic.NewGraphic(geom, gls.LINES)
  389. sx.mat.Init(color)
  390. gr.AddMaterial(sx, &sx.mat, 0, 0)
  391. sx.Panel.InitializeGraphic(chart.ContentWidth(), chart.ContentHeight(), gr)
  392. sx.recalc()
  393. return sx
  394. }
  395. // recalc recalculates the position and size of this scale inside its parent
  396. func (sx *ChartScaleX) recalc() {
  397. py := sx.chart.top
  398. if sx.chart.title != nil {
  399. py += sx.chart.title.Height()
  400. }
  401. sx.SetPosition(sx.chart.left, py)
  402. sx.SetSize(sx.chart.ContentWidth()-sx.chart.left, sx.chart.ContentHeight()-py-sx.chart.bottom)
  403. }
  404. // RenderSetup is called by the renderer before drawing this graphic
  405. // It overrides the original panel RenderSetup
  406. // Calculates the model matrix and transfer to OpenGL.
  407. func (sx *ChartScaleX) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  408. // Sets model matrix and transfer to shader
  409. var mm math32.Matrix4
  410. sx.SetModelMatrix(gs, &mm)
  411. sx.modelMatrixUni.SetMatrix4(&mm)
  412. sx.modelMatrixUni.Transfer(gs)
  413. // Sets bounds in OpenGL window coordinates and transfer to shader
  414. _, _, _, height := gs.GetViewport()
  415. sx.bounds.Set(sx.pospix.X, float32(height)-sx.pospix.Y, sx.width, sx.height)
  416. sx.bounds.Transfer(gs)
  417. }
  418. //
  419. //
  420. // ChartScaleY is a panel with LINE geometry which draws the chart Y vertical scale axis,
  421. // horizontal and labels.
  422. //
  423. //
  424. type ChartScaleY struct {
  425. Panel // Embedded panel
  426. chart *Chart // Container chart
  427. lines int // Number of horizontal lines
  428. bounds gls.Uniform4f // Bound uniform in OpenGL window coordinates
  429. mat chartMaterial // Chart material
  430. }
  431. // newChartScaleY creates and returns a pointer to a new ChartScaleY for the specified
  432. // chart, number of lines and color
  433. func newChartScaleY(chart *Chart, lines int, color *math32.Color) *ChartScaleY {
  434. if lines < 2 {
  435. lines = 2
  436. }
  437. sy := new(ChartScaleY)
  438. sy.chart = chart
  439. sy.lines = lines
  440. sy.bounds.Init("Bounds")
  441. // Appends left vertical line
  442. positions := math32.NewArrayF32(0, 0)
  443. positions.Append(0+deltaLine, 0, 0, 0+deltaLine, -1, 0)
  444. // Appends horizontal lines
  445. step := 1 / float32(lines-1)
  446. for i := 0; i < lines; i++ {
  447. ny := -1 + float32(i)*step
  448. if i == 0 {
  449. ny += deltaLine
  450. }
  451. if i == lines-1 {
  452. ny -= deltaLine
  453. }
  454. positions.Append(0, ny, 0, 1, ny, 0)
  455. }
  456. // Creates geometry and adds VBO
  457. geom := geometry.NewGeometry()
  458. geom.AddVBO(gls.NewVBO().AddAttrib("VertexPosition", 3).SetBuffer(positions))
  459. // Initializes the panel with this graphic
  460. gr := graphic.NewGraphic(geom, gls.LINES)
  461. sy.mat.Init(color)
  462. gr.AddMaterial(sy, &sy.mat, 0, 0)
  463. sy.Panel.InitializeGraphic(chart.ContentWidth(), chart.ContentHeight(), gr)
  464. sy.recalc()
  465. return sy
  466. }
  467. // recalc recalculates the position and size of this scale inside its parent
  468. func (sy *ChartScaleY) recalc() {
  469. py := sy.chart.top
  470. if sy.chart.title != nil {
  471. py += sy.chart.title.Height()
  472. }
  473. sy.SetPosition(sy.chart.left, py)
  474. sy.SetSize(sy.chart.ContentWidth()-sy.chart.left, sy.chart.ContentHeight()-py-sy.chart.bottom)
  475. }
  476. // RenderSetup is called by the renderer before drawing this graphic
  477. // It overrides the original panel RenderSetup
  478. // Calculates the model matrix and transfer to OpenGL.
  479. func (sy *ChartScaleY) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  480. // Sets model matrix and transfer to shader
  481. var mm math32.Matrix4
  482. sy.SetModelMatrix(gs, &mm)
  483. sy.modelMatrixUni.SetMatrix4(&mm)
  484. sy.modelMatrixUni.Transfer(gs)
  485. // Sets bounds in OpenGL window coordinates and transfer to shader
  486. _, _, _, height := gs.GetViewport()
  487. sy.bounds.Set(sy.pospix.X, float32(height)-sy.pospix.Y, sy.width, sy.height)
  488. sy.bounds.Transfer(gs)
  489. }
  490. //
  491. //
  492. // Graph
  493. //
  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. func newGraph(chart *Chart, color *math32.Color, y []float32) *Graph {
  506. lg := new(Graph)
  507. lg.bounds.Init("Bounds")
  508. lg.chart = chart
  509. lg.color = *color
  510. lg.data = y
  511. // Creates geometry and adds VBO with positions
  512. geom := geometry.NewGeometry()
  513. lg.vbo = gls.NewVBO().AddAttrib("VertexPosition", 3)
  514. lg.positions = math32.NewArrayF32(0, 0)
  515. lg.vbo.SetBuffer(lg.positions)
  516. geom.AddVBO(lg.vbo)
  517. // Initializes the panel with this graphic
  518. gr := graphic.NewGraphic(geom, gls.LINE_STRIP)
  519. lg.mat.Init(&lg.color)
  520. gr.AddMaterial(lg, &lg.mat, 0, 0)
  521. lg.Panel.InitializeGraphic(lg.chart.ContentWidth(), lg.chart.ContentHeight(), gr)
  522. lg.SetData(y)
  523. return lg
  524. }
  525. // SetColor sets the color of the graph
  526. func (lg *Graph) SetColor(color *math32.Color) {
  527. lg.mat.color.SetColor(color)
  528. }
  529. // SetData sets the graph data
  530. func (lg *Graph) SetData(data []float32) {
  531. lg.data = data
  532. lg.updateData()
  533. }
  534. // SetLineWidth sets the graph line width
  535. func (lg *Graph) SetLineWidth(width float32) {
  536. lg.mat.SetLineWidth(width)
  537. }
  538. // updateData regenerates the lines for the current data
  539. func (lg *Graph) updateData() {
  540. lines := 1
  541. if lg.chart.scaleX != nil {
  542. lines = lg.chart.scaleX.lines
  543. }
  544. step := 1.0 / (float32(lines) * lg.chart.countStepX)
  545. positions := math32.NewArrayF32(0, 0)
  546. rangeY := lg.chart.maxY - lg.chart.minY
  547. for i := 0; i < len(lg.data); i++ {
  548. px := float32(i) * step
  549. vy := lg.data[i]
  550. py := -1 + ((vy - lg.chart.minY) / rangeY)
  551. positions.Append(px, py, 0)
  552. }
  553. lg.vbo.SetBuffer(positions)
  554. }
  555. // recalc recalculates the position and width of the this panel
  556. func (lg *Graph) recalc() {
  557. py := lg.chart.top
  558. if lg.chart.title != nil {
  559. py += lg.chart.title.Height()
  560. }
  561. px := lg.chart.left
  562. w := lg.chart.ContentWidth() - lg.chart.left
  563. h := lg.chart.ContentHeight() - py - lg.chart.bottom
  564. lg.SetPosition(px, py)
  565. lg.SetSize(w, h)
  566. }
  567. // RenderSetup is called by the renderer before drawing this graphic
  568. // It overrides the original panel RenderSetup
  569. // Calculates the model matrix and transfer to OpenGL.
  570. func (lg *Graph) RenderSetup(gs *gls.GLS, rinfo *core.RenderInfo) {
  571. // Sets model matrix and transfer to shader
  572. var mm math32.Matrix4
  573. lg.SetModelMatrix(gs, &mm)
  574. lg.modelMatrixUni.SetMatrix4(&mm)
  575. lg.modelMatrixUni.Transfer(gs)
  576. // Sets bounds in OpenGL window coordinates and transfer to shader
  577. _, _, _, height := gs.GetViewport()
  578. lg.bounds.Set(lg.pospix.X, float32(height)-lg.pospix.Y, lg.width, lg.height)
  579. lg.bounds.Transfer(gs)
  580. }
  581. //
  582. //
  583. // Chart material (for lines)
  584. //
  585. //
  586. type chartMaterial struct {
  587. material.Material // Embedded material
  588. color *gls.Uniform3f // Emissive color uniform
  589. }
  590. func (cm *chartMaterial) Init(color *math32.Color) {
  591. cm.Material.Init()
  592. cm.SetShader("shaderChart")
  593. // Creates uniforms and adds to material
  594. cm.color = gls.NewUniform3f("MatColor")
  595. // Set initial values
  596. cm.color.SetColor(color)
  597. }
  598. func (cm *chartMaterial) RenderSetup(gs *gls.GLS) {
  599. cm.Material.RenderSetup(gs)
  600. cm.color.Transfer(gs)
  601. }
  602. //
  603. // Vertex Shader template
  604. //
  605. const shaderChartVertex = `
  606. #version {{.Version}}
  607. // Vertex attributes
  608. {{template "attributes" .}}
  609. // Input uniforms
  610. uniform mat4 ModelMatrix;
  611. uniform vec3 MatColor;
  612. // Outputs for fragment shader
  613. out vec3 Color;
  614. void main() {
  615. Color = MatColor;
  616. // Set position
  617. vec4 pos = vec4(VertexPosition.xyz, 1);
  618. vec4 posclip = ModelMatrix * pos;
  619. gl_Position = posclip;
  620. }
  621. `
  622. //
  623. // Fragment Shader template
  624. //
  625. const shaderChartFrag = `
  626. #version {{.Version}}
  627. // Input uniforms from vertex shader
  628. in vec3 Color;
  629. // Input uniforms
  630. uniform vec4 Bounds;
  631. // Output
  632. out vec4 FragColor;
  633. void main() {
  634. // Discard fragment outside of the received bounds in OpenGL window pixel coordinates
  635. // Bounds[0] - x
  636. // Bounds[1] - y
  637. // Bounds[2] - width
  638. // Bounds[3] - height
  639. if (gl_FragCoord.x < Bounds[0] || gl_FragCoord.x > Bounds[0] + Bounds[2]) {
  640. discard;
  641. }
  642. if (gl_FragCoord.y > Bounds[1] || gl_FragCoord.y < Bounds[1] - Bounds[3]) {
  643. discard;
  644. }
  645. FragColor = vec4(Color, 1.0);
  646. }
  647. `