diff --git a/editor-blender/core/actions/property/partial_load.py b/editor-blender/core/actions/property/partial_load.py index 54050cf0..5591047c 100644 --- a/editor-blender/core/actions/property/partial_load.py +++ b/editor-blender/core/actions/property/partial_load.py @@ -24,12 +24,10 @@ def set_loaded_frame_at_full_range(): music_frame = state.partial_load_frames state.dancer_load_frames = music_frame - state.dancer_load_frames = (music_frame[0], music_frame[1]) + set_min_max_frame(music_frame[0], music_frame[1]) if not bpy.context: return - bpy.context.scene.frame_start = music_frame[0] - bpy.context.scene.frame_end = music_frame[1] def set_state_of_loaded_frame_range(): @@ -42,8 +40,6 @@ def set_state_of_loaded_frame_range(): if not bpy.context: return - bpy.context.scene.frame_start = min_frame - bpy.context.scene.frame_end = max_frame def init_loaded_frame_range(): diff --git a/editor-blender/core/actions/property/timeline.py b/editor-blender/core/actions/property/timeline.py index 95ea9539..3f8e41eb 100644 --- a/editor-blender/core/actions/property/timeline.py +++ b/editor-blender/core/actions/property/timeline.py @@ -31,6 +31,12 @@ def set_current_frame_index(self: bpy.types.WindowManager, value: str): current_frame = state.control_map[current_frame_id] start = current_frame.start + if ( + start < state.dancer_load_frames[0] + or start > state.dancer_load_frames[1] + ): + return + bpy.context.scene.frame_current = start state.current_control_index = current_frame_index @@ -44,6 +50,12 @@ def set_current_frame_index(self: bpy.types.WindowManager, value: str): current_frame = state.pos_map[current_frame_id] start = current_frame.start + if ( + start < state.dancer_load_frames[0] + or start > state.dancer_load_frames[1] + ): + return + bpy.context.scene.frame_current = start state.current_pos_index = current_frame_index @@ -104,7 +116,7 @@ def set_time(self: bpy.types.WindowManager, value: str): if not bpy.context: return frame = time_to_frame(value) - if frame < 0: + if frame < state.dancer_load_frames[0] or frame > state.dancer_load_frames[1]: return self["ld_time"] = value diff --git a/editor-blender/core/actions/state/control_editor.py b/editor-blender/core/actions/state/control_editor.py index 31f180b8..170b3ca6 100644 --- a/editor-blender/core/actions/state/control_editor.py +++ b/editor-blender/core/actions/state/control_editor.py @@ -93,7 +93,6 @@ async def add_control_frame(): set_requesting(False) -# TODO finish this function async def save_control_frame(start: int | None = None): if not bpy.context: return diff --git a/editor-blender/core/actions/state/pos_editor.py b/editor-blender/core/actions/state/pos_editor.py index 89b59293..5568b469 100644 --- a/editor-blender/core/actions/state/pos_editor.py +++ b/editor-blender/core/actions/state/pos_editor.py @@ -5,6 +5,7 @@ from ...log import logger from ...models import DancerName, EditingData, EditMode, Location from ...states import state +from ...utils.algorithms import linear_interpolation from ...utils.notification import notify from ...utils.ui import redraw_area from .app_state import set_requesting @@ -78,13 +79,6 @@ def pos_frame_neighbors( ) -def linear_interpolation(lval: float, ldist: int, rval: float, rdist: int): - if ldist == 0 and rdist == 0: - return lval - else: - return (lval * float(rdist) + rval * float(ldist)) / float(ldist + rdist) - - async def add_pos_frame(): if not bpy.context: return diff --git a/editor-blender/core/actions/state/timeline.py b/editor-blender/core/actions/state/timeline.py index c924cb64..773acda3 100644 --- a/editor-blender/core/actions/state/timeline.py +++ b/editor-blender/core/actions/state/timeline.py @@ -1,3 +1,5 @@ +from typing import Literal + import bpy from ...models import Editor @@ -7,18 +9,37 @@ def increase_frame_index(): if not bpy.context: return + match state.editor: case Editor.CONTROL_EDITOR: + sorted_ctrl_frames = sorted( + [item[1].start for item in state.control_map.items()] + ) current_frame_index = state.current_control_index + if current_frame_index < len(state.control_record) - 1: + if ( + sorted_ctrl_frames[current_frame_index + 1] + >= state.dancer_load_frames[1] + ): + return setattr( bpy.context.window_manager, "ld_current_frame_index", str(current_frame_index + 1), ) case Editor.POS_EDITOR: + sorted_pos_frames = sorted( + [item[1].start for item in state.pos_map.items()] + ) current_frame_index = state.current_pos_index + if current_frame_index < len(state.pos_record) - 1: + if ( + sorted_pos_frames[current_frame_index + 1] + >= state.dancer_load_frames[1] + ): + return setattr( bpy.context.window_manager, "ld_current_frame_index", @@ -33,16 +54,34 @@ def decrease_frame_index(): return match state.editor: case Editor.CONTROL_EDITOR: + sorted_ctrl_frames = sorted( + [item[1].start for item in state.control_map.items()] + ) current_frame_index = state.current_control_index + if current_frame_index > 0: + if ( + sorted_ctrl_frames[current_frame_index] + <= state.dancer_load_frames[0] + ): + return setattr( bpy.context.window_manager, "ld_current_frame_index", str(current_frame_index - 1), ) case Editor.POS_EDITOR: + sorted_pos_frames = sorted( + [item[1].start for item in state.pos_map.items()] + ) current_frame_index = state.current_pos_index + if current_frame_index > 0: + if ( + sorted_pos_frames[current_frame_index] + <= state.dancer_load_frames[0] + ): + return setattr( bpy.context.window_manager, "ld_current_frame_index", diff --git a/editor-blender/core/utils/algorithms.py b/editor-blender/core/utils/algorithms.py index feeb9867..3d2df8ce 100644 --- a/editor-blender/core/utils/algorithms.py +++ b/editor-blender/core/utils/algorithms.py @@ -17,7 +17,7 @@ def binary_search(arr: list[int], x: int) -> int: return r -def _binary_search_with_lr( +def binary_search_for_neighbors( arr: list[int], x: int ) -> ( tuple[int, int] @@ -59,8 +59,8 @@ def binary_search_for_range(arr: list[int], left: int, right: int) -> tuple[int, :param left, right: designated range :return: the smallest continuous range in arr that includes [left, right], which is in the form of indexes """ - search_l = _binary_search_with_lr(arr, left)[0] - search_r = _binary_search_with_lr(arr, right)[1] + search_l = binary_search_for_neighbors(arr, left)[0] + search_r = binary_search_for_neighbors(arr, right)[1] if search_l == "OutOfRange_Larger": return (len(arr) - 1, len(arr) - 1) @@ -71,3 +71,10 @@ def binary_search_for_range(arr: list[int], left: int, right: int) -> tuple[int, if search_r == "OutOfRange_Larger": search_r = len(arr) - 1 return (search_l, search_r) + + +def linear_interpolation(lval: float, ldist: int, rval: float, rdist: int): + if ldist == 0 and rdist == 0: + return lval + else: + return (lval * float(rdist) + rval * float(ldist)) / float(ldist + rdist) diff --git a/editor-blender/operators/editor/__init__.py b/editor-blender/operators/editor/__init__.py index 6f4afd08..ac58d48c 100644 --- a/editor-blender/operators/editor/__init__.py +++ b/editor-blender/operators/editor/__init__.py @@ -290,7 +290,10 @@ def poll(cls, context: bpy.types.Context | None): return state.ready and ld_ui_led_editor.edit_effect != "" else: - return state.ready + shown_all_dancer = reduce( + lambda acc, cur: acc & cur, state.show_dancers, True + ) + return state.ready and shown_all_dancer async def async_execute(self, context: bpy.types.Context): confirm: bool = getattr(self, "confirm") diff --git a/editor-blender/operators/load/__init__.py b/editor-blender/operators/load/__init__.py index 68833c68..5de57a3f 100644 --- a/editor-blender/operators/load/__init__.py +++ b/editor-blender/operators/load/__init__.py @@ -13,11 +13,9 @@ # import state class LoadPartialOperator(AsyncOperator): bl_idname = "lightdance.load_partial" - bl_label = "Load frames for selected dancers" - # bl_label = "Load frames in selected interval" + bl_label = "Load frames of selected dancers and time interval" async def async_execute(self, context: bpy.types.Context): - # TODO set_state_of_dancer_selection() set_state_of_loaded_frame_range() await init_load() diff --git a/editor-blender/operators/timeline/__init__.py b/editor-blender/operators/timeline/__init__.py index a2a35761..86114b77 100644 --- a/editor-blender/operators/timeline/__init__.py +++ b/editor-blender/operators/timeline/__init__.py @@ -1,6 +1,9 @@ import bpy from ...core.actions.state.timeline import decrease_frame_index, increase_frame_index +from ...core.models import Editor +from ...core.states import state +from ...core.utils.algorithms import binary_search_for_neighbors class IncreaseFrameIndex(bpy.types.Operator): diff --git a/editor-blender/panels/lightdance/__init__.py b/editor-blender/panels/lightdance/__init__.py index 6b4b061b..e3824ca8 100644 --- a/editor-blender/panels/lightdance/__init__.py +++ b/editor-blender/panels/lightdance/__init__.py @@ -1,7 +1,6 @@ from typing import Any import bpy -from bpy.types import Context, UILayout from ...core.states import state from ...properties.types import Preferences @@ -44,8 +43,8 @@ class LD_UL_PartialDancerLoad(bpy.types.UIList): def draw_item( self, - context: Context | None, - layout: UILayout, + context: bpy.types.Context | None, + layout: bpy.types.UILayout, data: Any | None, item: DancerSelectionType | None, icon: int | None, @@ -61,7 +60,9 @@ def draw_item( row.prop(item, "shown", text="", emboss=True) row.label(text=item.name) - def draw_filter(self, context: Context | None, layout: UILayout): + def draw_filter( + self, context: bpy.types.Context | None, layout: bpy.types.UILayout + ): row = layout.row() row.prop(self, "select_all_connect", text="Select all connected RPi") row.prop(self, "select_all", text="Select all RPi") diff --git a/editor-blender/properties/ui/frame_range.py b/editor-blender/properties/ui/frame_range.py index 56b313e0..042b6ec2 100644 --- a/editor-blender/properties/ui/frame_range.py +++ b/editor-blender/properties/ui/frame_range.py @@ -7,6 +7,10 @@ def update_frame_range_min(self, _): if self.ld_ui_frame_range_min >= self.ld_ui_frame_range_max: self.ld_ui_frame_range_min = self.ld_ui_frame_range_max - 1 + if not bpy.context or not bpy.context.scene: + return + bpy.context.scene.frame_start = self.ld_ui_frame_range_min + def update_frame_range_max(self, _): if ( @@ -16,6 +20,9 @@ def update_frame_range_max(self, _): self.ld_ui_frame_range_max = state.partial_load_frames[1] elif self.ld_ui_frame_range_max <= self.ld_ui_frame_range_min: self.ld_ui_frame_range_max = self.ld_ui_frame_range_min + 1 + if not bpy.context or not bpy.context.scene: + return + bpy.context.scene.frame_end = self.ld_ui_frame_range_max def register():