chart.go 17 KB

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