@@ -20,7 +20,6 @@ const SerialPlotter = () => {
2020 const [ isConnected , setIsConnected ] = useState ( false ) ;
2121 const [ rawData , setRawData ] = useState < string > ( "" ) ;
2222 const [ selectedChannels , setSelectedChannels ] = useState < number [ ] > ( Array . from ( { length : maxChannels } , ( _ , i ) => i ) ) ;
23- const [ showCombined , setShowCombined ] = useState ( true ) ;
2423 const selectedChannelsRef = useRef < number [ ] > ( [ ] ) ;
2524 const rawDataRef = useRef < HTMLDivElement | null > ( null ) ;
2625 const maxPoints = 1000 ;
@@ -36,6 +35,8 @@ const SerialPlotter = () => {
3635 const channelsref = useRef < number > ( 1 ) ;
3736 const sweepPositions = useRef < number [ ] > ( new Array ( channelsref . current ) . fill ( 0 ) ) ; // Array for sweep positions
3837
38+ const dataRef = useRef < DataPoint [ ] > ( [ ] ) ;
39+
3940 useEffect ( ( ) => {
4041 if ( rawDataRef . current ) {
4142 rawDataRef . current . scrollTop = rawDataRef . current . scrollHeight ;
@@ -70,32 +71,49 @@ const SerialPlotter = () => {
7071
7172 useEffect ( ( ) => {
7273 if ( ! canvasRef . current ) return ;
73-
7474 const canvas = canvasRef . current ;
7575 canvas . width = canvas . clientWidth ;
7676 canvas . height = canvas . clientHeight ;
7777
78- if ( viewMode === "both" || viewMode === "plotter" ) {
79- const wglp = new WebglPlot ( canvas ) ;
80- wglpRef . current = wglp ;
78+ // Only (re)initialize if viewMode or selectedChannels change
79+ const wglp = new WebglPlot ( canvas ) ;
80+ wglpRef . current = wglp ;
81+ linesRef . current = [ ] ;
8182
82- // Clear and re-add lines
83- linesRef . current = [ ] ;
83+ selectedChannels . forEach ( ( _ , i ) => {
84+ const line = new WebglLine ( getLineColor ( i ) , maxPoints ) ;
85+ line . lineSpaceX ( - 1 , 2 / maxPoints ) ;
86+ wglp . addLine ( line ) ;
87+ linesRef . current . push ( line ) ;
88+ } ) ;
89+ // You can call wglp.update() here once for the initial setup.
90+ wglp . update ( ) ;
91+ } , [ selectedChannels , viewMode , isConnected ] ) ; // Remove data from dependencies if you handle data updates elsewhere
8492
85- selectedChannels . forEach ( ( _ , i ) => {
86- const line = new WebglLine ( getLineColor ( i ) , maxPoints ) ;
87- line . lineSpaceX ( - 1 , 2 / maxPoints ) ;
88- wglp . addLine ( line ) ;
89- linesRef . current . push ( line ) ;
90- } ) ;
93+ useEffect ( ( ) => {
94+ if ( data . length === 0 ) return ;
9195
92- // Re-plot existing data
93- updateWebGLPlot ( data ) ; // Ensure existing data is plotted
94- wglp . update ( ) ;
96+ // Calculate autoscale on the entire data state
97+ const allValues = data . flatMap ( dp => dp . values ) ;
98+ const MIN_POINTS_FOR_SCALING = 10 ;
99+ let yMin , yMax ;
100+
101+ if ( allValues . length < MIN_POINTS_FOR_SCALING ) {
102+ yMin = - 1 ;
103+ yMax = 1 ;
95104 } else {
96- wglpRef . current = null ; // Reset the WebGL plot reference when hiding
105+ yMin = Math . min ( ...allValues ) ;
106+ yMax = Math . max ( ...allValues ) ;
97107 }
98- } , [ selectedChannels , showCombined , data , viewMode ] ) ; // Include viewMode in dependencies
108+ const yRange = yMax - yMin || 1 ;
109+
110+ // Optionally, update all current lines with the new scale
111+ linesRef . current . forEach ( ( line , i ) => {
112+
113+ } ) ;
114+ } , [ data ] ) ;
115+
116+
99117
100118 const getLineColor = ( index : number ) : ColorRGBA => {
101119 const hex = channelColors [ index % channelColors . length ] ;
@@ -193,9 +211,12 @@ const SerialPlotter = () => {
193211 } ) ;
194212
195213 if ( newData . length > 0 ) {
196- updateWebGLPlot ( newData ) ;
197- setData ( ( prev ) => [ ...prev , ...newData ] . slice ( - maxPoints ) ) ;
214+ // Update the ref synchronously
215+ dataRef . current = [ ...dataRef . current , ...newData ] . slice ( - maxPoints ) ;
216+ updateWebGLPlot ( newData , dataRef . current ) ;
217+ setData ( dataRef . current ) ;
198218 }
219+
199220 }
200221 }
201222 serialReader . releaseLock ( ) ;
@@ -241,61 +262,49 @@ const SerialPlotter = () => {
241262 return ( ) => clearInterval ( interval ) ;
242263 } , [ port ] ) ;
243264
244- const updateWebGLPlot = ( newData : DataPoint [ ] ) => {
265+ const updateWebGLPlot = ( newData : DataPoint [ ] , windowData : DataPoint [ ] ) => {
245266 if ( ! wglpRef . current || linesRef . current . length === 0 || newData . length === 0 ) return ;
246- // Calculate Y-axis min and max values
247- const yMin = Math . min ( ...newData . flatMap ( dp => dp . values ) ) ;
248- const yMax = Math . max ( ...newData . flatMap ( dp => dp . values ) ) ;
249- const yRange = yMax - yMin || 1 ; // Avoid division by zero
250-
251- // Iterate over new data points and update plots
252- newData . forEach ( ( dataPoint ) => {
253- linesRef . current . forEach ( ( line , i ) => {
254267
255- if ( i >= dataPoint . values . length ) return ; // Prevent out-of-bounds errors
268+ // Use the entire window for autoscaling
269+ const allValues = windowData . flatMap ( dp => dp . values ) ;
270+ const MIN_POINTS_FOR_SCALING = 10 ;
271+ let yMin , yMax ;
256272
257- // Clamp Y-value to be within -1 and 1
258- const yValue = Math . max ( - 1 , Math . min ( 1 , ( ( dataPoint . values [ i ] - yMin ) / yRange ) * 2 - 1 ) ) ;
259-
260- // Update combined plot
261- // Ensure sweepPositions.current[i] is initialized
262- if ( sweepPositions . current [ i ] === undefined ) {
263- sweepPositions . current [ i ] = 0 ;
264- }
273+ if ( allValues . length < MIN_POINTS_FOR_SCALING ) {
274+ yMin = - 1 ;
275+ yMax = 1 ;
276+ } else {
277+ yMin = Math . min ( ... allValues ) ;
278+ yMax = Math . max ( ... allValues ) ;
279+ }
280+ const yRange = yMax - yMin || 1 ;
265281
282+ newData . forEach ( ( dataPoint ) => {
283+ linesRef . current . forEach ( ( line , i ) => {
284+ if ( i >= dataPoint . values . length ) return ;
285+ const yValue = ( ( dataPoint . values [ i ] - yMin ) / yRange ) * 2 - 1 ;
266286 const currentPos = sweepPositions . current [ i ] % line . numPoints ;
267- if ( Number . isNaN ( currentPos ) ) {
268- console . error ( `Invalid currentPos at i ${ i } . sweepPositions.current[i]:` , sweepPositions . current [ i ] ) ;
269- return ;
270- }
271-
272- if ( line ) {
273- try {
274- line . setY ( currentPos , yValue ) ;
275- } catch ( error ) {
276- console . error ( `Error plotting data for line ${ i } at position ${ currentPos } :` , error ) ;
277- }
278-
287+ try {
288+ line . setY ( currentPos , yValue ) ;
289+ } catch ( error ) {
290+ console . error ( `Error plotting data for line ${ i } at position ${ currentPos } :` , error ) ;
279291 }
280- // Clear the next point for visual effect
281292 const clearPosition = Math . ceil ( ( currentPos + maxPoints / 100 ) % line . numPoints ) ;
282293 try {
283294 line . setY ( clearPosition , NaN ) ;
284295 } catch ( error ) {
285296 console . error ( `Error clearing data at position ${ clearPosition } for line ${ i } :` , error ) ;
286297 }
287-
288- // Increment the sweep position
289298 sweepPositions . current [ i ] = ( currentPos + 1 ) % line . numPoints ;
290299 } ) ;
291300 } ) ;
292301
293- // Efficiently trigger a render update
294302 requestAnimationFrame ( ( ) => {
295303 if ( wglpRef . current ) wglpRef . current . update ( ) ;
296304 } ) ;
297305 } ;
298306
307+
299308 const disconnectSerial = async ( ) => {
300309 if ( reader ) {
301310 await reader . cancel ( ) ;
0 commit comments