File: _stimuluscloud.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 (209 lines) | stat: -rw-r--r-- 6,866 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
#!/usr/bin/env python

"""
A stimulus cloud stimulus.

This module contains a class implementing a stimulus cloud stimulus.

"""

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


import random

import pygame

import defaults
import expyriment
from expyriment.stimuli._visual import Visual


class StimulusCloud(Visual):
    """A stimulus cloud class.

    This class produces a cloud of ANY visual stimuli.
    The cloud will be of rectengular shape!

    """

    def __init__(self, size=None, position=None, background_colour=None):
        """Create a stimulus cloud.

        Parameters
        ----------
        size : (int, int), optional
            size of the cloud
        position : (int, int), optional
            position of the cloud
        background_colour : (int, int, int), optional
            colour of the background

        """

        Visual.__init__(self, position)
        self._cloud = []
        if size is not None:
            self._size = size
        else:
            size = defaults.stimuluscloud_size
            if size is None:
                try:
                    self._size = expyriment._active_exp.screen.surface.get_size()
                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.stimuluscloud_background_colour
        self._rect = None

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

    @property
    def size(self):
        """Getter for size."""
        return self._size

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

        if self.has_surface:
            raise AttributeError(
                StimulusCloud._getter_exception_message.format("size"))
        else:
            self._size = value
    @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(
                StimulusCloud._getter_exception_message.format("background_colour"))
        else:
            self._background_colour = value

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

        surface = pygame.surface.Surface(self.size,
                                        pygame.SRCALPHA).convert_alpha()
        if self.background_colour is not None:
            surface.fill(self.background_colour)
        for stim in self._cloud:
            stim.rect = pygame.Rect((0, 0), stim.surface_size)
            surface_size = surface.get_size()
            stim.rect.center = [stim.position[0] + surface_size[0] / 2,
                                - stim.position[1] + surface_size[1] / 2]
            surface.blit(stim._get_surface(), stim.rect)
        return surface

    def make(self, stimuli, min_distance=None):
        """Make the cloud by randomly putting stimuli on it.

        Notes
        -----
        If min_distance is None, the stimuli will automatically spaced to not
        overlap. This will result in a long computation!
        Set the distance manually to space them wit a minimal distance between
        centers. This will result in a way shorter computation!

        This will build surfaces for all stimuli in the cloud!

        Parameters
        ----------
        stimuli : list
            list of stimuli to put in the cloud
        min_distance : int, optional
            minimal allowed distance between stimuli

        """

        surface = pygame.surface.Surface(self.size,
                                         pygame.SRCALPHA).convert_alpha()
        surface.fill((0, 0, 0))
        self._set_surface(surface)
        remix = 0
        while(True): #remix-loop
            self._cloud = []
            remix = remix + 1
            for stimulus in stimuli:
                reps = 0
                stimulus._set_surface(stimulus._get_surface())
                while(True): #find a solution
                    stimulus.position = (random.randint(-self.size[0] / 2,
                                                        self.size[0] / 2),
                                         random.randint(-self.size[1] / 2,
                                                        self.size[1] / 2))
                    reps = reps + 1
                    if stimulus.inside_stimulus(self):
                        okay = True
                        if min_distance is None:
                            for s in self._cloud:
                                if stimulus.overlapping_with_stimulus(s)[0]:
                                    okay = False
                                    break
                        else:
                            for s in self._cloud:
                                if stimulus.distance(s) < min_distance:
                                    okay = False
                                    break
                        if okay:
                            self._cloud.append(stimulus)
                            reps = 0
                            if len(self._cloud) == len(stimuli):
                                self.clear_surface()
                                return True
                            break
                    if reps > 10000:
                        break

            if (remix > 10):
                message = "Stimuluscloud make: Cannot find a solution."
                print("Warning: ", message)
                return False

    def shuffel_surface_sequence(self, from_idx=0, to_idx= -1):
        """Shuffle the surfaces 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.stimuli._textline import TextLine
    from expyriment import control
    control.set_develop_mode(True)
    defaults.event_logging = 0
    exp = control.initialize()
    stimuluscloud = StimulusCloud()
    stims = []
    for i in range(0, 25):
        stims.append(TextLine("A"))
    stimuluscloud.make(stims)
    stimuluscloud.present()
    exp.clock.wait(1000)