Exemplo n.º 1
0
    def build(self):

        # configure beamer
        self.set_arena_mode('greenHDMI')

        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(self.parent.mainNode)
        self.cylinder.setColor(self.bg_intensity/255.,self.bg_intensity/255.,self.bg_intensity/255.,1)

        self.z = self.z * 10/5.
        self.dz = self.dz * 10/5.
        if self.dz > 26.:
            self.dz = 26.
        if self.dphi > 360.:
            self.dphi = 360.


        self.ts_window = pcore.TextureStage('ts_bar')
        tex_window = tools.create_window_texture(self.dphi, self.dz, self.bar_intensity, self.bg_intensity)
        self.cylinder.clearColor()
        self.cylinder.setTexture(self.ts_window, tex_window)

        # set starting point of bar depending if it will go rightwards or leftwards
        if self.velocity > 0:
            self.phi0 = -(90. + self.dphi/2.)
        elif self.velocity <= 0:
            self.phi0 = +(90. + self.dphi/2.)

        self.cylinder.setTexOffset(self.ts_window, -0.25 + self.dphi/2./360. - self.phi0/360., 0.5 - self.dz/2./26. - self.z/26.)

        self.T = (180. + self.dphi)/math.fabs(self.velocity)
Exemplo n.º 2
0
    def build(self):

        # configure beamer
        self.set_arena_mode('greenHDMI')

        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(self.parent.mainNode)
        self.cylinder.setColor(self.bg_intensity/255.,self.bg_intensity/255.,self.bg_intensity/255.,1)

        # scale cylinder up in height in case the window texture is overlapping the edges
        self.cylinder_height = self.cylinder_height + self.dz * 2
        self.cylinder.setScale(self.cylinder_radius,self.cylinder_radius,self.cylinder_height)

        self.dz = self.dz * 10/5.
        self.velocity = self.velocity * 10/5.
        if self.dz > 26.:
            self.dz = 26.
        if self.dphi > 360.:
            self.dphi = 360.
        self.ts_window = pcore.TextureStage('ts_bar')


        tex_window = tools.create_window_texture_set_size(self.dphi, self.dz, self.bar_intensity, self.bg_intensity, 360, int(self.cylinder_height/0.1))
        self.cylinder.clearColor()
        self.cylinder.setTexture(self.ts_window, tex_window)

        # set starting point depending whether direction is downwards or upwards
        if self.velocity > 0:
            self.z0 = -(13 + self.dz/2.)
        elif self.velocity <= 0:
            self.z0 = (13 + self.dz/2.)


        self.cylinder.setTexOffset(self.ts_window, -0.25 + self.dphi/2./360. - self.phi/360., 0.5 - self.dz/2./self.cylinder_height - self.z0/self.cylinder_height)

        self.T = (26. + self.dz)/math.fabs(self.velocity)
Exemplo n.º 3
0
    def build(self):

        # configure beamer
        self.set_arena_mode('greenHDMI')

        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)
        self.cylinder.setScale(10, 10, 50)
        self.cylinder_height = 50
        self.cylinder_radius = 10

        chess_image = pcore.PNMImage(2, 2)
        chess_image.setXelA(0, 0, 0, 0, 0, 1)
        chess_image.setXelA(1, 1, 0, 0, 0, 1)
        chess_image.setXelA(0, 1, 1, 1, 1, 1)
        chess_image.setXelA(1, 0, 1, 1, 1, 1)

        chess_tex = pcore.Texture()
        chess_tex.load(chess_image)
        chess_tex.setMagfilter(pcore.Texture.FTNearest)
        N_vertical = self.cylinder_height / (
            2 * math.pi * self.cylinder_radius / self.N_horizontal
        )  #1./(2*math.pi/N_horizontal)
        self.cylinder.setTexScale(pcore.TextureStage.getDefault(),
                                  self.N_horizontal, N_vertical)
        self.cylinder.setTexture(chess_tex)

        self.v_tex_offset_count = 0
        self.h_tex_offset_count = 0

        self.fov_right_diff = [0.0, 0.0]
        self.fov_left_diff = [0.0, 0.0]

        self.T = self.duration
    def build(self):

        # configure beamer
        if self.color == 'b':
            self.set_arena_mode('optoblueHDMI')
        elif self.color == 'r':
            self.set_arena_mode('optoredHDMI')
        elif self.color == 'g':
            self.set_arena_mode('optogreenHDMI')

        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(self.parent.mainNode)
        self.cylinder.setColor(self.min_intensity/255.,self.min_intensity/255.,self.min_intensity/255.,1) # set background color

        self.min_intensity = (self.min_intensity)/255.
        self.max_intensity = (self.max_intensity)/255.
        self.bg_intensity = self.min_intensity

        # create window
        self.ts_window = pcore.TextureStage('ts_window')
        #self.ts_window.setMode(pcore.TextureStage.MBlend)


        self.worlds_share(self.cylinder) # store cylinder in shared space to change its color synchronously in all color worlds
        self.pulse_done = False
        self.pulse_end = False
