Passed
Push — master ( bbd02a...7ebed9 )
by Matt
02:12
created

PyDMXControl.effects.Color._Chase.Chase.callback()   B

Complexity

Conditions 8

Size

Total Lines 43
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 26
dl 0
loc 43
rs 7.3333
c 0
b 0
f 0
cc 8
nop 1
1
"""
2
 *  PyDMXControl: A Python 3 module to control DMX using uDMX.
3
 *                Featuring fixture profiles, built-in effects and a web control panel.
4
 *  <https://github.com/MattIPv4/PyDMXControl/>
5
 *  Copyright (C) 2022 Matt Cowley (MattIPv4) ([email protected])
6
"""
7
8
from math import ceil, floor
9
10
from ..defaults import Effect
11
from ... import Colors
12
from ...utils.exceptions import MissingArgumentException, InvalidArgumentException
13
14
15
class Chase(Effect):
16
17
    def __init__(self, *args, **kwargs):
18
        if 'colors' not in kwargs:
19
            raise MissingArgumentException('colors', True)
20
21
        self.__colors = kwargs['colors'].copy()
22
        del kwargs['colors']
23
        self.__length = len(self.__colors)
24
25
        if self.__length < 2:
26
            raise InvalidArgumentException('callback', 'Must contain two or more colors', True)
27
28
        self.__snap = False
29
        if 'snap' in kwargs and isinstance(kwargs['snap'], bool):
30
            self.__snap = kwargs['snap']
31
        if 'snap' in kwargs:
32
            del kwargs['snap']
33
34
        super().__init__(*args, **kwargs)
35
36
        # Start of loop
37
        self.__start = None
38
39
    def callback(self):
40
        # New
41
        if self.__start is None:
42
            self.__start = self.fixture.controller.ticker.millis_now()
43
44
        offset = (self.speed / self.__length) * self.offset  # Calculate offset duration
45
        start = self.__start + offset  # Account for initial offset
46
        since_start = self.fixture.controller.ticker.millis_now() - start  # Calculate time since effect started
47
        since_last = since_start % self.speed  # Calculate time since this loop started
48
49
        # Get progress through this loop (excl delay)
50
        progress = since_last / self.speed
51
52
        # Ensure in range 0 <= p <= 1
53
        while progress > 1:
54
            progress = progress - 1
55
        while progress < 0:
56
            progress = 1 + progress
57
58
        # Convert to color index
59
        progress_index = self.__length * progress
60
        next_i = ceil(progress_index) - 1
61
        previous_i = floor(progress_index) - 1
62
        percent = 1 - (progress_index - 1 - previous_i)
63
64
        # Hit 0% & 100%
65
        if percent >= 0.99:
66
            percent = 1
67
        if percent <= 0.01:
68
            percent = 0
69
70
        # Snapping
71
        if self.__snap:
72
            if percent <= 0.5:
73
                percent = 0
74
            else:
75
                percent = 1
76
77
        # Generate color
78
        color = Colors.mix(self.__colors[previous_i], self.__colors[next_i], percent)
79
80
        # Apply color
81
        self.fixture.color(color, 0)
82
83
    def start(self):
84
        self.__start = None
85
        super().start()
86