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)
|