Exemplo n.º 5
0
    def build(self):

        # configure beamer
        self.set_arena_mode('greenHDMI')

        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)

        # load texture
        # for horizontal gratings the edge is at the left edge of the arena (phi = 0)
        # for vertical gratings the edge is at the bottom edge of the arena (z = -6.5)
        anti_alias_texture_size = int(
            math.ceil(608. / (math.degrees(
                math.atan(self.cylinder_height / 2. / self.cylinder_radius)) *
                              2 / self.wave_length / 0.9) - 2 * 4) / 2. +
            2)  # empirically
        if self.mode == 'sin':
            # starts always at the edge of the texture with phase = 0 of the sine wave (rescaled to the range between max_intensity and min_intensity)
            self.tex = tools.create_sine_grating_texture(
                anti_alias_texture_size, self.min_intensity,
                self.max_intensity)
        elif self.mode == 'sq':
            # starts always at the edge of the texture with the black stripe
            self.tex = tools.create_grating_texture(anti_alias_texture_size,
                                                    self.min_intensity,
                                                    self.max_intensity)

        # generate wavelength by rescaling the texture ( which contains exact one period of the grating) in relation to the desired wavelength
        # for the horizontal component the scaling factor is given by the ratio between the cylinder width (360 degree) and the wavelength
        # for the vertical component the scaling factor is given by the ratio between 180 degree (because we would see the whole texture if the scaling factor would be 1)
        #       and the wavelength times the aspect ratio of the arena screen (such that 45 degree remains 45 degree)
        #
        # NOTE: Using this convention the vertical scaling factor is given by the requirement that 45 degree generates exactly 45 degree on the arena screen
        #       This scaling factor also defines how wide the bars will be in vertical direction.
        #       Hence, the wavelength is strictly speaking defined only for horizontal gratings moving along the azimuth

        self.cylinder.setTexture(self.tex)
        #self.cylinder.setTexScale(pcore.TextureStage.getDefault(), 360.0/self.wave_length, math.degrees(math.atan(self.cylinder_height/2./self.cylinder_radius))*2/self.wave_length)
        self.cylinder.setTexScale(
            pcore.TextureStage.getDefault(), 360.0 / self.wave_length,
            360.0 / self.wave_length / 2. / (10. * math.pi / 26.))
        self.cylinder.setTexRotate(pcore.TextureStage.getDefault(),
                                   -self.rotation)

        self.tex_offset = 0.
        self.cylinder.setTexOffset(pcore.TextureStage.getDefault(),
                                   (self.tex_offset + self.angle_function(0)) /
                                   self.wave_length % 1, 0)

        # set up shared variables for synchronizing all color worlds
        self.shared.rotation = self.rotation

        # set up keyboard commands for moving the window
        base.accept("arrow_up", self.increase_rot)
        base.accept("arrow_down", self.decrease_rot)
        base.accept("arrow_up-repeat", self.increase_rot)
        base.accept("arrow_down-repeat", self.decrease_rot)
