Coverage for ase / gui / history.py: 82.81%

64 statements  

« prev     ^ index     » next       coverage.py v7.13.3, created at 2026-02-04 10:20 +0000

1# Right now the gui.update_history method is peppered around the code. 

2# It should probably work off the observers but right now I am not sure 

3# it can. Since draw() is called all over the place and it calls the 

4# change_atoms observer, this would lead to a lot of saved history 

5# states where nothing actually changed. If the proposed refactoring is 

6# done some day, registering update_history to the observers seems like 

7# the correct move. 

8 

9 

10class History: 

11 def __init__(self, images): 

12 self.max_entries = 20 

13 self.images = images 

14 self.initialize_history() 

15 

16 def _is_at_end(self, frame): 

17 frame_history = self.history_per_image[frame] 

18 now = self.now_per_image[frame] 

19 return frame_history[now] is frame_history[-1] 

20 

21 def _is_at_start(self, frame): 

22 frame_history = self.history_per_image[frame] 

23 now = self.now_per_image[frame] 

24 return frame_history[now] is frame_history[0] 

25 

26 def append_image(self, image): 

27 self.history_per_image.append([image.copy()]) 

28 self.now_per_image.append(-1) 

29 

30 def cut_tail(self, frame: int, i: int): 

31 self.history_per_image[frame] = list(self.history_per_image[frame][:i]) 

32 # If the 'now' index would point out of range, reset it (this could 

33 # be a problem if the index becomes positive somehow): 

34 if self.now_per_image[frame] >= len(self.history_per_image[frame]): 

35 self.now_per_image[frame] = -1 

36 

37 def isolate_history(self, frame): 

38 """Takes the history of the given index and makes it the only one""" 

39 self.history_per_image = [self.history_per_image[frame]] 

40 self.now_per_image = [self.now_per_image[frame]] 

41 

42 def initialize_history(self): 

43 """Copies the images' current states to the history and creates 

44 what's necessary to track changes""" 

45 # Will contain a list for each image containing its own linear history 

46 # (as copies of the Atoms objects [or anything mutable, really]): 

47 self.history_per_image = [] 

48 # Will contain an index for each image pointing to the historic entry 

49 # which is 'active': 

50 self.now_per_image = [] 

51 for img in self.images: 

52 self.history_per_image.append([img.copy()]) 

53 self.now_per_image.append(-1) 

54 

55 def insert_image(self, frame, image): 

56 self.history_per_image.insert(frame, [image.copy()]) 

57 self.now_per_image.insert(frame, -1) 

58 

59 def pop_image(self, frame): 

60 """Removes the entire history of an image""" 

61 self.history_per_image.pop(frame) 

62 self.now_per_image.pop(frame) 

63 

64 def pop_history_item(self, frame, i): 

65 """Removes a point in an image's history""" 

66 len_history = len(self.history_per_image[frame]) 

67 # The 'now' may shift when a history item is removed 

68 # -> let's take note: 

69 i_abs = i % len_history 

70 now = self.now_per_image[frame] 

71 if now < 0: 

72 now_abs = now % len_history 

73 if now_abs < i_abs: 

74 self.now_per_image[frame] = now + 1 

75 elif now > 0 and now > i_abs: 

76 self.now_per_image[frame] = now_abs - 1 - len_history 

77 else: 

78 self.now_per_image[frame] = -1 

79 # Finally, pop out the history: 

80 self.history_per_image[frame].pop(i) 

81 

82 def redo_history(self, frame): 

83 if not self._is_at_end(frame): 

84 now = self.now_per_image[frame] + 1 

85 self.now_per_image[frame] = now 

86 self.images._images[frame] = self.history_per_image[frame][ 

87 now 

88 ].copy() 

89 

90 def undo_history(self, frame): 

91 if not self._is_at_start(frame): 

92 now = self.now_per_image[frame] - 1 

93 self.now_per_image[frame] = now 

94 self.images._images[frame] = self.history_per_image[frame][ 

95 now 

96 ].copy() 

97 

98 def update_history(self, frame): 

99 if len(self.history_per_image[frame]) >= self.max_entries: 

100 self.pop_history_item(frame, 0) 

101 if not self._is_at_end(frame): 

102 self.cut_tail(frame, self.now_per_image[frame] + 1) 

103 self.now_per_image[frame] = -1 

104 self.history_per_image[frame].append(self.images[frame].copy())