Skip to content

File: ShaderFlow/Modules/Others/Bouncing.py

ShaderFlow.Modules.Others.Bouncing

ShaderBouncing

Bases: ShaderModule

Source code in Projects/ShaderFlow/ShaderFlow/Modules/Others/Bouncing.py
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
@define
class ShaderBouncing(ShaderModule):
    name: str = "iBounce"
    position: numpy.ndarray = None
    velocity: numpy.ndarray = None
    aspect_ratio: float = 1

    def setup(self):
        self.set_velocity_polar(1, random.uniform(0, TAU))
        self.position = numpy.array((0.0, 0.0))

    def update(self):
        self.position += (self.velocity * self.scene.dt)

        for i, limit in enumerate((self.aspect_ratio, 1)):
            if abs(self.position[i]) > limit:
                self.velocity[i] = -self.velocity[i]
                self.position[i] = clamp(self.position[i], -limit, limit)

    def pipeline(self) -> Iterable[ShaderVariable]:
        yield Uniform("vec2", f"{self.name}Position", self.position)
        yield Uniform("vec2", f"{self.name}Velocity", self.velocity)

    # # Quality of Life

    def set_velocity_polar(self, magnitude: float, angle: Degrees):
        self.velocity = magnitude*numpy.array((cos(angle), sin(angle)))

    @property
    def x(self) -> float:
        return self.position[0]

    @property
    def y(self) -> float:
        return self.position[1]

    @x.setter
    def x(self, value: float):
        self.position[0] = value

    @y.setter
    def y(self, value: float):
        self.position[1] = value

    # # Advanced

    def advanced_ratios(self, image: LoadableImage, steps: int=1000) -> ShaderTexture:
        """Get a texture of `aspect_ratio(angle)` from linspace(0, tau, steps)"""
        ratios = numpy.zeros((steps, 1), dtype=numpy.float32)
        image = numpy.array(LoadImage(image))
        width, height, _ = image.shape
        bigger = max(width, height)

        import cv2

        # image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
        image = image[:, :, 3]

        # Make anew image with a centered raw copy
        square = numpy.zeros((bigger, bigger), dtype=numpy.uint8)
        x, y = (bigger-width)//2, (bigger-height)//2
        square[x:x+width, y:y+height] = image

        # Rotate the image and find its alpha content bounding box
        for i, angle in enumerate(numpy.linspace(0, 360, steps)):
            rotation = cv2.getRotationMatrix2D((bigger/2, bigger/2), angle, 1)
            rotated  = cv2.warpAffine(square, rotation, (bigger, bigger))
            thresh   = cv2.threshold(rotated, 4, 255, cv2.THRESH_BINARY)[1]
            contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
            _, _, w, h = cv2.boundingRect(max(contours, key=cv2.contourArea))
            ratios[i][0] = (w/h)

        return ShaderTexture(
            name=f"{self.name}AspectRatio",
            scene=self.scene,
            components=1,
            width=steps,
            height=1,
        ).from_numpy(ratios)

name

name: str = 'iBounce'

position

position: numpy.ndarray = None

velocity

velocity: numpy.ndarray = None

aspect_ratio

aspect_ratio: float = 1

setup

setup()
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Others/Bouncing.py
23
24
25
def setup(self):
    self.set_velocity_polar(1, random.uniform(0, TAU))
    self.position = numpy.array((0.0, 0.0))

update

update()
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Others/Bouncing.py
27
28
29
30
31
32
33
def update(self):
    self.position += (self.velocity * self.scene.dt)

    for i, limit in enumerate((self.aspect_ratio, 1)):
        if abs(self.position[i]) > limit:
            self.velocity[i] = -self.velocity[i]
            self.position[i] = clamp(self.position[i], -limit, limit)

pipeline

pipeline() -> Iterable[ShaderVariable]
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Others/Bouncing.py
35
36
37
def pipeline(self) -> Iterable[ShaderVariable]:
    yield Uniform("vec2", f"{self.name}Position", self.position)
    yield Uniform("vec2", f"{self.name}Velocity", self.velocity)

set_velocity_polar

set_velocity_polar(magnitude: float, angle: Degrees)
Source code in Projects/ShaderFlow/ShaderFlow/Modules/Others/Bouncing.py
41
42
def set_velocity_polar(self, magnitude: float, angle: Degrees):
    self.velocity = magnitude*numpy.array((cos(angle), sin(angle)))

x

x: float

y

y: float

advanced_ratios

advanced_ratios(
    image: LoadableImage, steps: int = 1000
) -> ShaderTexture

Get a texture of aspect_ratio(angle) from linspace(0, tau, steps)

Source code in Projects/ShaderFlow/ShaderFlow/Modules/Others/Bouncing.py
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
def advanced_ratios(self, image: LoadableImage, steps: int=1000) -> ShaderTexture:
    """Get a texture of `aspect_ratio(angle)` from linspace(0, tau, steps)"""
    ratios = numpy.zeros((steps, 1), dtype=numpy.float32)
    image = numpy.array(LoadImage(image))
    width, height, _ = image.shape
    bigger = max(width, height)

    import cv2

    # image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    image = image[:, :, 3]

    # Make anew image with a centered raw copy
    square = numpy.zeros((bigger, bigger), dtype=numpy.uint8)
    x, y = (bigger-width)//2, (bigger-height)//2
    square[x:x+width, y:y+height] = image

    # Rotate the image and find its alpha content bounding box
    for i, angle in enumerate(numpy.linspace(0, 360, steps)):
        rotation = cv2.getRotationMatrix2D((bigger/2, bigger/2), angle, 1)
        rotated  = cv2.warpAffine(square, rotation, (bigger, bigger))
        thresh   = cv2.threshold(rotated, 4, 255, cv2.THRESH_BINARY)[1]
        contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]
        _, _, w, h = cv2.boundingRect(max(contours, key=cv2.contourArea))
        ratios[i][0] = (w/h)

    return ShaderTexture(
        name=f"{self.name}AspectRatio",
        scene=self.scene,
        components=1,
        width=steps,
        height=1,
    ).from_numpy(ratios)