Exemplo n.º 6
0
    def build(self):

        self.trigger_pixel.setColor(0, 0, 0, 1)

        # configure beamer
        self.set_arena_mode('greenHDMI')
        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)
        self.cylinder.setColor(self.intensity / 255., self.intensity / 255.,
                               self.intensity / 255., 1)
Exemplo n.º 7
0
    def build(self):  #, old_geometry):

        # configure beamer
        self.set_arena_mode('greenHDMI')

        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)

        self.T = self.motion_T * 2 + self.pause

        # load texture (same as in default.Grating)
        anti_alias_texture_size = int(
            math.ceil(608. / (math.degrees(
                math.atan(self.cylinder_height / 2. / self.cylinder_radius)) *
                              2 / self.wave_length / 0.9) - 2 * 4) / 2. +
            2)  # empirically
        if self.mode == 'sin':
            self.tex = tools.create_sine_grating_texture(
                anti_alias_texture_size, self.min_intensity,
                self.max_intensity)
        elif self.mode == 'sq':
            self.tex = tools.create_grating_texture(anti_alias_texture_size,
                                                    self.min_intensity,
                                                    self.max_intensity)

        # generate wavelength by rescaling the texture ( which contains exact one period of the grating) in relation to the desired wavelength
        # for the horizontal component the scaling factor is given by the ratio between the cylinder width (360 degree) and the wavelength
        # for the vertical component the scaling factor is given by the ratio between 180 degree (because we would see the whole texture if the scaling factor would be 1)
        #       and the wavelength times the aspect ratio of the arena screen (such that 45 degree remains 45 degree)
        #
        # NOTE: Using this convention the vertical scaling factor is given by the requirement that 45 degree generates exactly 45 degree on the arena screen
        #       This scaling factor also defines how wide the bars will be in vertical direction.
        #       Hence, the wavelength is strictly speaking defined only for horizontal gratings moving along the azimuth

        self.cylinder.setTexture(self.tex)
        #self.cylinder.setTexScale(pcore.TextureStage.getDefault(), 360.0/self.wave_length, math.degrees(math.atan(self.cylinder_height/2./self.cylinder_radius))*2/self.wave_length)
        self.cylinder.setTexScale(
            pcore.TextureStage.getDefault(), 360.0 / self.wave_length,
            360.0 / self.wave_length / 2. / (10. * math.pi / 26.))
        self.cylinder.setTexRotate(pcore.TextureStage.getDefault(),
                                   -self.rotation)
        self.velocity = -self.velocity

        self.tex_offset = 0.
        self.cylinder.setTexOffset(pcore.TextureStage.getDefault(),
                                   (self.tex_offset + self.angle_function(0)) /
                                   self.wave_length % 1, 0)

        # set up variables to memorize which triggers have been made already
        self.stop1stdirection_trigger = True
        self.start2ndirection_trigger = True
Exemplo n.º 8
0
    def build(self):

        # configure beamer
        self.set_arena_mode('greenHDMI')

        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)

        self.cylinder_height = self.cylinder_height + self.dz * 2
        self.cylinder.setScale(self.cylinder_radius, self.cylinder_radius,
                               self.cylinder_height)

        self.ts_window = pcore.TextureStage('ts_window')
        self.plain_tex = tools.create_plain_texture(self.bg_intensity)
        self.cylinder.setTexture(self.ts_window, self.plain_tex)

        self.nr_before = 0

        self.z = self.z * 10 / 5.
        self.dz = self.dz * 10 / 5.
        if self.dz > 26:
            self.dz = 26
        if self.dphi > 360.:
            self.dphi = 360.

        self.tex_windowA = tools.create_window_texture_set_size(
            self.dphi, self.dz, self.max_intensity, self.bg_intensity, 360,
            int(self.cylinder_height / 0.1))
        self.tex_windowB = tools.create_window_texture_set_size(
            self.dphi, self.dz, self.min_intensity, self.bg_intensity, 360,
            int(self.cylinder_height / 0.1))

        self.cylinder.setTexOffset(
            self.ts_window, -(-self.dphi / 2. / 360. + self.phi / 360.),
            0.5 - self.dz / 2. / self.cylinder_height -
            self.z / self.cylinder_height)

        # set up shared variables for synchronizing all color worlds
        self.shared.phi = self.phi
        self.shared.z = self.z
        self.shared.factor = 1

        # set up keyboard commands for moving the window
        base.accept("arrow_right", self.increase_phi)
        base.accept("arrow_left", self.decrease_phi)
        base.accept("arrow_up", self.increase_z)
        base.accept("arrow_down", self.decrease_z)
        base.accept("arrow_right-repeat", self.increase_phi)
        base.accept("arrow_left-repeat", self.decrease_phi)
        base.accept("arrow_up-repeat", self.increase_z)
        base.accept("arrow_down-repeat", self.decrease_z)
Exemplo n.º 9
0
    def build(self):

        # configure beamer
        self.set_arena_mode('greenHDMI')
        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)

        self.tex = tools.create_plain_texture(255)
        self.cylinder.setTexture(self.tex)
        self.cylinder.setColor(self.min_intensity, self.min_intensity,
                               self.min_intensity, 1)

        # initialize binary variable to memorize the current phase of the flicker
        self.nr_before = 0
    def build(self):

        N_z = self.N_z
        N_phi = self.N_phi
        self.N = N_z * N_phi

        # Texture: Background
        self.bg_tex_shape = (N_z, N_phi)

        # set inital condition for lowpass filter
        np.random.seed(0)
        self.frame_memory = np.random.normal(0, self.scaling_fac,
                                             self.bg_tex_shape)
        np.random.seed(0)

        # configure beamer
        self.set_arena_mode('greenHDMI')

        # generate standard cylinder
        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)
        self.cylinder.setScale(self.cylinder_radius, self.cylinder_radius,
                               self.cylinder_height * 1)

        # like in default.PDNDgrating
        self.T = self.T

        # INITIALIZE CYLINDER TEXTURE for the start frame (first N samples counting from random seed 0)
        self.framecount = 0
        np.random.seed(0)
        try:
            self.tex = self.shared.texture
        except:
            ###
            #self.shared.control_values = np.zeros( (1000,) + self.bg_tex_shape )
            ###

            self.shared.texture = self.generate_texture(0)

        # leading color world gets initialized again and jumps N random numbers (because the start frame was already generated)
        if self.first:
            np.random.seed(0)
            self.generate_texture(0)
            self.framecount = 1

        # set texture in all color worlds
        self.tex = self.shared.texture
        self.cylinder.setTexture(pcore.TextureStage.getDefault(), self.tex)
        self.cylinder.setTexScale(pcore.TextureStage.getDefault(), 2., 1.)
    def build(self):

        # configure beamer
        self.set_arena_mode('greenHDMI')


        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(self.parent.mainNode)

        # paint the cylinder black on the ground
        tex_ground = tools.create_plain_texture(0)
        self.ts_ground = pcore.TextureStage('ts_ground')
        self.cylinder.setTexture(self.ts_ground, tex_ground)

        # generate textures
        anti_alias_texture_size = int(math.ceil(608./(math.degrees(math.atan(self.cylinder_height/2./self.cylinder_radius))*2/self.wave_length/0.9) - 2*4)/2. + 2) # empirically

        # unfortunately, MAdd Texture mode does not work together with the setColorScale - command from base.py
        # this means, that those textures that are added onto another one cannot be tinted in a certain color
        # hence, here we generate differently coloured texture from the beginning, depending on the temporal offset of the Stimulus World
        if self.t_offset == 0.:
            color_vector = [0,0,1] # blue
        elif self.t_offset == 1.:
            color_vector = [1,0,0] # red
        elif self.t_offset == 2.:
            color_vector = [0,1,0] # green

        # generate two sine gratings that are anti-phasic
        # then in the beginning everything is gray and on the left edge (phi = 0) the first polarity is dark
        self.tex = tools.create_coloured_sine_grating_texture(anti_alias_texture_size, self.min_intensity, self.max_intensity, color_vector=color_vector)
        self.tex2 = tools.create_coloured_sine_grating_texture(anti_alias_texture_size, self.max_intensity, self.min_intensity, color_vector=color_vector)

        # add the two sine gratings onto each other to generate a counterphase flicker
        self.ts1 = pcore.TextureStage('ts1')
        self.ts1.setMode(pcore.TextureStage.MAdd)
        self.ts2 = pcore.TextureStage('ts2')
        self.ts2.setMode(pcore.TextureStage.MAdd)

        self.cylinder.setTexture(self.ts1, self.tex)
        self.cylinder.setTexScale(self.ts1, 360.0/self.wave_length, math.degrees(math.atan(self.cylinder_height/2./self.cylinder_radius))*2/self.wave_length)
        self.cylinder.setTexRotate(self.ts1, -self.rotation)

        self.cylinder.setTexture(self.ts2, self.tex2)
        self.cylinder.setTexScale(self.ts2, 360.0/self.wave_length, math.degrees(math.atan(self.cylinder_height/2./self.cylinder_radius))*2/self.wave_length)
        self.cylinder.setTexRotate(self.ts2, -self.rotation)

        self.tex_offset = 0.
        self.cylinder.setTexOffset(self.ts1, (self.tex_offset + self.angle_function(0))/self.wave_length % 1, 0)
        self.cylinder.setTexOffset(self.ts2, (self.tex_offset - self.angle_function(0))/self.wave_length % 1, 0)
