@@ -34,7 +34,7 @@ const SerialPlotter = () => {
3434 const bitsref = useRef < number > ( 10 ) ;
3535 const channelsref = useRef < number > ( 1 ) ;
3636 const sweepPositions = useRef < number [ ] > ( new Array ( channelsref . current ) . fill ( 0 ) ) ; // Array for sweep positions
37-
37+ const currentScaleRef = useRef ( { yMin : - 1 , yMax : 1 } ) ;
3838 const dataRef = useRef < DataPoint [ ] > ( [ ] ) ;
3939
4040 useEffect ( ( ) => {
@@ -67,7 +67,7 @@ const SerialPlotter = () => {
6767 } ) ;
6868
6969 wglp . update ( ) ;
70- } , [ selectedChannels ] ) ; // ✅ Runs when channels are detected
70+ } , [ selectedChannels ] ) ;
7171
7272 useEffect ( ( ) => {
7373 if ( ! canvasRef . current ) return ;
@@ -88,31 +88,53 @@ const SerialPlotter = () => {
8888 } ) ;
8989 // You can call wglp.update() here once for the initial setup.
9090 wglp . update ( ) ;
91- } , [ selectedChannels , viewMode , isConnected ] ) ; // Remove data from dependencies if you handle data updates elsewhere
91+ } , [ selectedChannels , viewMode , isConnected ] ) ;
9292
9393 useEffect ( ( ) => {
94- if ( data . length === 0 ) return ;
95-
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 ;
104- } else {
105- yMin = Math . min ( ...allValues ) ;
106- yMax = Math . max ( ...allValues ) ;
107- }
108- const yRange = yMax - yMin || 1 ;
94+ const smoothingFactor = 0.5 ; // Adjust this between 0 (no change) and 1 (immediate change)
95+ // Update frequently for a continuous effect (e.g., every 50ms)
96+ const interval = setInterval ( ( ) => {
97+ if ( ! wglpRef . current || linesRef . current . length === 0 || dataRef . current . length === 0 ) return ;
98+
99+ // Compute the new autoscale parameters from the full data window
100+ const allValues = dataRef . current . flatMap ( dp => dp . values ) ;
101+ const MIN_POINTS_FOR_SCALING = 10 ;
102+ let newYMin = - 1 , newYMax = 1 ;
103+ if ( allValues . length >= MIN_POINTS_FOR_SCALING ) {
104+ newYMin = Math . min ( ...allValues ) ;
105+ newYMax = Math . max ( ...allValues ) ;
106+ }
109107
110- // Optionally, update all current lines with the new scale
111- linesRef . current . forEach ( ( line , i ) => {
108+ // Smoothly interpolate between the current scale and the new scale
109+ currentScaleRef . current . yMin += smoothingFactor * ( newYMin - currentScaleRef . current . yMin ) ;
110+ currentScaleRef . current . yMax += smoothingFactor * ( newYMax - currentScaleRef . current . yMax ) ;
111+
112+ const currentYMin = currentScaleRef . current . yMin ;
113+ const currentYMax = currentScaleRef . current . yMax ;
114+ const yRange = currentYMax - currentYMin || 1 ;
115+
116+ // Update every point in the buffer using the smoothed scale
117+ linesRef . current . forEach ( ( line , channelIndex ) => {
118+ const numPoints = dataRef . current . length ;
119+ for ( let j = 0 ; j < numPoints ; j ++ ) {
120+ const value = dataRef . current [ j ] . values [ channelIndex ] ;
121+ if ( value === undefined ) continue ;
122+ const scaledY = ( ( value - currentYMin ) / yRange ) * 2 - 1 ;
123+ try {
124+ line . setY ( j , scaledY ) ;
125+ } catch ( e ) {
126+ console . error ( `Error autoscaling channel ${ channelIndex } at point ${ j } :` , e ) ;
127+ }
128+ }
129+ } ) ;
112130
113- } ) ;
114- } , [ data ] ) ;
131+ if ( wglpRef . current ) {
132+ wglpRef . current . update ( ) ;
133+ }
134+ } , 10 ) ; // Update every 50ms for a continuous, smooth effect
115135
136+ return ( ) => clearInterval ( interval ) ;
137+ } , [ ] ) ;
116138
117139
118140 const getLineColor = ( index : number ) : ColorRGBA => {
@@ -141,18 +163,13 @@ const SerialPlotter = () => {
141163 selectedChannelsRef . current = [ ] ;
142164 readSerialData ( selectedPort ) ;
143165
144-
145166 setTimeout ( ( ) => {
146167 sweepPositions . current = new Array ( 6 ) . fill ( 0 ) ;
147-
148168 } , 6000 ) ;
149-
150-
151169 } catch ( err ) {
152170 console . error ( "Error connecting to serial:" , err ) ;
153171 }
154- } , [ baudRateref . current , setPort , setIsConnected , setRawData , wglpRef , linesRef ] ) ;
155-
172+ } , [ baudRateref . current , setPort , setIsConnected , setRawData ] ) ;
156173
157174 const readSerialData = async ( serialPort : SerialPort ) => {
158175 try {
@@ -204,7 +221,6 @@ const SerialPlotter = () => {
204221 if ( prevChannels . length !== values . length ) {
205222 return Array . from ( { length : values . length } , ( _ , i ) => i ) ;
206223 }
207-
208224 return prevChannels ;
209225 } ) ;
210226 }
@@ -216,7 +232,6 @@ const SerialPlotter = () => {
216232 updateWebGLPlot ( newData , dataRef . current ) ;
217233 setData ( dataRef . current ) ;
218234 }
219-
220235 }
221236 }
222237 serialReader . releaseLock ( ) ;
@@ -265,37 +280,22 @@ const SerialPlotter = () => {
265280 const updateWebGLPlot = ( newData : DataPoint [ ] , windowData : DataPoint [ ] ) => {
266281 if ( ! wglpRef . current || linesRef . current . length === 0 || newData . length === 0 ) return ;
267282
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 ;
272-
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- }
283+ // Use the smoothed scale from currentScaleRef for the new data
284+ const { yMin, yMax } = currentScaleRef . current ;
280285 const yRange = yMax - yMin || 1 ;
281286
282287 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 ;
286- const currentPos = sweepPositions . current [ i ] % line . numPoints ;
288+ linesRef . current . forEach ( ( line , channelIndex ) => {
289+ if ( channelIndex >= dataPoint . values . length ) return ;
290+ const yValue = ( ( dataPoint . values [ channelIndex ] - yMin ) / yRange ) * 2 - 1 ;
291+ const currentPos = sweepPositions . current [ channelIndex ] % line . numPoints ;
287292 try {
288293 line . setY ( currentPos , yValue ) ;
289294 } catch ( error ) {
290- console . error ( `Error plotting data for line ${ i } at position ${ currentPos } :` , error ) ;
291- }
292- const clearPosition = Math . ceil ( ( currentPos + maxPoints / 100 ) % line . numPoints ) ;
293- try {
294- line . setY ( clearPosition , NaN ) ;
295- } catch ( error ) {
296- console . error ( `Error clearing data at position ${ clearPosition } for line ${ i } :` , error ) ;
295+ console . error ( `Error plotting data for line ${ channelIndex } at pos ${ currentPos } :` , error ) ;
297296 }
298- sweepPositions . current [ i ] = ( currentPos + 1 ) % line . numPoints ;
297+ // Advance the circular buffer pointer
298+ sweepPositions . current [ channelIndex ] = ( currentPos + 1 ) % line . numPoints ;
299299 } ) ;
300300 } ) ;
301301
@@ -305,6 +305,7 @@ const SerialPlotter = () => {
305305 } ;
306306
307307
308+
308309 const disconnectSerial = async ( ) => {
309310 if ( reader ) {
310311 await reader . cancel ( ) ;
@@ -342,7 +343,6 @@ const SerialPlotter = () => {
342343 const writer = port . writable . getWriter ( ) ; // Get writer
343344 await writer . write ( new TextEncoder ( ) . encode ( command + "\n" ) ) ;
344345 writer . releaseLock ( ) ; // Release writer after writing
345-
346346 } catch ( err ) {
347347 console . error ( "Error sending command:" , err ) ;
348348 }
@@ -371,7 +371,7 @@ const SerialPlotter = () => {
371371 ref = { rawDataRef }
372372 className = "w-full border rounded-xl shadow-lg bg-[#1a1a2e] text-white overflow-auto flex flex-col"
373373 style = { {
374- height : viewMode === "monitor" ? "calc(100vh - 100px)" : "35vh" , // Adjust height when only monitor is shown
374+ height : viewMode === "monitor" ? "calc(100vh - 100px)" : "35vh" ,
375375 maxHeight : viewMode === "monitor" ? "calc(100vh - 100px)" : "35vh" ,
376376 minHeight : "35vh" ,
377377 } }
@@ -385,29 +385,28 @@ const SerialPlotter = () => {
385385 onChange = { ( e ) => setCommand ( e . target . value ) }
386386 placeholder = "Enter command"
387387 className = "w-full p-2 text-xs font-semibold rounded bg-gray-800 text-white border border-gray-600"
388- style = { { height : '36px' } } // Ensure the height is consistent with buttons
388+ style = { { height : '36px' } }
389389 />
390390
391391 { /* Buttons (Shifted Left) */ }
392392 < div className = "flex items-center space-x-2 mr-auto" >
393393 < Button
394394 onClick = { sendCommand }
395395 className = "px-4 py-2 text-xs font-semibold bg-gray-500 rounded shadow-md hover:bg-gray-500 transition ml-2"
396- style = { { height : '36px' } } // Set height equal to the input box
396+ style = { { height : '36px' } }
397397 >
398398 Send
399399 </ Button >
400400 < button
401401 onClick = { ( ) => setRawData ( "" ) }
402402 className = "px-4 py-2 text-xs bg-red-600 text-white rounded shadow-md hover:bg-red-700 transition"
403- style = { { height : '36px' } } // Set height equal to the input box
403+ style = { { height : '36px' } }
404404 >
405405 Clear
406406 </ button >
407407 </ div >
408408 </ div >
409409
410-
411410 { /* Data Display */ }
412411 < pre className = "text-xs whitespace-pre-wrap break-words px-4 pb-4 flex-grow overflow-auto rounded-xl" >
413412 { rawData }
@@ -418,7 +417,6 @@ const SerialPlotter = () => {
418417
419418 { /* Footer Section */ }
420419 < footer className = "flex flex-col gap-2 sm:flex-row py-2 m-2 w-full shrink-0 items-center justify-center px-2 md:px-4" >
421-
422420 { /* Connection Button */ }
423421 < div className = "flex justify-center" >
424422 < Button
@@ -437,8 +435,8 @@ const SerialPlotter = () => {
437435 onClick = { ( ) => setViewMode ( mode ) }
438436 className = { `px-4 py-2 text-sm transition font-semibold
439437 ${ viewMode === mode
440- ? "bg-primary text-white dark:text-gray-900 shadow-md" // Active state
441- : "bg-gray-500 text-gray-900 hover:bg-gray-300" } // Inactive state (lighter shade)
438+ ? "bg-primary text-white dark:text-gray-900 shadow-md"
439+ : "bg-gray-500 text-gray-900 hover:bg-gray-300" }
442440 ${ index === 0 ? "rounded-xl rounded-r-none" : "" }
443441 ${ index === arr . length - 1 ? "rounded-xl rounded-l-none" : "" }
444442 ${ index !== 0 && index !== arr . length - 1 ? "rounded-none" : "" } ` }
@@ -448,7 +446,6 @@ const SerialPlotter = () => {
448446 ) ) }
449447 </ div >
450448
451-
452449 { /* Baud Rate Selector */ }
453450 < div className = "flex items-center space-x-2" >
454451 < label className = "text-sm font-semibold" > Baud Rate:</ label >
@@ -463,9 +460,8 @@ const SerialPlotter = () => {
463460 </ select >
464461 </ div >
465462 </ footer >
466-
467463 </ div >
468464 ) ;
469465} ;
470466
471- export default SerialPlotter ;
467+ export default SerialPlotter ;
0 commit comments