Skip to content

Commit 823bfee

Browse files
AmirMohammad CheraghaliAmirMohammad Cheraghali
authored andcommitted
fix: Robust 2-pass smoothing algorithm to fix gradient artifacts and logic errors
1 parent 3cbfae4 commit 823bfee

1 file changed

Lines changed: 23 additions & 39 deletions

File tree

src/components/ProteinViewer.tsx

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)