diff --git a/find_bad_motion_tracks/find_bad_tracks.py b/find_bad_motion_tracks/find_bad_tracks.py index 482ae20..a201665 100644 --- a/find_bad_motion_tracks/find_bad_tracks.py +++ b/find_bad_motion_tracks/find_bad_tracks.py @@ -163,7 +163,7 @@ def combine_badnesses(*args: Dict[str, Badness]) -> Dict[str, Badness]: adjusted_amount = badness.amount if percentile != 0: adjusted_amount = badness.amount / percentile - if adjusted_amount > to_beat_amount: + if adjusted_amount > to_beat_amount or track not in combined: combined[track] = Badness(adjusted_amount, badness.frame) return combined @@ -183,7 +183,7 @@ def find_bad_tracks(clip: MovieClip) -> Dict[str, Badness]: # Map track names to marker shape change amounts shape_badnesses: Dict[str, Badness] = {} - for frame_index in range(first_frame_index + 1, last_frame_index): + for frame_index in range(first_frame_index + 1, last_frame_index + 1): dx_list: List[TrackWithFloat] = [] dy_list: List[TrackWithFloat] = [] ddx_list: List[TrackWithFloat] = [] diff --git a/tests/test_find_bad_tracks.py b/tests/test_find_bad_tracks.py index a6ac15c..75eb693 100644 --- a/tests/test_find_bad_tracks.py +++ b/tests/test_find_bad_tracks.py @@ -1,35 +1,80 @@ -from typing import cast, List +from typing import cast, List, Tuple, Optional, Union, Any from bpy.types import ( MovieClip, MovieTracking, MovieTrackingTrack, MovieTrackingTracks, + MovieTrackingMarkers, MovieTrackingMarker, ) from find_bad_motion_tracks.find_bad_tracks import ( find_bad_tracks, shape_change_amount, + Badness, BadnessCalculator, TrackWithFloat, ) +class TestMovieTrackingMarkers(MovieTrackingMarkers): + def __init__(self, coordinates: List[Tuple[float, float]]) -> None: + super().__init__() + + self.coordinates = coordinates + + def find_frame( + self, frame: Optional[int], exact: Optional[Union[bool, Any]] = True + ) -> "MovieTrackingMarker": + marker = MovieTrackingMarker() + if frame is None or frame < 0 or frame >= len(self.coordinates): + marker.mute = True + return marker + + marker.co = self.coordinates[frame] + + # Badness code expects markers to come with four corners. Corners are + # relative to the marker position, so we give all markers the same + # corners. + marker.pattern_corners = [[-1.0, -1.0], [-1.0, 1.0], [1.0, 1.0], [1.0, -1.0]] + return marker + + def make_clip() -> MovieClip: - tracks: List[MovieTrackingTrack] = [] + """ + Create a clip with two frames and four tracks, each track moving 10 to the right. + + Each track is a list of x-y coordinate tuples. + + Each x-y coordinate tuple represents the marker position at one frame. + """ + movieTrackingTracks: List[MovieTrackingTrack] = [] + for i in range(4): + movieTrackingTrack = MovieTrackingTrack() + movieTrackingTrack.name = f"Track {i}" + + y = i * 10.0 + 10.0 + track = [(0.0 + y * 20.0, y), (10.0 + y * 20.0, y)] + movieTrackingTrack.markers = TestMovieTrackingMarkers(track) + + movieTrackingTracks.append(movieTrackingTrack) clip = MovieClip() clip.frame_start = 0 - clip.frame_duration = 10 + clip.frame_duration = 2 clip.tracking = MovieTracking() - clip.tracking.tracks = cast(MovieTrackingTracks, tracks) + clip.tracking.tracks = cast(MovieTrackingTracks, movieTrackingTracks) return clip -def test_find_bad_tracks_no_tracks() -> None: - test_clip = make_clip() - find_bad_tracks(test_clip) +def test_find_bad_tracks_all_good() -> None: + assert find_bad_tracks(make_clip()) == { + "Track 0": Badness(0.0, 1), + "Track 1": Badness(0.0, 1), + "Track 2": Badness(0.0, 1), + "Track 3": Badness(0.0, 1), + } def test_compute_badness_score() -> None: