99 "math"
1010 "reflect"
1111 "syscall/js"
12+
13+ "github.com/gaarutyunov/guix/pkg/runtime/chart/shaders"
1214)
1315
1416//go:embed chart/shaders/candlestick.wgsl
@@ -17,16 +19,6 @@ var candlestickShader string
1719//go:embed chart/shaders/line.wgsl
1820var lineShader string
1921
20- // ohlcvData represents extracted OHLCV data
21- type ohlcvData struct {
22- Timestamp int64
23- Open float64
24- High float64
25- Low float64
26- Close float64
27- Volume float64
28- }
29-
3022// ChartRenderer manages rendering of charts
3123type ChartRenderer struct {
3224 Canvas * GPUCanvas
@@ -341,7 +333,7 @@ func (cr *ChartRenderer) renderCandlestickSeries(pass js.Value, series *GPUNode)
341333 // Log first candle for debugging
342334 if len (candles ) > 0 {
343335 c := candles [0 ]
344- log (fmt .Sprintf ("[ChartRenderer] First candle - Timestamp: %d , O: %.2f, H: %.2f, L: %.2f, C: %.2f, V: %.2f" ,
336+ log (fmt .Sprintf ("[ChartRenderer] First candle - Timestamp: %.0f , O: %.2f, H: %.2f, L: %.2f, C: %.2f, V: %.2f" ,
345337 c .Timestamp , c .Open , c .High , c .Low , c .Close , c .Volume ))
346338 }
347339
@@ -514,7 +506,7 @@ func (cr *ChartRenderer) renderLineSeries(pass js.Value, series *GPUNode) {
514506// Helper functions
515507
516508// extractOHLCVData uses reflection to extract OHLCV data from any slice type
517- func (cr * ChartRenderer ) extractOHLCVData (data interface {}) []ohlcvData {
509+ func (cr * ChartRenderer ) extractOHLCVData (data interface {}) []shaders. Candle {
518510 log (fmt .Sprintf ("[ChartRenderer] extractOHLCVData called with type: %T" , data ))
519511
520512 v := reflect .ValueOf (data )
@@ -534,7 +526,7 @@ func (cr *ChartRenderer) extractOHLCVData(data interface{}) []ohlcvData {
534526 firstItem := v .Index (0 )
535527 log (fmt .Sprintf ("[ChartRenderer] First item type: %v, kind: %v" , firstItem .Type (), firstItem .Kind ()))
536528
537- result := make ([]ohlcvData , v .Len ())
529+ result := make ([]shaders. Candle , v .Len ())
538530 successCount := 0
539531
540532 for i := 0 ; i < v .Len (); i ++ {
@@ -548,13 +540,13 @@ func (cr *ChartRenderer) extractOHLCVData(data interface{}) []ohlcvData {
548540 close := getFloat64Field (item , "Close" )
549541 volume := getFloat64Field (item , "Volume" )
550542
551- result [i ] = ohlcvData {
552- Timestamp : timestamp ,
553- Open : open ,
554- High : high ,
555- Low : low ,
556- Close : close ,
557- Volume : volume ,
543+ result [i ] = shaders. Candle {
544+ Timestamp : float32 ( timestamp ) ,
545+ Open : float32 ( open ) ,
546+ High : float32 ( high ) ,
547+ Low : float32 ( low ) ,
548+ Close : float32 ( close ) ,
549+ Volume : float32 ( volume ) ,
558550 }
559551
560552 if i == 0 {
@@ -620,7 +612,7 @@ func (cr *ChartRenderer) getPadding() map[string]float32 {
620612 return padding
621613}
622614
623- func (cr * ChartRenderer ) calculateDataRanges (candles []ohlcvData ) {
615+ func (cr * ChartRenderer ) calculateDataRanges (candles []shaders. Candle ) {
624616 if len (candles ) == 0 {
625617 return
626618 }
@@ -630,8 +622,8 @@ func (cr *ChartRenderer) calculateDataRanges(candles []ohlcvData) {
630622
631623 for _ , c := range candles {
632624 timestamp := float64 (c .Timestamp )
633- high := c .High
634- low := c .Low
625+ high := float64 ( c .High )
626+ low := float64 ( c .Low )
635627
636628 if timestamp < minX {
637629 minX = timestamp
@@ -688,26 +680,15 @@ func (cr *ChartRenderer) calculateLineDataRanges(points []interface{}) {
688680 cr .DataYRange = [2 ]float64 {minY , maxY }
689681}
690682
691- func (cr * ChartRenderer ) createCandleDataBuffer (candles []ohlcvData ) * GPUBuffer {
683+ func (cr * ChartRenderer ) createCandleDataBuffer (candles []shaders. Candle ) * GPUBuffer {
692684 // Each candle: timestamp(f32), open(f32), high(f32), low(f32), close(f32), volume(f32) = 24 bytes
693685 bufferSize := len (candles ) * 24
694686 data := make ([]byte , bufferSize )
695687
688+ // Use generated ToBytes() method for type-safe zero-copy serialization
696689 for i , c := range candles {
697- offset := i * 24
698- timestamp := float64 (c .Timestamp )
699- open := c .Open
700- high := c .High
701- low := c .Low
702- close := c .Close
703- volume := c .Volume
704-
705- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (float32 (timestamp )))
706- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (float32 (open )))
707- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (float32 (high )))
708- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (float32 (low )))
709- binary .LittleEndian .PutUint32 (data [offset + 16 :], math .Float32bits (float32 (close )))
710- binary .LittleEndian .PutUint32 (data [offset + 20 :], math .Float32bits (float32 (volume )))
690+ candleBytes := c .ToBytes ()
691+ copy (data [i * 24 :(i + 1 )* 24 ], candleBytes )
711692 }
712693
713694 // Create buffer and write data
@@ -762,108 +743,40 @@ func (cr *ChartRenderer) createLineDataBuffer(points []interface{}) *GPUBuffer {
762743func (cr * ChartRenderer ) createCandleUniforms (upColor , downColor , wickColor Vec4 , candleWidth float32 ) []byte {
763744 padding := cr .getPadding ()
764745
765- // Uniform layout matches WGSL struct
766- data := make ([]byte , 256 )
767- offset := 0
768-
769- // viewportSize: vec2<f32>
770- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (float32 (cr .Canvas .Width )))
771- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (float32 (cr .Canvas .Height )))
772- offset += 16 // vec2 aligned to 16 bytes
773-
774- // dataRange: vec4<f32>
775- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (float32 (cr .DataXRange [0 ])))
776- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (float32 (cr .DataXRange [1 ])))
777- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (float32 (cr .DataYRange [0 ])))
778- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (float32 (cr .DataYRange [1 ])))
779- offset += 16
780-
781- // padding: vec4<f32>
782- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (padding ["top" ]))
783- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (padding ["right" ]))
784- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (padding ["bottom" ]))
785- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (padding ["left" ]))
786- offset += 16
787-
788- // candleWidth: f32
789- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (candleWidth ))
790- offset += 16 // aligned to 16 bytes
791-
792- // upColor: vec4<f32>
793- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (upColor .X ))
794- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (upColor .Y ))
795- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (upColor .Z ))
796- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (upColor .W ))
797- offset += 16
798-
799- // downColor: vec4<f32>
800- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (downColor .X ))
801- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (downColor .Y ))
802- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (downColor .Z ))
803- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (downColor .W ))
804- offset += 16
805-
806- // wickColor: vec4<f32>
807- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (wickColor .X ))
808- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (wickColor .Y ))
809- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (wickColor .Z ))
810- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (wickColor .W ))
811-
812- return data
746+ // Use generated ChartUniforms struct with automatic ToBytes() serialization
747+ uniforms := shaders.ChartUniforms {
748+ ViewportSize : [2 ]float32 {float32 (cr .Canvas .Width ), float32 (cr .Canvas .Height )},
749+ DataRange : [4 ]float32 {float32 (cr .DataXRange [0 ]), float32 (cr .DataXRange [1 ]), float32 (cr .DataYRange [0 ]), float32 (cr .DataYRange [1 ])},
750+ Padding : [4 ]float32 {padding ["top" ], padding ["right" ], padding ["bottom" ], padding ["left" ]},
751+ CandleWidth : candleWidth ,
752+ UpColor : [4 ]float32 {upColor .X , upColor .Y , upColor .Z , upColor .W },
753+ DownColor : [4 ]float32 {downColor .X , downColor .Y , downColor .Z , downColor .W },
754+ WickColor : [4 ]float32 {wickColor .X , wickColor .Y , wickColor .Z , wickColor .W },
755+ }
756+
757+ return uniforms .ToBytes ()
813758}
814759
815760func (cr * ChartRenderer ) createLineUniforms (strokeColor Vec4 , strokeWidth float32 , fill bool , fillColor Vec4 ) []byte {
816761 padding := cr .getPadding ()
817762
818- // Uniform layout matches WGSL struct
819- data := make ([]byte , 256 )
820- offset := 0
821-
822- // viewportSize: vec2<f32>
823- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (float32 (cr .Canvas .Width )))
824- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (float32 (cr .Canvas .Height )))
825- offset += 16
826-
827- // dataRange: vec4<f32>
828- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (float32 (cr .DataXRange [0 ])))
829- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (float32 (cr .DataXRange [1 ])))
830- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (float32 (cr .DataYRange [0 ])))
831- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (float32 (cr .DataYRange [1 ])))
832- offset += 16
833-
834- // padding: vec4<f32>
835- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (padding ["top" ]))
836- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (padding ["right" ]))
837- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (padding ["bottom" ]))
838- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (padding ["left" ]))
839- offset += 16
840-
841- // strokeWidth: f32
842- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (strokeWidth ))
843- offset += 16
844-
845- // strokeColor: vec4<f32>
846- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (strokeColor .X ))
847- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (strokeColor .Y ))
848- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (strokeColor .Z ))
849- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (strokeColor .W ))
850- offset += 16
851-
852- // fillEnabled: u32
853763 fillValue := uint32 (0 )
854764 if fill {
855765 fillValue = 1
856766 }
857- binary .LittleEndian .PutUint32 (data [offset :], fillValue )
858- offset += 16
859767
860- // fillColor: vec4<f32>
861- binary .LittleEndian .PutUint32 (data [offset :], math .Float32bits (fillColor .X ))
862- binary .LittleEndian .PutUint32 (data [offset + 4 :], math .Float32bits (fillColor .Y ))
863- binary .LittleEndian .PutUint32 (data [offset + 8 :], math .Float32bits (fillColor .Z ))
864- binary .LittleEndian .PutUint32 (data [offset + 12 :], math .Float32bits (fillColor .W ))
768+ // Use generated LineUniforms struct with automatic ToBytes() serialization
769+ uniforms := shaders.LineUniforms {
770+ ViewportSize : [2 ]float32 {float32 (cr .Canvas .Width ), float32 (cr .Canvas .Height )},
771+ DataRange : [4 ]float32 {float32 (cr .DataXRange [0 ]), float32 (cr .DataXRange [1 ]), float32 (cr .DataYRange [0 ]), float32 (cr .DataYRange [1 ])},
772+ Padding : [4 ]float32 {padding ["top" ], padding ["right" ], padding ["bottom" ], padding ["left" ]},
773+ StrokeWidth : strokeWidth ,
774+ StrokeColor : [4 ]float32 {strokeColor .X , strokeColor .Y , strokeColor .Z , strokeColor .W },
775+ FillEnabled : fillValue ,
776+ FillColor : [4 ]float32 {fillColor .X , fillColor .Y , fillColor .Z , fillColor .W },
777+ }
865778
866- return data
779+ return uniforms . ToBytes ()
867780}
868781
869782func (cr * ChartRenderer ) createCandleBindGroup (dataBuffer * GPUBuffer ) js.Value {
0 commit comments