@@ -1872,24 +1872,18 @@ export const ProteinViewer = forwardRef<ProteinViewerRef, ProteinViewerProps>(({
18721872
18731873
18741874 // --- GLOBAL CHAIN SMOOTHING ALGORITHM ---
1875- // 1. Linearize chain residues to array
1876- // 2. Assign base vs custom colors
1877- // 3. Apply floating-point moving average smoothing
1878- // 4. Map back to atoms
1875+ // Fixed: Uses 2-pass iteration to avoid ResidueProxy reference issues
18791876
18801877 try {
18811878 component . structure . eachChain ( ( chain : any ) => {
1882- // Collection phase
1883- const residues : any [ ] = [ ] ;
1884- const bgColors : number [ ] = [ ] ; // Base colors (element/custom)
1879+ const bgColors : number [ ] = [ ] ;
18851880
1881+ // PASS 1: Collection
18861882 chain . eachResidue ( ( res : any ) => {
1887- residues . push ( res ) ;
18881883 const key = `${ res . chainname } :${ res . resno } ` ;
18891884 let col = residueColorMap . get ( key ) ;
18901885
18911886 if ( col === undefined ) {
1892- // Default to element color
18931887 col = 0xCCCCCC ;
18941888 try {
18951889 const firstAtom = Array . from ( res . iterateAtom ( ) ) [ 0 ] ;
@@ -1900,25 +1894,23 @@ export const ProteinViewer = forwardRef<ProteinViewerRef, ProteinViewerProps>(({
19001894 }
19011895 } catch ( e ) { /* ignore */ }
19021896 }
1897+
19031898 bgColors . push ( col || 0xCCCCCC ) ;
19041899 } ) ;
19051900
1906- // Smoothing Phase (RGB separation)
1907- // We use a floating point buffer for precision accumulation
1901+ // Smoothing Phase
19081902 const len = bgColors . length ;
19091903 let rBuffer = new Float32Array ( len ) ;
19101904 let gBuffer = new Float32Array ( len ) ;
19111905 let bBuffer = new Float32Array ( len ) ;
19121906
1913- // Initialize
19141907 for ( let i = 0 ; i < len ; i ++ ) {
19151908 const c = bgColors [ i ] || 0xCCCCCC ;
19161909 rBuffer [ i ] = ( c >> 16 ) & 0xFF ;
19171910 gBuffer [ i ] = ( c >> 8 ) & 0xFF ;
19181911 bBuffer [ i ] = c & 0xFF ;
19191912 }
19201913
1921- // Multi-pass smoothing (3 passes of [0.25, 0.5, 0.25] kernel approx)
19221914 const passes = 10 ;
19231915 for ( let p = 0 ; p < passes ; p ++ ) {
19241916 const newR = new Float32Array ( len ) ;
@@ -1929,37 +1921,29 @@ export const ProteinViewer = forwardRef<ProteinViewerRef, ProteinViewerProps>(({
19291921 let sumR = 0 , sumG = 0 , sumB = 0 ;
19301922 let count = 0 ;
19311923
1932- // Previous
1933- if ( i > 0 ) {
1934- sumR += rBuffer [ i - 1 ] ; sumG += gBuffer [ i - 1 ] ; sumB += bBuffer [ i - 1 ] ;
1935- count ++ ;
1936- }
1937- // Current (weight x2 for stability)
1938- sumR += rBuffer [ i ] * 2 ; sumG += gBuffer [ i ] * 2 ; sumB += bBuffer [ i ] * 2 ;
1939- count += 2 ;
1940- // Next
1941- if ( i < len - 1 ) {
1942- sumR += rBuffer [ i + 1 ] ; sumG += gBuffer [ i + 1 ] ; sumB += bBuffer [ i + 1 ] ;
1943- count ++ ;
1944- }
1924+ if ( i > 0 ) { sumR += rBuffer [ i - 1 ] ; sumG += gBuffer [ i - 1 ] ; sumB += bBuffer [ i - 1 ] ; count ++ ; }
1925+ sumR += rBuffer [ i ] * 2 ; sumG += gBuffer [ i ] * 2 ; sumB += bBuffer [ i ] * 2 ; count += 2 ;
1926+ if ( i < len - 1 ) { sumR += rBuffer [ i + 1 ] ; sumG += gBuffer [ i + 1 ] ; sumB += bBuffer [ i + 1 ] ; count ++ ; }
19451927
1946- newR [ i ] = sumR / count ;
1947- newG [ i ] = sumG / count ;
1948- newB [ i ] = sumB / count ;
1928+ newR [ i ] = sumR / count ; newG [ i ] = sumG / count ; newB [ i ] = sumB / count ;
19491929 }
19501930 rBuffer = newR ; gBuffer = newG ; bBuffer = newB ;
19511931 }
19521932
1953- // Assignment Phase
1954- residues . forEach ( ( res , idx ) => {
1955- const r = Math . round ( rBuffer [ idx ] ) ;
1956- const g = Math . round ( gBuffer [ idx ] ) ;
1957- const b = Math . round ( bBuffer [ idx ] ) ;
1958- const finalColor = ( r << 16 ) | ( g << 8 ) | b ;
1959-
1960- res . eachAtom ( ( atom : any ) => {
1961- atomColorMap . set ( atom . index , finalColor ) ;
1962- } ) ;
1933+ // PASS 2: Assignment (Re-iterate to get valid proxies)
1934+ let resIdx = 0 ;
1935+ chain . eachResidue ( ( res : any ) => {
1936+ if ( resIdx < len ) {
1937+ const r = Math . round ( rBuffer [ resIdx ] ) ;
1938+ const g = Math . round ( gBuffer [ resIdx ] ) ;
1939+ const b = Math . round ( bBuffer [ resIdx ] ) ;
1940+ const finalColor = ( r << 16 ) | ( g << 8 ) | b ;
1941+
1942+ res . eachAtom ( ( atom : any ) => {
1943+ atomColorMap . set ( atom . index , finalColor ) ;
1944+ } ) ;
1945+ resIdx ++ ;
1946+ }
19631947 } ) ;
19641948 } ) ;
19651949 } catch ( e ) {
0 commit comments