Exemplo n.º 12
0
    def build(self):

        # configure beamer
        self.set_arena_mode('greenHDMI')

        # generate standard cylinder
        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)
        self.cylinder.setScale(self.cylinder_radius, self.cylinder_radius,
                               self.cylinder_height * 1)

        ## PRE-RENDER background texture for all steps
        self.bg_tex_shape = (self.N_z, self.N_phi, self.N_steps + 1)
        self.bg_tex_npy = np.ones(self.bg_tex_shape) * (
            self.c_bg
        )  # initialize as negative number (important to set BG color later)

        x0 = self.phi0 / self.sampling_phi  # find x,y values for the center of the first box
        y0 = 0.5 * self.N_z + self.N_z / 13.0 * self.z0

        width = self.width / self.sampling_phi  # width and height of the box
        height = self.height / self.sampling_phi

        mask_list = []
        for i in range(1, self.bg_tex_npy.shape[2]):
            n0 = np.array([
                np.sin(np.radians(self.direction)),
                np.cos(np.radians(self.direction))
            ])  #  normal vector
            yn, xn = self.step_length / self.sampling_phi * n0 * (
                i - 1) + np.array([y0, x0])  # center coordinates for n-th box

            mask = self.get_box_mask(
                self.N_phi, self.N_z, xn, yn, width / 2., height / 2.,
                self.direction)  # get pixels inside the box
            mask_list.append(
                mask)  # remember each pixel mask for each time step

        for i in range(
                1, self.bg_tex_npy.shape[2]
        ):  # first, color all pixel during times where the box is OFF with c_off
            mask = mask_list[i - 1]
            self.bg_tex_npy[mask, :i] = self.c_off
            self.bg_tex_npy[mask, i + 1:] = self.c_off

        for i in range(
                1, self.bg_tex_npy.shape[2]
        ):  # second, color all pixel during times where the box is ON with c_on
            mask = mask_list[i - 1]
            self.bg_tex_npy[mask, i] = self.c_on

        self.texture_list = [
        ]  # convert the numpy array to actual panda3D textures and store them in a list for each time step
        for i in range(0, self.bg_tex_npy.shape[2]):
            self.texture_list.append(
                self.convert_npy_to_texture(self.bg_tex_npy[:, :, i]))

        # INITIALIZE CYLINDER TEXTURE for the start frame
        self.n_before = -1
        self.tex = self.texture_list[0]
        self.cylinder.setTexture(pcore.TextureStage.getDefault(), self.tex)
        self.cylinder.setTexScale(pcore.TextureStage.getDefault(), 2., 1.)
