From 1521e99bcbdc776fb888b4d3e7efa276688f1d25 Mon Sep 17 00:00:00 2001 From: Brady Johnston Date: Mon, 16 Sep 2024 11:38:05 +0800 Subject: [PATCH 1/3] add option to offset start of trajectory playback --- .../entities/trajectory/trajectory.py | 50 ++++++++++++------- molecularnodes/props.py | 8 ++- molecularnodes/ui/panel.py | 1 + 3 files changed, 39 insertions(+), 20 deletions(-) diff --git a/molecularnodes/entities/trajectory/trajectory.py b/molecularnodes/entities/trajectory/trajectory.py index 60b18930..9eff302a 100644 --- a/molecularnodes/entities/trajectory/trajectory.py +++ b/molecularnodes/entities/trajectory/trajectory.py @@ -94,6 +94,17 @@ def subframes(self, value: int): return None obj.mn.subframes = value + @property + def offset(self) -> int: + try: + return self.object.mn.offset + except AttributeError: + return None + + @offset.setter + def offset(self, value: int): + self.object.mn.offset = value + @property def interpolate(self) -> bool: obj = self.object @@ -516,15 +527,17 @@ def _update_positions(self, frame): raise ObjectMissingError( "Object is deleted and unable to establish a connection with a new Blender Object." ) - try: - subframes = obj.mn.subframes - interpolate = obj.mn.interpolate - except ReferenceError as e: - print(e) - return None - - if frame < 0: - return None + subframes: int = obj.mn.subframes + interpolate: bool = obj.mn.interpolate + offset: int = obj.mn.offset + + # we subtraect the offset, a negative offset value ensures that the trajectory starts + # playback that many frames before 0 and a positive value ensures we start the + # playback after 0 + frame -= offset + # for actually getting frames from the trajectory we need to clamp it to a lower + # bound of 0 which will be the start frame for the trajectory + frame = max(frame, 0) if frame_mapping: # add the subframes to the frame mapping @@ -553,27 +566,26 @@ def _update_positions(self, frame): fraction = frame % (subframes + 1) / (subframes + 1) # get the positions for the next frame - locations_a = self.positions + positions_a = self.positions if frame_b < universe.trajectory.n_frames: self.frame = frame_b - locations_b = self.positions + positions_b = self.positions if obj.mn.correct_periodic and self.is_orthorhombic: - locations_b = correct_periodic_positions( - locations_a, - locations_b, + positions_b = correct_periodic_positions( + positions_a, + positions_b, dimensions=universe.dimensions[:3] * self.world_scale, ) # interpolate between the two sets of positions - locations = lerp(locations_a, locations_b, t=fraction) + positions = lerp(positions_a, positions_b, t=fraction) else: - locations = self.positions + positions = self.positions - # update the positions of the underlying vertices and record which frame was used - # for setting these positions - self.set_position(locations) + # update the positions of the underlying vertices + self.set_position(positions) def __repr__(self): return f" Date: Mon, 16 Sep 2024 12:14:49 +0800 Subject: [PATCH 2/3] test for offset --- tests/test_trajectory.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_trajectory.py b/tests/test_trajectory.py index 4ec1ec2d..a1ff5a4b 100644 --- a/tests/test_trajectory.py +++ b/tests/test_trajectory.py @@ -90,6 +90,27 @@ def test_trajectory_update(self, snapshot_custom, Trajectory): assert not np.allclose(pos_a, pos_b) + @pytest.mark.parametrize("offset", [-2, 2]) + def test_trajectory_offset(self, Trajectory, offset): + traj = Trajectory + traj.offset = 0 + bpy.context.scene.frame_set(0) + pos_0 = traj.named_attribute("position") + + # if we change the offset, we should change the positions if negative + traj.offset = offset + if offset < 0: + assert not np.allclose(pos_0, traj.named_attribute("position")) + else: + assert np.allclose(pos_0, traj.named_attribute("position")) + bpy.context.scene.frame_set(offset - 1) + assert np.allclose(pos_0, traj.named_attribute("position")) + + # after resetting the offset to 0, it should be the same as the initial positions + bpy.context.scene.frame_set(0) + traj.offset = 0 + assert np.allclose(pos_0, traj.named_attribute("position")) + @pytest.mark.parametrize("interpolate", [True, False]) def test_subframes(self, Trajectory, interpolate): traj = Trajectory From d9288bcc734974f18d94fb78ac15706b0fceffbf Mon Sep 17 00:00:00 2001 From: Brady Johnston Date: Mon, 16 Sep 2024 12:15:46 +0800 Subject: [PATCH 3/3] add more comment for test --- tests/test_trajectory.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_trajectory.py b/tests/test_trajectory.py index a1ff5a4b..f76eecad 100644 --- a/tests/test_trajectory.py +++ b/tests/test_trajectory.py @@ -97,7 +97,9 @@ def test_trajectory_offset(self, Trajectory, offset): bpy.context.scene.frame_set(0) pos_0 = traj.named_attribute("position") - # if we change the offset, we should change the positions if negative + # if the offset is negative, the positions of the starting frame 0 will change. + # if the offset is positive, then all of the frames up till the offset frame + # will remain the same traj.offset = offset if offset < 0: assert not np.allclose(pos_0, traj.named_attribute("position"))