File size: 4,014 Bytes
d49f7bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# Copyright (c) Meta Platforms, Inc. and affiliates.
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

""" Controller Abstract Base Class Module """

from __future__ import annotations
from typing import Optional
from abc import abstractmethod
import logging

from animated_drawings.model.scene import Scene
from animated_drawings.view.view import View
from animated_drawings.config import ControllerConfig


class Controller():
    """
    Base Controller class from which all other Controllers will be derived.
    Controllers are responsible for:
        - running the game loop.
        - handling user input and forwarding it to the view or scene.
        - triggering the scene's update method
        - trigger the view's render method
    """

    def __init__(self, cfg: ControllerConfig, scene: Scene) -> None:
        self.cfg: ControllerConfig = cfg
        self.scene: Scene = scene
        self.view: Optional[View] = None

    def set_scene(self, scene: Scene) -> None:
        """ Sets the scene attached to this controller."""
        self.scene = scene

    def set_view(self, view: View) -> None:
        """ Sets the view attached to this controller."""
        self.view = view

    @abstractmethod
    def _tick(self) -> None:
        """Subclass and add logic is necessary to progress time"""

    @abstractmethod
    def _update(self) -> None:
        """Subclass and add logic is necessary to update scene after progressing time"""

    @abstractmethod
    def _is_run_over(self) -> bool:
        """Subclass and add logic is necessary to end the scene"""

    @abstractmethod
    def _start_run_loop_iteration(self) -> None:
        """Subclass and add code to start run loop iteration"""

    @abstractmethod
    def _handle_user_input(self) -> None:
        """Subclass and add code to handle user input"""

    @abstractmethod
    def _render(self) -> None:
        """Subclass and add logic needed to have viewer render the scene"""

    @abstractmethod
    def _finish_run_loop_iteration(self) -> None:
        """Subclass and add steps necessary before starting next iteration of run loop. """

    @abstractmethod
    def _prep_for_run_loop(self) -> None:
        """Subclass and add anything necessary to do immediately prior to run loop. """

    @abstractmethod
    def _cleanup_after_run_loop(self) -> None:
        """Subclass and add anything necessary to do after run loop has finished. """

    def run(self) -> None:
        """ The run loop. Subclassed controllers should overload and define functionality for each step in this function."""

        self._prep_for_run_loop()
        while not self._is_run_over():
            self._start_run_loop_iteration()
            self._update()
            self._render()
            self._tick()
            self._handle_user_input()
            self._finish_run_loop_iteration()

        self._cleanup_after_run_loop()

    @staticmethod
    def create_controller(controller_cfg: ControllerConfig, scene: Scene, view: View) -> Controller:
        """ Takes in a controller dictionary from mvc config file, scene, and view. Constructs and return appropriate controller."""
        if controller_cfg.mode == 'video_render':
            from animated_drawings.controller.video_render_controller import VideoRenderController
            return VideoRenderController(controller_cfg, scene, view,)
        elif controller_cfg.mode == 'interactive':
            from animated_drawings.controller.interactive_controller import InteractiveController
            from animated_drawings.view.window_view import WindowView
            assert isinstance(view, WindowView)  # for static analysis. checks elsewhere ensure this always passes
            return InteractiveController(controller_cfg, scene, view)
        else:
            msg = f'Unknown controller mode specified: {controller_cfg.mode}'
            logging.critical(msg)
            assert False, msg