Exemplo n.º 13
0
    def build(self):

        self.N_phi = self.N_pixels

        # configure beamer
        if self.color == 'b':
            self.set_arena_mode('optoblueHDMI')
        elif self.color == 'r':
            self.set_arena_mode('optoredHDMI')
        elif self.color == 'g':
            self.set_arena_mode('optogreenHDMI')

        # generate background cylinder
        _, _, self.bg_cylinder = tools.standard_cylinder(self.parent.mainNode)
        bg_cylinder_radius = 10
        bg_cylinder_height = 50
        self.bg_cylinder.setScale(bg_cylinder_radius, bg_cylinder_radius,
                                  bg_cylinder_height)
        phi_width = 180. / self.N_phi

        z_height = phi_width / 360. * 2 * np.pi * bg_cylinder_radius
        self.N_z = np.ceil(bg_cylinder_height / z_height)

        # take only one and the same noise for all color copys of this world!!
        try:
            self.bg_noise = self.shared.noise
        except:
            self.bg_noise = (np.random.randint(
                size=(int(self.N_phi * 2), int(self.N_z)), low=0,
                high=2)) * (self.c_on - self.c_off) + self.c_off
            self.shared.noise = self.bg_noise

        self.noise_tex = tools.make_matrix_to_texture(self.bg_noise)
        self.bg_cylinder.setTexture(self.noise_tex)

        cylinder_radius = 10
        # generate first cylinder segment
        self.obj = tools.create_cylinder_segment(self.parent.mainNode,
                                                 self.dphi)
        self.obj.setScale(cylinder_radius, cylinder_radius, self.dz * 2)
        self.obj.setHpr(90, 0, 0)

        N_phi = np.ceil(self.dphi / phi_width).astype(np.int)
        N_z = np.ceil(self.dz * 2 / z_height).astype(np.int)

        # take only one and the same noise for all color copys of this world!!
        try:
            self.obj_noise = self.shared.obj_noise
        except:
            self.obj_noise = (np.random.randint(
                size=(N_phi, N_z), low=0,
                high=2)) * (self.c_on - self.c_off) + self.c_off
            self.shared.obj_noise = self.obj_noise

        self.obj_tex = tools.make_matrix_to_texture(self.obj_noise)

        #baum_tex = loader.loadTexture("../stimuli/default/baum.jpg")
        #self.obj.setTexture(baum_tex)
        self.obj.setTexture(self.obj_tex)

        self.obj.setBin(
            "fixed",
            20)  # render object first (even before trigger_pixel! <40!)
        self.obj.setDepthTest(False)
        self.obj.setDepthWrite(False)

        # set starting point of bar depending if it will go rightwards or leftwards
        if self.velocity > 0:
            self.phi0 = -self.dphi / 2.
        elif self.velocity <= 0:
            self.phi0 = 180. + self.dphi / 2.
        self.setPos(self.phi0, self.z)

        self.T = (180. + self.dphi) / np.abs(self.velocity)
