File: _dotcloud.py

package info (click to toggle)
python-expyriment 0.7.0%2Bgit34-g55a4e7e-3
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 1,504 kB
  • ctags: 2,094
  • sloc: python: 12,766; makefile: 150
file content (258 lines) | stat: -rw-r--r-- 7,671 bytes parent folder | download | duplicates (2)
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#!/usr/bin/env python

"""
A dotcloud stimulus.

This module contains a class implementing a dotcloud stimulus.

"""

__author__ = 'Florian Krause <florian@expyriment.org>, \
Oliver Lindemann <oliver@expyriment.org>'
__version__ = '0.7.0'
__revision__ = '55a4e7e'
__date__ = 'Wed Mar 26 14:33:37 2014 +0100'


import random
import pygame

import expyriment
from expyriment.stimuli._visual import Visual
from expyriment.stimuli._dot import Dot as Dot
import  defaults


class DotCloud(Visual):
    """A dot cloud class.

    This class creates dots arranged in a circular cloud.

    """

    def __init__(self, radius=None, position=None, background_colour=None,
                 dot_colour=None):
        """Create a dot cloud.

        Parameters
        ----------
        radius : int, optional
            radius of the cloud
        position : (int, int), optional
            position of the stimulus
        background_colour : (int, int, int), optional
            colour of the background
        dot_colour : (int, int, int), optional
            colour of the dots

        """

        Visual.__init__(self, position)
        self._cloud = []
        if radius is not None:
            self._radius = radius
        else:
            radius = defaults.dotcloud_radius
            if radius is None:
                try:
                    self._radius = min(
                        expyriment._active_exp.screen.surface.get_size()) / 2
                except:
                    raise RuntimeError("Could not get size of screen!")
        if background_colour is not None:
            self._background_colour = background_colour
        else:
            self._background_colour = \
                    defaults.dotcloud_background_colour
        if dot_colour is None:
            dot_colour = defaults.dotcloud_dot_colour
        if dot_colour is not None:
            self._dot_colour = dot_colour
        else:
            self._dot_colour = expyriment._active_exp.foreground_colour
        self.create_area()

    _getter_exception_message = "Cannot set {0} if surface exists!"

    @property
    def radius(self):
        """Getter for radius."""

        return self._radius

    @radius.setter
    def radius(self, value):
        """Setter for radius."""

        if self.has_surface:
            raise AttributeError(DotCloud._getter_exception_message.format(
                "radius"))
        else:
            self._radius = value
            self.create_area()

    @property
    def background_colour(self):
        """Getter for background_colour."""

        return self._background_colour

    @background_colour.setter
    def background_colour(self, value):
        """Setter for background_colour."""

        if self.has_surface:
            raise AttributeError(DotCloud._getter_exception_message.format(
                "background_colour"))
        else:
            self._background_colour = value
            self.create_area()

    @property
    def dot_colour(self):
        """Getter for dot_colour."""

        return self._dot_colour

    @dot_colour.setter
    def dot_colour(self, value):
        """Setter for dot_colour."""

        if self.has_surface:
            raise AttributeError(DotCloud._getter_exception_message.format(
                "dot_colour"))
        else:
            self._dot_colour = value
            self.create_area()

    @property
    def area(self):
        """Getter for area."""

        return self._area

    def _create_surface(self):
        """Create the surface of the stimulus."""

        surface = self.area._get_surface()
        for dot in self._cloud:
            dot.rect = pygame.Rect((0, 0), dot.surface_size)
            surface_size = surface.get_size()
            dot.rect.center = [dot.position[0] + surface_size[0] / 2,
                               dot.position[1] + surface_size[1] / 2]
            surface.blit(dot._get_surface(), dot.rect)
        return surface

    def create_area(self):
        """Create the area of the cloud (a dot object itself)."""

        self._area = Dot(radius=self._radius,
                         position=(0, 0),
                         colour=self._background_colour)
        expyriment.stimuli._stimulus.Stimulus._id_counter -= 1
        self._area._set_surface(pygame.surface.Surface(
            (self.radius * 2, self.radius * 2),
            pygame.SRCALPHA).convert_alpha())
        if self._background_colour is not None:
            pygame.draw.circle(self._area._get_surface(), self._background_colour,
                               (self._radius, self._radius), self._radius)

    def _is_overlapping_with_point(self, dot, gap):
        """Return True if a dot in the cloud is overlapping with another dot.

        Parameters
        ----------
        dot : stimuli.dot
            the other dot
        gap : int
            constant added to the distance

        Returns
        -------
        out : bool
            True if a dot in the cloud is overlapping with another dot

        """

        for elem in self._cloud:
            d = elem.distance(dot)
            if d <= (elem.radius + dot.radius + gap):
                return True
        return False

    def make(self, n_dots, dot_radius, gap=0):
        """Make the cloud by randomly putting dots on it.

        Parameters
        ----------
        n_dots : int
            number of dots to put into the cloud
        dot_radius : int
            radius of the dots
        gap : int, optional
            gap between dots (default = 0)

        """

        top_left = dot_radius - self._radius
        bottom_right = self._radius - dot_radius
        remix = 0

        while(True): #remix-loop
            self._cloud = []
            remix = remix + 1
            reps = 0
            while(True): #find a solution
                dot = Dot(radius=dot_radius)
                expyriment.stimuli._stimulus.Stimulus._id_counter -= 1
                dot.position = (random.randint(top_left, bottom_right),
                                random.randint(top_left, bottom_right))
                reps = reps + 1

                if dot.is_inside(self.area):
                    if not self._is_overlapping_with_point(dot, gap):
                        self._cloud.append(dot)
                        reps = 0
                if reps > 10000:
                    break
                if len(self._cloud) >= n_dots:
                    self.clear_surface()
                    return True

            if remix > 10:
                message = "Dotcloud make: Cannot find a solution."
                print("Warning: ", message)
                if self._logging:
                    expyriment._active_exp._event_file_log(message)
                return False

    def shuffel_dot_sequence(self, from_idx=0, to_idx= -1):
        """Shuffle the dots sequence.

        Parameters
        ----------
        from_idx : int, optional
            index to start from (default = 0)
        to_idx : int, optional
            index to end on (default = -1)

        """
        if (from_idx < 0):
            from_idx = 0
        if to_idx < from_idx or to_idx >= len(self._cloud):
            to_idx = len(self._cloud) - 1

        for x in range(from_idx, to_idx) :
            r = random.randint(from_idx, to_idx)
            self._cloud[r], self._cloud[x] = self._cloud[x], self._cloud[r]


if __name__ == "__main__":
    from expyriment import control
    control.set_develop_mode(True)
    defaults.event_logging = 0
    exp = control.initialize()
    dotcloud = DotCloud()
    dotcloud.make(25, 10)
    dotcloud.present()
    exp.clock.wait(1000)