chart.go 18 KB

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