@@ -61,8 +61,31 @@ export default class GCContentAdapter extends BaseFeatureDataAdapter {
6161 )
6262 const residues = feats [ 0 ] ?. get ( 'seq' ) || ''
6363
64- let start = performance . now ( )
6564 await updateStatus ( 'Calculating GC' , statusCallback , ( ) => {
65+ // Initialize the first window
66+ let nc = 0
67+ let ng = 0
68+ let len = 0
69+ let start = performance . now ( )
70+
71+ // Calculate initial window
72+ const startIdx = halfWindowSize
73+ for (
74+ let j = startIdx - halfWindowSize ;
75+ j < startIdx + halfWindowSize ;
76+ j ++
77+ ) {
78+ const letter = residues [ j ]
79+ if ( letter === 'c' || letter === 'C' ) {
80+ nc ++
81+ } else if ( letter === 'g' || letter === 'G' ) {
82+ ng ++
83+ }
84+ if ( letter !== 'N' ) {
85+ len ++
86+ }
87+ }
88+
6689 for (
6790 let i = halfWindowSize ;
6891 i < residues . length - halfWindowSize ;
@@ -72,22 +95,52 @@ export default class GCContentAdapter extends BaseFeatureDataAdapter {
7295 checkStopToken ( stopToken )
7396 start = performance . now ( )
7497 }
75- const r = isWindowSizeOneBp
76- ? residues [ i ]
77- : residues . slice ( i - halfWindowSize , i + halfWindowSize )
78- let nc = 0
79- let ng = 0
80- let len = 0
81- for ( const letter of r ) {
82- if ( letter === 'c' || letter === 'C' ) {
83- nc ++
84- } else if ( letter === 'g' || letter === 'G' ) {
85- ng ++
98+
99+ // For windowSize === 1, just get the single character
100+ if ( isWindowSizeOneBp ) {
101+ const letter = residues [ i ]
102+ nc = letter === 'c' || letter === 'C' ? 1 : 0
103+ ng = letter === 'g' || letter === 'G' ? 1 : 0
104+ len = letter !== 'N' ? 1 : 0
105+ } else if ( i > halfWindowSize ) {
106+ // Rolling window: remove characters that are no longer in window
107+ // and add new characters that entered the window
108+ const prevStart = i - windowDelta - halfWindowSize
109+ const prevEnd = i - windowDelta + halfWindowSize
110+ const currStart = i - halfWindowSize
111+ const currEnd = i + halfWindowSize
112+
113+ // Remove old characters
114+ for ( let j = prevStart ; j < Math . min ( prevEnd , currStart ) ; j ++ ) {
115+ if ( j >= 0 && j < residues . length ) {
116+ const letter = residues [ j ]
117+ if ( letter === 'c' || letter === 'C' ) {
118+ nc --
119+ } else if ( letter === 'g' || letter === 'G' ) {
120+ ng --
121+ }
122+ if ( letter !== 'N' ) {
123+ len --
124+ }
125+ }
86126 }
87- if ( letter !== 'N' ) {
88- len ++
127+
128+ // Add new characters
129+ for ( let j = Math . max ( prevEnd , currStart ) ; j < currEnd ; j ++ ) {
130+ if ( j >= 0 && j < residues . length ) {
131+ const letter = residues [ j ]
132+ if ( letter === 'c' || letter === 'C' ) {
133+ nc ++
134+ } else if ( letter === 'g' || letter === 'G' ) {
135+ ng ++
136+ }
137+ if ( letter !== 'N' ) {
138+ len ++
139+ }
140+ }
89141 }
90142 }
143+
91144 const pos = qs
92145 const score =
93146 this . gcMode === 'content'
@@ -111,10 +164,4 @@ export default class GCContentAdapter extends BaseFeatureDataAdapter {
111164 observer . complete ( )
112165 } )
113166 }
114-
115- /**
116- * called to provide a hint that data tied to a certain region
117- * will not be needed for the foreseeable future and can be purged
118- * from caches, etc
119- */
120167}
0 commit comments