Comments (3)
It seems that the opacity is controlled by the property checked
of each pocket
object in the pockets
array.
When this value changes, I suspect that the pockets
array changes as well.
pockets
is reactive dependency of 2 different useEffect: one that sets the opacity, and the other one that loads the pocket data.
When pockets
changes both useEffect are executed: the second one changes the opacity of representations, and the first one reloads data from server. When the data from server is fetched, setPocketData
is executed.
This changes pocketData
so memoizedPocketData
gets a new value (because it has pocketData
as a dependency; note that this memoization is never effective there)
Next, the useEffect that depends on memoizedPocketData
is executed as well, pocket representations are removed and recreated with an opacity of 0.3.
IMO, this bug is not related to NGL, but is related to React intricacies. Cascades of useEffect make the execution of the code difficult to reason about.
To circumvent this, I would suggest 2 options:
- regarding the pocket objects: split the data part from the controls part. Like that a change in the cheched property would not cause the refetch of data and re-creation of the pockets
- second option would be to keep the same data structure, but to control the whole pocket management in a single useEffect. To avoid refetch/rerenders, you can use the
name
property for the representation and check wether they have already been created or not. You'd have to manage the internal state of the NGL stage by yourself to avoid unneeded recomputations. I personally prefer doing that, rather than let React logics take care of the NGL state because React philosophy is to re-render components multiple times to sync with state which is acceptable for the DOM, but not suitable for 3D rendering and scientific computation
from ngl.
I created a separate component without any additional logic to try to implement the desired behavior. The screen recording shows that with all attempts to trigger only one mutable pocket object, I have a loading file on both pocket files:
Screen.Recording.2024-01-28.at.11.56.42.mov
Code:
import { useState, useEffect, useRef } from "react";
import * as NGL from "ngl";
import pdbUrl from "../../assets/test-pockets/3hi7.pdb";
import pocket1Url from "../../assets/test-pockets/pocket1.sdf";
import pocket2Url from "../../assets/test-pockets/pocket2.sdf";
const TestOpacity = () => {
const containerRef = useRef();
const stageRef = useRef();
const pocketReps = useRef([]); // To store pocket representations
const [pocketsArray, setPocketsArray] = useState([
{ url: pocket1Url, checked: false, color: "red", name: 'red' },
{ url: pocket2Url, checked: false, color: 'blue', name: 'blue' },
]);
const prevPocketsRef = useRef();
// Initialize NGL Stage
useEffect(() => {
const stage = new NGL.Stage(containerRef.current, {
backgroundColor: "#ededed",
});
stageRef.current = stage;
// Cleanup function
return () => {
if (stage) {
stage.viewer.wrapper.remove();
stage.viewer.renderer.forceContextLoss();
stage.dispose();
}
};
}, []);
// Load main PDB file
useEffect(() => {
if (pdbUrl && stageRef.current) {
stageRef.current
.loadFile(pdbUrl, {
defaultRepresentation: true,
})
.then((o) => {
console.log("Loaded object:", o);
o.addRepresentation("cartoon");
o.autoView();
})
.catch((error) => {
console.error("Error loading file:", error);
});
}
}, [pdbUrl]);
// Load pockets only once or when URLs change
useEffect(() => {
if (stageRef.current) {
pocketsArray.forEach((pocket, index) => {
stageRef.current
.loadFile(pocket.url)
.then((o) => {
const rep = o.addRepresentation("surface", {
color: pocket.color,
opacity: pocket.checked ? 1 : 0.3,
});
pocketReps.current[index] = rep;
})
.catch((error) => {
console.error("Error loading pocket:", error);
});
});
}
const currentPocketReps = pocketReps.current;
// Cleanup function
return () => {
currentPocketReps.forEach(rep => rep && rep.dispose());
};
}, [pocketsArray.map(pocket => pocket.url)]);
// Update opacity when checked state changes
useEffect(() => {
pocketsArray.forEach((pocket, index) => {
const rep = pocketReps.current[index];
if (rep && prevPocketsRef.current && prevPocketsRef.current[index].checked !== pocket.checked) {
rep.setParameters({ opacity: pocket.checked ? 1 : 0.3 });
}
});
prevPocketsRef.current = pocketsArray;
}, [pocketsArray.map(pocket => pocket.checked)]);
// Toggle checked state
const toggleChecked = (index) => {
setPocketsArray((currentPockets) =>
currentPockets.map((pocket, idx) =>
idx === index ? { ...pocket, checked: !pocket.checked } : pocket
)
);
};
return (
<div>
<div>
{pocketsArray.map((pocket, index) => (
<label key={index}>
<input
type="checkbox"
checked={pocket.checked}
onChange={() => toggleChecked(index)}
/>
Pocket {pocket.name}
</label>
))}
</div>
<div ref={containerRef} style={{ width: "90vw", height: "90vh" }} />
</div>
);
};
export default TestOpacity;
from ngl.
This line of code in the dependency list of the useEffect creates a new reference at each rerender because it always returns a new array:
pocketsArray.map(pocket => pocket.url)
These questions have nothing to do with bugs or features in NGL. I can't dedicate time to reviewing third party code
from ngl.
Related Issues (20)
- ngl/src/store /residue-type.ts isRna() issue HOT 5
- An option to hide intra-protein non-bonded interactions would be nice [feature request] HOT 1
- Use addArrow to add Cartesian axes HOT 1
- dispose() method leaves part of the old representation when changing model HOT 2
- Should threejs be changed to a peer dependency? HOT 2
- change color of specific residue HOT 2
- getAtomSetWithinSelection and getAtomSetWithinGroup in several files HOT 4
- info in tooltip HOT 2
- Mass in .pdb files HOT 2
- Focus camera on selection
- Allow more export of components HOT 4
- Why is the effect after superposition not as good as before superposition HOT 1
- Export .pdb file HOT 2
- Question: How to save and restore transformation?
- Funnel like shape
- Multi-touch zoom with multiple NGL instances
- Show connections in two difference files HOT 4
- Hiding non-polar hydrogens HOT 1
- MeasureRepresentation between non-atoms HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from ngl.