Exemplo n.º 14
0
    def build(self):

        # configure beamer
        self.set_arena_mode('greenHDMI')

        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)

        # define 4 quadrants for the rotation angle phi:
        #   a) 0 < phi < 90:
        #       standard configuration, edge starts from lower left
        #   b) 90 < phi < 180:
        #       rotate the cylinder AND flip it, edge starts from lower right
        #   c) 180 < phi < 270:
        #       rotate the cylinder, edge starts from upper right
        #   d) 270 < phi < 360:
        #       flip the cylinder, edge starts from upper left
        #
        # for each case define a new rotation angle phi_ (flip the sign if necessary)

        phi = self.rotation % 360.
        if (0. <= phi) and (phi < 90.):
            phi_ = phi % 90.
        elif (90. <= phi) and (phi < 180.):
            self.cylinder.setHpr(90, 180, 0)
            self.cylinder.setScale(self.cylinder_radius, self.cylinder_radius,
                                   -self.cylinder_height)
            self.cylinder.setAttrib(pcore.CullFaceAttrib.makeReverse())
            phi_ = 90 - (phi % 90.)
        elif (180. <= phi) and (phi < 270.):
            self.cylinder.setHpr(90, 180, 0)
            phi_ = phi % 90.
        elif (270. <= phi) and (phi < 360.):
            self.cylinder.setScale(self.cylinder_radius, self.cylinder_radius,
                                   -self.cylinder_height)
            self.cylinder.setAttrib(pcore.CullFaceAttrib.makeReverse())
            phi_ = 90 - (phi % 90.)

        # load texture
        self.wave_length = 600.  # big enough that even at 50.38 degree (when the grating is oriented along the diagonal of the arena) one phase can cover the whole screen
        anti_alias_texture_size = int(
            math.ceil(608. / (math.degrees(
                math.atan(self.cylinder_height / 2. / self.cylinder_radius)) *
                              2 / self.wave_length / 0.9) - 2 * 4) / 2. +
            2)  # empirically
        self.tex = tools.create_grating_texture(anti_alias_texture_size,
                                                self.bg_intensity,
                                                self.edge_intensity)

        # use the same scaling and rotation convention as for gratings
        self.cylinder.setTexture(self.tex)
        self.cylinder.setTexScale(
            pcore.TextureStage.getDefault(), 360.0 / self.wave_length,
            360.0 / self.wave_length / 2. / (10. * math.pi / 26.))
        #self.cylinder.setTexRotate(pcore.TextureStage.getDefault(), -self.rotation)
        self.cylinder.setTexRotate(pcore.TextureStage.getDefault(), -phi_)

        self.tex_offset = 0.0
        self.cylinder.setTexOffset(pcore.TextureStage.getDefault(),
                                   self.tex_offset, 0)

        # Define stimulus length as the time an edge travels across the whole azimuth times the ratio
        # between the length of the diagonal and the azimuthal length plus 0.5 seconds
        # In this way the stimulus is longh enough for all orientations and the start and end trigger not too close to each other
        self.max_T = 180. / math.fabs(
            self.velocity) * (math.sqrt(26.**2 + (10. * math.pi)**2) /
                              (10. * math.pi))
        self.T = self.max_T + 0.5
    def build(self):  #, old_geometry):

        # configure beamer
        self.set_arena_mode('greenHDMI')

        self.cylinder_height, self.cylinder_radius, self.cylinder = tools.standard_cylinder(
            self.parent.mainNode)

        # make cylinder bigger in case the window is overlapping the edges and needs more space
        self.cylinder_height = self.cylinder_height + self.dz * 2
        self.cylinder.setScale(self.cylinder_radius, self.cylinder_radius,
                               self.cylinder_height)

        # like in default.PDNDgrating
        self.period = self.motion_T * 2 + self.pause * 2
        self.T = 100000  # go on for eternity
        self.count_period = 0

        # load texture
        anti_alias_texture_size = int(
            math.ceil(608. / (math.degrees(
                math.atan(self.cylinder_height / 2. / self.cylinder_radius)) *
                              2 / self.wave_length / 0.9) - 2 * 4) / 2. +
            2)  # empirically
        if self.mode == 'sin':
            self.tex = tools.create_sine_grating_texture(
                anti_alias_texture_size, self.min_intensity,
                self.max_intensity)
        elif self.mode == 'sq':
            self.tex = tools.create_grating_texture(anti_alias_texture_size,
                                                    self.min_intensity,
                                                    self.max_intensity)
        self.cylinder.setTexture(self.tex)

        # scale like in the normal PDNDgrating and then scale down even more by the factor between the bigger cylinder and the "normal" cylinder of height 26.
        #factor_visible = math.degrees(math.atan(26/2./self.cylinder_radius))*2/self.wave_length * self.cylinder_height/26.
        #self.cylinder.setTexScale(pcore.TextureStage.getDefault(), 360.0/self.wave_length, factor_visible) # visible cylinder height is 26

        # generate wavelength by rescaling the texture ( which contains exact one period of the grating) in relation to the desired wavelength
        # for the horizontal component the scaling factor is given by the ratio between the cylinder width (360 degree) and the wavelength
        # for the vertical component the scaling factor is given by the ratio between 180 degree (because we would see the whole texture if the scaling factor would be 1)
        #       and the wavelength times the aspect ratio of the arena screen (such that 45 degree remains 45 degree)
        #
        # NOTE: Using this convention the vertical scaling factor is given by the requirement that 45 degree generates exactly 45 degree on the arena screen
        #       This scaling factor also defines how wide the bars will be in vertical direction.
        #       Hence, the wavelength is strictly speaking defined only for horizontal gratings moving along the azimuth

        self.cylinder.setTexture(self.tex)
        #self.cylinder.setTexScale(pcore.TextureStage.getDefault(), 360.0/self.wave_length, math.degrees(math.atan(self.cylinder_height/2./self.cylinder_radius))*2/self.wave_length)
        self.cylinder.setTexScale(
            pcore.TextureStage.getDefault(), 360.0 / self.wave_length, 360.0 /
            self.wave_length / 2. / (10. * math.pi / self.cylinder_height))

        self.cylinder.setTexRotate(pcore.TextureStage.getDefault(),
                                   -self.rotation)
        self.velocity = -self.velocity

        self.tex_offset = 0.
        self.cylinder.setTexOffset(pcore.TextureStage.getDefault(),
                                   (self.tex_offset + self.angle_function(0)) /
                                   self.wave_length % 1, 0)

        # create window
        self.z = self.z * 10 / 5.
        self.dz = self.dz * 10 / 5.
        if self.dz > 26:
            self.dz = 26
        if self.dphi > 360.:
            self.dphi = 360.

        # create black/white mask for transparent area
        self.ts_window = pcore.TextureStage('ts_window')
        self.ts_window.setMode(pcore.TextureStage.MBlend)
        tex_window = tools.create_window_texture_set_size(
            self.dphi, self.dz, 0, 255, 360, int(self.cylinder_height / 0.1))
        self.cylinder.setTexture(self.ts_window, tex_window)
        self.cylinder.setTexOffset(
            self.ts_window, -(-self.dphi / 2. / 360. + self.phi / 360.),
            0.5 - self.dz / 2. / self.cylinder_height -
            self.z / self.cylinder_height)

        # set color of the background around the window
        self.ts_window.setColor(
            pcore.Vec4(self.bg_intensity / 255., self.bg_intensity / 255.,
                       self.bg_intensity / 255., 1))

        # initialize variables to memorize phase of the stimulus
        self.stop1stdirection_trigger = True
        self.start2ndirection_trigger = True

        # set up shared variables for synchronizing all color worlds
        self.shared.phi = self.phi
        self.shared.z = self.z
        self.shared.factor = 1

        # set up keyboard commands for moving the window
        base.accept("arrow_right", self.increase_phi)
        base.accept("arrow_left", self.decrease_phi)
        base.accept("arrow_up", self.increase_z)
        base.accept("arrow_down", self.decrease_z)
        base.accept("arrow_right-repeat", self.increase_phi)
        base.accept("arrow_left-repeat", self.decrease_phi)
        base.accept("arrow_up-repeat", self.increase_z)
        base.accept("arrow_down-repeat", self.decrease_z)