Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gap_remove.py « operators « power_sequencer - git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 7f2ec5ae17b131d866b815da2e59f10b218d21f0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors

# This file is part of Power Sequencer.

import bpy
from operator import attrgetter

from .utils.doc import doc_name, doc_idname, doc_brief, doc_description
from .utils.functions import slice_selection


class POWER_SEQUENCER_OT_gap_remove(bpy.types.Operator):
    """
    Remove gaps, starting from the first frame, with the ability to ignore locked strips
    """

    doc = {
        "name": doc_name(__qualname__),
        "demo": "",
        "description": doc_description(__doc__),
        "shortcuts": [],
        "keymap": "Sequencer",
    }
    bl_idname = doc_idname(__qualname__)
    bl_label = doc["name"]
    bl_description = doc_brief(doc["description"])
    bl_options = {"REGISTER", "UNDO"}

    ignore_locked: bpy.props.BoolProperty(
        name="Ignore Locked Strips",
        description="Remove gaps without moving locked strips",
        default=True,
    )
    all: bpy.props.BoolProperty(
        name="Remove All",
        description="Remove all gaps starting from the time cursor",
        default=False,
    )
    frame: bpy.props.IntProperty(
        name="Frame",
        description="Frame to remove gaps from, defaults at the time cursor",
        default=-1,
    )
    move_time_cursor: bpy.props.BoolProperty(
        name="Move Time Cursor",
        description="Move the time cursor when closing the gap",
        default=False,
    )

    @classmethod
    def poll(cls, context):
        return context.sequences

    def execute(self, context):
        frame = self.frame if self.frame >= 0 else context.scene.frame_current
        sequences = (
            [s for s in context.sequences if not s.lock]
            if self.ignore_locked
            else context.sequences
        )
        sequences = [
            s for s in sequences if s.frame_final_start >= frame or s.frame_final_end > frame
        ]
        sequence_blocks = slice_selection(context, sequences)
        if not sequence_blocks:
            return {"FINISHED"}

        gap_frame = self.find_gap_frame(context, frame, sequence_blocks[0])
        if gap_frame == -1:
            return {"FINISHED"}

        first_block_start = min(
            sequence_blocks[0], key=attrgetter("frame_final_start")
        ).frame_final_start
        blocks_after_gap = (
            sequence_blocks[1:] if first_block_start <= gap_frame else sequence_blocks
        )

        self.gaps_remove(context, blocks_after_gap, gap_frame)
        if self.move_time_cursor:
            context.scene.frame_current = gap_frame
        return {"FINISHED"}

    def find_gap_frame(self, context, frame, sorted_sequences):
        """
        Finds and returns the frame at which the gap starts.
        Takes a list sequences sorted by frame_final_start.
        """
        strips_start = min(sorted_sequences, key=attrgetter("frame_final_start")).frame_final_start
        strips_end = max(sorted_sequences, key=attrgetter("frame_final_end")).frame_final_end

        gap_frame = -1
        if strips_start > frame:
            strips_before_frame_start = [s for s in context.sequences if s.frame_final_end <= frame]
            frame_target = 0
            if strips_before_frame_start:
                frame_target = max(
                    strips_before_frame_start, key=attrgetter("frame_final_end")
                ).frame_final_end
            gap_frame = frame_target if frame_target < strips_start else frame
        else:
            gap_frame = strips_end
        return gap_frame

    def gaps_remove(self, context, sequence_blocks, gap_frame_start):
        """
        Recursively removes gaps between blocks of sequences.
        """

        gap_frame = gap_frame_start
        for block in sequence_blocks:
            gap_size = block[0].frame_final_start - gap_frame
            if gap_size < 1:
                continue

            for s in block:
                try:
                    s.frame_start -= gap_size
                except AttributeError:
                    continue

            self.move_markers(context, gap_frame, gap_size)
            if not self.all:
                break
            gap_frame = block[-1].frame_final_end

    def move_markers(self, context, gap_frame, gap_size):
        markers = (m for m in context.scene.timeline_markers if m.frame > gap_frame)
        for m in markers:
            m.frame -= min({gap_size, m.frame - gap_frame})