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: 4a73605150621ea41e08166cad25976d804cb7bd (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#
# Copyright (C) 2016-2020 by Nathan Lovato, Daniel Oakey, Razvan Radulescu, and contributors
#
# This file is part of Power Sequencer.
#
# Power Sequencer is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Power Sequencer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with Power Sequencer. If
# not, see <https://www.gnu.org/licenses/>.
#
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})