Shady Package

The World Class

class Shady.World(width=None, height=None, left=None, top=None, screen=None, threaded=True, canvas=False, frame=False, fullScreenMode=None, visible=True, openglContextVersion=None, legacy=None, backend=None, acceleration=None, debugTiming=False, profile=False, syncType=-1, logfile=None, reportVersions=False, window=None, **kwargs)

Bases: LinkGL

A World instance encapsulates the window and rendering environment in which you draw stimuli. By default, a World will fill one screen, but its size, offset and decoration can also be tailored explicitly if necessary. When you initialize a World, Shady creates an OpenGL program and compiles and links a vertex shader and a fragment shader to it: this is what allows signal generation, contrast modulation, windowing, gamma correction and dithering to be performed on the graphics processor.

Once you have created the World, you will probably want to call need to call the Stimulus() method one or more times, to configure the things that should be drawn in it.

Parameters:
  • width (int) – width of drawable area in “screen coordinates” (which usually means pixels, but see the note below).

  • height (int) – height of drawable area in “screen coordinates” (which usually means pixels, but see the note below).

  • size (int, tuple or list) – width and height of drawable area in “screen coordinates” (see note below). If this is a single number, it is used for both width and height. If it is a tuple or list, it is interpreted as [width, height]. However, the separate width and/or height arguments take precedence, if supplied.

  • left (int) – horizontal offset from the edge of the screen, in “screen coordinates” (which usually means pixels, but see the note below).

  • top (int) – vertical offset from the top of the screen, in “screen coordinates” (which usually means pixels, but see the note below).

  • screen (int) – Screen number. 0 or None (default) means use whichever is designated as the primary screen. A positive integer explicitly selects a screen number. The output of the global Screens() function may help you choose the screen number you want.

  • threaded (bool) – If you specify threaded=False, the World’s main rendering/event- processing loop will not be started automatically: you will have to start it yourself, from the appropriate thread, using the Run() method. In this case, the best way to perform initial World configuration and Stimulus creation is to put the code in the body of a Prepare() method that you specify by subclassing World.

    With threaded=True, a new thread will be created to perform all the work of World construction and then, automatically, to run the main loop. Any subsequent call to the Run() method will do nothing except sleep until the thread ends. This is the easiest way to use Shady: you can then create and manipulate stimuli either from a Prepare method, or from wherever else you want. This appears to work well on Windows, but will have problems (which can only partially be worked-around) on other operating systems: see the Shady.Documentation.Concurrency docstring or click here.

  • canvas (bool) – If you set this to True a “canvas” Stimulus instance will be created automatically, filling the screen behind other stimuli. If you do not create one automatically like this, you can do it later by calling the MakeCanvas() method. A canvas allows gamma-correction and dynamic-range enhancement tricks to be performed on the backdrop as well as on your foreground stimuli, and it allows the World’s “atmosphere” properties (backgroundColor, gamma, noiseAmplitude and friends) to take effect: see the Shady.Documentation.PreciseControlOfLuminance docstring or click here.

  • frame (bool) – Whether or not to draw a frame and title-/drag- bar around the window.

  • fullScreenMode (bool) – Default behavior is to create a window that exactly covers one screen. This can be done by creating an ordinary window that just happens to be the same size as the screen (fullScreenMode=False) or by actually asking the system to change to full-screen mode (fullScreenMode=True). The default on Windows is False, since it allows you to switch windows (e.g. with alt-Tab) and still have the Shady window visible in the background (note however, that background windows have poor timing precision - to render precisely without skipping frames you will need to keep the window in the foreground). With fullScreenMode=True this is impossible: the window will disappear when it loses focus. On the Mac, the default setting for full-sized windows is fullScreenMode=True, because this seems to be the only way to hide the menu bar at the top of the screen.

    For non-full-sized windows, the default is fullScreenMode=False. If you set it to True while also explicitly designating the size of the World, the OS will attempt to change resolution. This will probably be a bad idea for psychophysical applications on most modern screens: for rendering accuracy, you should address the screen at its native (maximum) resolution.

    (Experimental feature, only available when using the ShaDyLib accelerator:) you can also specify a number greater than 1 for fullScreenMode, in which case Shady will try to use this as the refresh rate for the screen.

  • visible (bool) – If you set this to False, the window will be created off-screen and will only become visible when you set its visible property to True.

  • debugTiming (bool) – Every World records the timing intervals between its frame callbacks, to aid in analyzing timing performance. If you set debugTiming=True, it will record additional information that breaks down the allocation of this time. By default the setting will be propagated to every Stimulus instance as well (set each stim.debugTiming=False if this is not what you want—if there are many stimuli, then the timing debug calls themselves can start to have a measurable impact on performance).

  • logfile (str) – Optionally specify the name of a text file which will log various pieces of useful diagnostic information. If your filename includes the substring {}, this will be replaced by a yyyymmdd-HHMMSS local timestamp. If the file stem ends with -full then self.logger.logSystemInfoOnClose will be set to True by default which means that a third-party program will be run when the World closes, to record extensive system information (NB: on Windows the program is dxdiag.exe which is time-consuming and produces lengthy logs). You can write to the log file yourself with self.logger.Log()

  • reportVersions (bool) – If this is True, the World instance will call its ReportVersions() method to report version information to the console, as soon as it is set up.

  • **kwargs – Managed property values can also be specified as optional keyword arguments during construction, for example:

    w = World( ..., clearColor=[0,0,0.5], ... )
    

Note: The easiest way to create a World is by omitting all geometry arguments. Then it will fill the display screen (the primary screen by default, but you can also specify the screen number explicitly). However, if you choose to use explicit geometry arguments (width, height, size, left, top) note that they are all in “screen coordinates”. Screen coordinates usually correspond to pixels, but in some systems (Macs with Retina screens) you may need to specify some fixed smaller proportion of the number of addressable pixels you actually want: for example, on a Late-2013 Macbook with 13-inch Retina screen, w = World(1280, 800) opens a window that actually has double that number of addressable pixels (2560 x 1600). After construction, w.size will indicate the correct (larger) number of pixels. Unfortunately, before construction, I have not yet found a general way of predicting the relationship between screen coordinates and pixels.

classmethod AddCustomUniform(name=None, defaultValue=None, **kwargs)

Modifies the class (World or Stimulus) so that it possesses one or more new managed properties, whose values are then accessible from inside the fragment shader. This must be performed before World construction.

Example:

Shady.World.AddCustomUniform( 'spam', [1,2,3] )
Shady.Stimulus.AddCustomUniform( eggs=4, beans=[5,6] )

Either syntax can be used in either class. The keyword-argument syntax has the advantage of being able to define multiple new properties in one call.

The default values you supply dictate whether the new property is 1-, 2-, 3- or 4-dimensional. For a 1-dimensional property, the type of your default value also determines whether the property gets defined as an integer or floating-point variable. (2-, 3- or 4- dimensional properties are always re-cast as floating-point).

The corresponding uniform variables are then automatically made available in the fragment shader code, with the first letter of the property name getting capitalized and a ‘u’ prepended. So, as a consequence of the two lines in the example above, the modified shader would then contain these definitions:

uniform vec3 uSpam;
uniform int uEggs;
uniform vec2 uBeans;

…all of which is useless unless you actually write some custom shader functions that access the new variables. You might use the new variables in your own custom signal-function, modulation-function, windowing-function or color-transformation snippets.

AddForeignStimulus(stimulus, name=None, z=0, **kwargs)

stim is either a class (or callable factory function) or an instance of a class. That class should have a draw() method. It is drawn on every frame with the Shady shader pipeline disabled (so, no automatic linearization or dithering, etc).

The intention is to allow the use of one’s own custom OpenGL primitives in drawing unusual stimuli.

AfterClose(func, *pargs, **kwargs)

This method registers a callable func (and optionally the *pargs and **kwargs that should be passed to it) that will be called just after the World closes. The method is otherwise identical to BeforeClose()

AnimationCallback(func=None)

Decorator version of SetAnimationCallback()

Examples:

w = Shady.World()

@w.AnimationCallback
def anim( self, t ):
        print( t )
BeforeClose(func, *pargs, **kwargs)

This method registers a callable func (and optionally the *pargs and **kwargs that should be passed to it) that will be called just before the World closes. One way to use this method is as a decorator—for example:

w = Shady.World()
@w.BeforeClose
def Goodbye():
        print( "goodbye" )
Returns:

(1) when func is called, its return value will be inserted into this list; (2) the id of the list instance uniquely identifies the callback you have just registered, so it can be used as a handle for cancelling the function with CancelOnClose()

Return type:

an empty list. This has two uses

BoundingBox(worldCoordinates=False)

This method returns the World’s bounding box, in pixels.

Parameters:

worldCoordinates (bool) – If True, then the left and bottom coordinates are computed relative to the World’s own anchor (so that gives you the bounding box within which you can draw stimuli, in coordinates a Stimulus would understand). If False, then left = bottom = 0.

Returns:

[left, bottom], [width, height] pixel coordinates for the World.

CancelOnClose(container)

Cancels a callback that had previously been scheduled to run at closing time by either BeforeClose() or AfterClose(), either of which will have returned the container that you need to pass as the input argument here.

Capture(pil=False, fullscreen=False, saveas='', size=None, origin=None, normalize='auto')

Takes a screenshot of the World and return the RGBA image data as a numpy array (pil=False) or as a PIL.Image.Image instance (pil=True).

Parameters:
  • pil (bool) – If True, and PIL or pillow is installed, an Image object is returned. Otherwise, return a numpy array (if numpy is installed) or a buffer containing the raw pixel information (if not).

  • fullscreen (bool) – Normally, with fullscreen=False, we capture just the World content. But if we specify fullscreen=True, and PIL or pillow is installed, and we’re on Windows, then the ImageGrab module will be used to grab a shot of the whole screen as-is (including other windows and desktop content if the World fills only part of the screen or is partly obscured).

  • saveas (str) – If a filename is specified here, and PIL or pillow is installed, then the image is automatically saved under the specified filename.

  • size (tuple, list) – This is a sequence (tuple, list, or 1-D numpy array) containing 2 integers: width and height, in pixels. If unspecified (None), the size of the World (self.size) is assumed.

  • origin (tuple, list) – This is a sequence (tuple, list, or 1-D numpy array) containing 2 integers: x and y, in pixels, indicating the offset in pixels between the lower left corner of the World and the lower left corner of the capture are. If unspecified (None), [0,0] is assumed.

  • normalize (bool or ‘auto’) – If False, return raw RGBA values as integers. If True, return floating-point values normalized in the range 0 to 1, and furthermore undo the effects of the current bitCombiningMode if any. If 'auto', the default is False except when all the following conditions are met: numpy is installed, pil is False, and self.bitCombiningMode is non-zero.

Returns:

A PIL.Image.Image object (with pil=True, provided PIL or pillow is installed) or a numpy array (with pil=False) containing 8-bit RGBA pixel values.

ClearDynamics()

Remove all property dynamics from the instance.

See also: GetDynamic(), GetDynamics(), SetDynamic()

Close()

Close the World.

CreatePropertyArray(propertyName, *stimuli)

This method returns a PropertyArray object which contains a numpy array. Each row of the array is the storage area for the named property of one of the specified stimuli. You can still address the property of each individual Stimulus in the usual way, but now you also have the option of addressing them all at once in a single array operation, which may be much more efficient if there are many stimuli.

Parameters:
  • propertyName (str) – The name (or alias) of a fully-fledge ManagedProperty of the Stimulus class (for example, 'color' or 'position').

  • *stimuli – This is flexible. You can pass the (string) names of Stimulus instances, or you can pass the instances themselves. You can pass them as separate arguments, and/or in tuples or lists. If you’re using names, you can even pass them as a single space-delimited string if you want.

Returns:

a PropertyArray instance whose A attribute contains the numpy array.

Example:

import numpy, Shady
w = Shady.World()
p = numpy.linspace( -1.0, +1.0, 20, endpoint=True )
stims = [ w.Stimulus( xy=w.Place( p_i, 0 ), bgalpha=0, pp=1 ) for p_i in p ]
position = w.CreatePropertyArray( 'xy', stims )
color = w.CreatePropertyArray( 'color', stims )
@w.AnimationCallback
def Animate( self, t ):
        s = Shady.Sinusoid( t, p * 180 )
        position.A[ :, 1 ] = 200 * s
        color.A[ :, 0 ] = 0.5 + 0.5 * s
        color.A[ :, 1 ] = 0.5 + 0.5 * s[ ::-1 ]
        color.A[ :, 2 ] = s ** 2
# one call with a few simple efficient numpy array operations
# instead of 20 stimuli x 4 shortcuts = 80 dynamic function calls
Culling(enable, alphaThreshold=0.25)

Depth culling is disabled by default. This means that where two stimuli overlap, every pixel is rendered in both stimuli. Depending on your stimulus arrangement, this may be a significant waste of resources.

Turn culling on with Culling( True ). Instead of drawing all pixels of all stimuli in furthest-to-nearest order (the painter’s algorithm), Shady will now draw them in reverse painter’s order, nearest-to-furthest, omitting calculations that would affect already-drawn-to pixels.

Depth culling is fine if all your stimuli are opaque. The disadvantage to depth culling is that it affects alpha blending and composition: when a stimulus with some transparent parts is overlapped on another stimulus, depth culling alone would cause the clearColor of the World to be drawn where there should be transparency. A partial countermeasure to this is alpha culling, which will be turned on concurrently with the depth test unless you explicitly specify a negative alphaThreshold. With alpha culling, any pixel in any stimulus whose alpha is equal to or less than alphaThreshold is simply omitted, and stimuli behind it will show through at that point even with depth culling enabled. The quality of some stimuli (e.g. antialiased text on a transparent background) will vary visibly depending on the threshold you choose, and will never be perfect. Note also that semi-transparent pixels (where alphaThreshold < alpha < 1.0) may be rendered with inaccurate colors: stimulus colors get alpha-blended with the clearColor, not with the colors of the stimuli behind them.

Use Culling( False ) to disable both depth and alpha culling.

Defer(func, *pargs, **kwargs)

Any method that actually makes GL calls, such as NewPage() or LoadTexture(), must be called from the same thread/context as the one where all other GL operations happen, otherwise you may fail to see any result and/or the program may crash. This is a workaround: if you call (for example) self.Defer( func, arg1, arg2, ... ) then func(arg1, arg2, ...) will be called at the end of the next frame.

This function is already used, under-the-hood, by the @DeferredCall method decorator, to make methods like World.Stimulus or Stimulus.NewPage safe. So there may be relatively few cases in which you need to use it directly.

Parameters:
  • func – this is the callable object to be called at the end of the next frame. Optionally, you may influence the order in which these pending tasks are performed by supplying a tuple (priority, func) instead of just func. The numeric value priority defaults to 0. The higher its value, the earlier the task will be performed, relative to other pending tasks.

  • *pargs and **kwargs – any additional positional arguments, and any keyword arguments, are simply passed through to func when it is called.

Returns:

An empty list. When func is finally called, its output will be inserted into this list. If func causes an exception to be raised, the stack trace information will be wrapped in a DeferredException instance and returned in the list. The list also serves as a handle by which you can Undefer() a deferred task.

EventHandler(slot=0)

Decorator version of SetEventHandler()

Examples:

w = Shady.World()

@w.EventHandler    # overwrites default slot=0
def handler( self, event ):
        print( 'handler got %s event' % event.type )

@w.EventHandler( slot=1 )   # uses an explicit slot
def handler2( self, event ):
        print( 'handler2 got %s event' % event.type )
GetDynamic(name)

For dynamic properties, return the actual callable object that generates property values, rather than the current static value.

Parameters:

name (str) – Name of the property

Returns:

Callable object responsible for generating values for the named property (or None if there is no such dynamic).

See also: GetDynamics(), SetDynamic(), ClearDynamics()

GetDynamics()

Get an ordered list of (name, callable) tuples detailing all the dynamics of this instance.

See also: GetDynamic(), SetDynamic(), ClearDynamics()

HandleEvent(event)

This method is called every time an event happens. Its argument event is a standardized instance of class Event, containing details of what happened. The default (superclass) implementation responds when event.type is 'key_release' and the released key is either the Q key or the escape key - this causes the window to close (and hence the drawing thread will terminate, if this World is threaded).

You can overshadow this method in your World subclass, or you can replace it on an instance-by-instance basis using the method SetEventHandler() or the decorator EventHandler()- for example:

def handler( self, event ):
    print( event )
w.SetEventHandler( handler )

Note that, for each event, it is possible to have more than one handler, occupying different “slots” in the cascade that is applied to each event. By default, the HandleEvent method occupies slot 0.

Inherit(other)

Set the values of all managed properties of this instance to match those of another instance. Only properties common to both objects will be considered. Dynamic property values will be copied as dynamic properties.

Parameters:

other – instance whose property values will be copied to the properties of self

Returns:

self

LinkPropertiesWithMaster(master, *pargs, **kwargs)

See ShareProperties()

Returns:

self

LookupTable(*pargs, **kwargs)

Creates a LookupTable instance using LookupTable( world=self, ... )

MakeCanvas(**kwargs)

Create a canvas stimulus that covers the World and thereby allows the World’s backgroundColor, gamma, ‘.ditheringDenominator` and other “atmosphere” parameters to be put into effect.

This is called automatically if you say canvas=True in your World constructor call.

MakePropertiesIndependent(*pargs, **kwargs)

Undoes the effect of ShareProperties(). Optionally, set the values of the properties after unsharing.

Example:

a.ShareProperties( b, c, d, alpha=1, beta=2, gamma=3 )
b.MakePropertiesIndependent( alpha=4 )
# Now `a` and `b` share properties `.beta` and `.gamma`, but
# `b.alpha` is independent and already has the new value 4;
# by contrast `a`, `c` and `d` still share all three properties.

c.MakePropertiesIndependent( 'beta', 'gamma' )
# Now `c` does not share anything except `.alpha`, although the
# property values themselves have not yet been changed.

c.Set( beta=c, gamma=c )
# a syntactic shorthand for the same operation as in the previous
# line
Returns:

self

Patch(**kwargs)

A convenience wrapper around the Stimulus() method, for creating patches of solid color (supply pp=1 for oval, pp=-1 for rectangular).

Place(xp, yp=None, worldCoordinates=True, polar=False)

Convert 2-D normalized coordinates (relative to the instance, -1 to +1 in each dimension), into 2-D pixel coordinates, either relative to the World’s current anchor, or relative to the World’s bottom left corner irrespective of anchor.

Input coordinates may be given as one scalar argument, two scalar arguments, or one argument that is a sequence of two numbers. Depending on the polar argument, these will be interpreted as x, y Cartesian coordinates (where, if y omitted, it defaults to y=x) or theta, r polar coordinates (where, if r is omitted, it defaults to r=1).

Parameters:
  • worldCoordinates (bool) – If True, return pixel coordinates relative to the World’s own anchor position. If False, return pixel coordinates relative to the World’s bottom left corner irrespective of its anchor.

  • polar (bool) – If True, input coordinates are interpreted as an angle (in degrees) and an optional radius (0 denoting the center, 1 denoting the edge).

Examples:

instance.Place( [ -1, 1 ] )      # top left corner
instance.Place( -1, 1 )          # likewise, top left corner
instance.Place( 90, polar=True )  # middle of top edge (radius 1 assumed)
instance.Place( [ 90, 0.5 ], polar=True )  # halfway between center and top
instance.Place( 90, 0.5, polar=True )  # likewise, halfway between center and top
Prepare(**kwargs)

This method is called after the World is initialized and the shader programs are set up, but before the first frame is actually rendered. It is carried out in the same thread as initialization and rendering.

In the World base class, this method does nothing. However, if you create a subclass of World, you can overshadow this method. It is then a good place to perform initial Stimulus creation and other configuration specific to your particular application. You can use any prototype you like for the method, as long as it has a self argument.

When you first construct the World, any keyword arguments that are not recognized by the constructor will be automatically passed through to Prepare().

classmethod Properties(includeShortcuts=False)

Return a list of all managed properties of this class. (Class method)

Parameters:

includeShortcuts – Determines whether ManagedShortcut instances should be included (True) or not (False; default) along with ManagedProperty instances.

Returns:

A list of ManagedProperty (and optionally also ManagedShortcut) descriptor instances.

ReportVersions(outputType='print', importAll=False)

This method calls the global function ReportVersions with the World instance as its first argument.

Run()

If the World was created with threaded=True and is already rendering, then this method does nothing except sleep in the current thread until the World has finished. If the World was not created threaded, then this method is required to actually execute its main rendering loop. Either way, the method does not return until the World closes.

RunDeferred(func, *pargs, **kwargs)

Defer() a function, then wait for it to run, and then return the output. NB: you’ll wait forever if the World is not running…

Note that you do not need to do this for most World methods: they have already been wrapped so that they run deferred when necessary and appropriate.

Set(**kwargs)

Set the values of multiple managed properties in one call. An error will be raised if you try to set the value of a non-existent, or non-managed, property.

Returns:

self

Example:

instance.Set( rotation=90, color=(0, 0, 1),  contrast=0.5 )
SetAnimationCallback(callback)

Bind the callable object callback as the instance’s animation callback. Each object, whether it is a World or a Stimulus, may optionally have a single animation callback which is called on every frame. If the callback argument is None, any existing callback is removed.

The animation callback is installed as the attribute self.Animate. By default, this attribute is None.

The prototype for an animation callback can be callback(self, t) or just callback(t) (if it’s the latter then you can, alternatively, simply assign it as self.Animate = callback ).

Example:

def animate( self, t ):
        print( t )
my_world = Shady.World().SetAnimationCallback( animate )

There is also a decorator version of the same operation, simply called AnimationCallback():

stim = my_world.Stimulus()
@stim.AnimationCallback
def Animate( self, t ):
        print( t )
SetBitCombiningMode(mode, verticalGrouping='iso')

This method supports high-dynamic-range rendering on specialized vision-science hardware—see documentation for the bitCombiningMode property for full details. Calling w.SetBitCombiningMode(mode) is the same as simply assigning w.bitCombiningMode = mode. The only difference is that, when mode=2 (sacrificing horizontal resolution to achieve 16-bit-per- channel full-color rendering), the method allows you to specify separately whether vertical resolution should also be sacrificed. Shady’s default behavior is to throw away vertical resolution at the same time, so that logical pixels remain physically square, as follows:

w.SetBitCombiningMode( mode=2, verticalGrouping=2 )

However you can override this, changing the aspect ratio of your pixels and retaining full vertical resolution, as follows:

w.SetBitCombiningMode( mode=2, verticalGrouping=1 )
classmethod SetDefault(**kwargs)

Affects the class. Sets the default values of named properties. In instances of the class created subsequently, managed properties will have the new default values.

Example:

cls.SetDefault( color=(1,0,0), rotation=90 )
SetDynamic(name, func, order=-1, canonicalized=False)

Associate a “dynamic” (i.e. a function that can be called repeatedly to set an attribute) with the name of an attribute.

For example:

foo.Set( 'bar',  lambda t: t ** 2 )

This will set foo.bar to t ** 2 every time the method foo._RunDynamics( t ) is called (this call will happen automatically from the API user’s point of view, in the object’s infrastructure implementation).

A dynamic can be attached to any attribute name. If the bar attribute happens to be a ManagedProperty or ManagedShortcut, then it will also automatically support “dynamic value assignment”, i.e. you can do:

foo.bar = lambda t: t ** 2

as a syntactic shorthand for SetDynamic()—the setter will detect the fact that the value is callable, and divert it to the register of dynamics rather than assigning it directly (so the actual static value you get from querying foo.bar will not immediately change).

Parameters:
  • name (str) – name of the attribute

  • func – callable object, or None to remove any dynamic that may already be associated with name

  • order (int, float) – optional numeric ordering key that allows you to control the serial order in which dynamics are evaluated

  • canonicalized (bool) – for internal use only. ManagedProperty and ManagedShortcut descriptors can have multiple aliases. The default settings, canonicalized=False says that name hsa not necessarily been translated to the descriptor’s canonical name, so this should be attempted

Returns:

self

See also: GetDynamic(), GetDynamics(), ClearDynamics()

SetEventHandler(handler, slot=0)

Bind the callable handler as part of the instance’s cascade of event handlers. The slot argument determines the serial order in which handlers are run on each event (negative numbers before positive). If handler is None, any handler currently occupying the specified slot is removed.

By default, the class’s HandleEvent() method is registered in slot 0.

The prototype for an event handler can be handler(self, event) or just handler(event). If the handler returns anything that evaluates to True in the boolean sense, that’s the signal to abort the cascade of handlers for the event in question (i.e. skip the handlers in higher-numbered slots, this time around).

Example:

def handler( self, event ):
        print( event )
w = Shady.World().SetEventHandler( handler, slot=-1 )

There is also a decorator version of the same operation, simply called EventHandler():

w = Shady.World()
@w.EventHandler( slot=-1 )
def handler( self, event ):
        print( event )
SetLUT(value)

Sets or unsets a LookupTable for a World or Stimulus. (For World`s, this will only be effective to the extent that the `World’s atmosphere properties are shared with Stimulus instances—for example, the canvas.)

Calling this method is the functional equivalent of setting the lut property:

stim.SetLUT( value )  # These are equivalent
stim.lut = value      #

Setting a look-up table disables automatic linearization via gamma and automatic dynamic-range enhancement via ditheringDenominator, and allows you to take direct control of these issues (although only for one dimension of luminance per pixel: using a look-up table is a form of indexed-color rendering).

See the Shady.Documentation.PreciseControlOfLuminance docstring or Gamma Correction, Dynamic Range Enhancement, and the Canvas for more details.

Parameters:

value (None, str, list, numpy.ndarray, LookupTable) – A pre-existing LookupTable instance may be used here. Alternatively, any valid constructor argument for a LookupTable may be used, and the instance will be constructed implicitly. That means you can use:

  • a numpy.ndarray of integers in n-by-3 or m-by-n-by-3 arrangement, or

  • a list of lists that can be implicitly converted into such an array by numpy, or

  • the filename of a npy or npz file that contains such an array (under the variable name lut in the latter case) to be loaded by Shady.Linearization.LoadLUT(), or

  • the filename of a png file containing the look-up table entries as RGB pixel values in column-first order, again to be loaded by Shady.Linearization.LoadLUT().

Finally you have the option of setting None, to disable the usage of look-up tables.

Returns:

A LookupTable instance.

SetSwapInterval(value)

By default, Shady attempts to update the screen on every physical frame the display hardware can produce. This corresponds to a “swap interval” of 1 and on a typical modern LCD display this usually means 60 frames per second. If you set the swap interval to 2, updates will happen on every second frame (hence typically 30 fps) allowing you more time to perform computations between frames.

To synchronize frames with the hardware at all, Shady relies on the “vertical synch” option which may have to be enabled explicitly in the control panel of your graphics card driver. Without synchronization, you may observe “tearing” artifacts.

The ability to change the swap interval is hit-and-miss, and back-end- and platform-dependent. This call may have no effect.

ShareProperties(*pargs, **kwargs)

Share the underlying array storage of the named managed properties with other objects, so that a change to one affects all. Optionally, set the values at the same time. Calling syntax is flexible - the following examples all take certain property arrays associated with instance a and share them with instances b, c and d:

a.ShareProperties( 'gamma noiseAmplitude backgroundColor', b, c, d )
a.ShareProperties( ['gamma', 'noiseAmplitude', 'backgroundColor'], [b, c], d )
a.ShareProperties( b, c, d,  gamma=-1, noiseAmplitude=0.01, backgroundColor=0.5 ) # sets values at the same time

A similar effect can be obtained via b.LinkPropertiesWithMaster( a, ... )—although there you are limited to one sharee at a time—and a syntactic shorthand for this is to pretend you are assigning the master object instance itself to the target property:

b.gamma = a

Undo with MakePropertiesIndependent() (or b.gamma = b)

Returns:

self

Sine(**kwargs)

A convenience wrapper around the Stimulus() method, for creating a functionally-generated sine-wave patch, with the linearization and dynamic-range enhancement parameters (“atmosphere” parameters) yoked to those of the World by default.

Stimulus(*pargs, **kwargs)

Creates a Stimulus instance using Stimulus( world=self, ... )

Tick()

Wait until after the next frame has been rendered. (Will have no effect, and return immediately, if your are calling this from the same thread in which your World runs.)

Undefer(target_container)

Cancels a pending task that has been scheduled by Defer() `to run at the end of the next frame.  Its input argument is the output argument of `Defer().

Wait()

Wait until after the next frame has been rendered. (Will have no effect, and return immediately, if your are calling this from the same thread in which your World runs.)

WaitFor(condition)

This function blocks the current thread until a specified condition becomes True. The condition will be checked repeatedly in between 1-millisecond sleeps. It can be one of two things:

  • a callable (function) which returns True to signal that the wait is over - e.g.:

    stim.WaitFor( lambda: stim.y < 0 )
    
  • a string that names a dynamic attribute belonging to the current instance. In this case, the wait ends when there is no longer a dynamic attached the the property or shortcut in question. A dynamic may be removed due to explicit action of another thread in your program, or explicit action in this instance’s AnimationCallback (or indeed any instance’s AnimationCallback). Alternatively, a dynamic may automatically remove itself, by raising a StopIteration exception (the Function object returned by Shady.Dynamics.Transition is an example of something that does this):

    stim.scaling = Shady.Transition( stim.scaling, 2, duration=5 )
    # the stimulus will now double in size over the course of 5 sec
    stim.WaitFor( 'scaling' )
    # wait until it's finished
    

All of this assumes that you are operating in a different thread from the World—if you call WaitFor from inside one of the World or Stimulus callbacks, then it will simply sleep indefinitely because nothing will get the chance to change.

anchor

This is a managed property. It is a pair of numbers specifying where, in normalized coordinates within the rendering area of the window, pixel coordinate (0,0) should be considered to be for Stimulus positioning. An origin of [-1,-1] means the bottom left corner; [0,0] means the center; [+1,+1] means the top right corner. Translations resulting from a change in anchor are automatically rounded down to an integer number of pixels, to avoid anchor becoming an unexpected source of interpolation artifacts.

  • Default value: [0.0, 0.0]

  • Canonical name: anchor

  • Other aliases: origin

  • Subscripting shortcuts:
anchor_x

anchor_x is an alias for ax_n

anchor_y

anchor_y is an alias for ay_n

property atmosphere

This property actually encompasses multiple managed properties, all related to linearization and dynamic-range enhancement.

If you query this property, you will get a dict of the relevant property names and values. You can also assign such a dictionary to it.

More usefully, you can use it to link all the properties between instances in one go:

stim.atmosphere = world   # hard-links all the "atmosphere" properties
                          # at once

For more information, see the Shady.Documentation.PreciseControlOfLuminance docstring or click here.

ax_n

This is a managed shortcut. It is a shortcut for anchor[0]

ay_n

This is a managed shortcut. It is a shortcut for anchor[1]

backgroundColor

This is a managed property. It is a triplet of numbers, each in the range 0 to 1, corresponding to red, green and blue channels. It specifies the backgroundColor of the background canvas Stimulus, if present (the World needs to be constructed with the canvas=True, or you otherwise need to call MakeCanvas() ). When a canvas is created, this property of the World is automatically linked to the corresponding property of the canvas Stimulus instance; if there is no canvas then this property is unused, although you may wish to link it explicitly to other stimuli with ShareProperties() or LinkPropertiesWithMaster().

bg

bg is an alias for backgroundColor

bgblue

This is a managed shortcut. It is a shortcut for backgroundColor[2]

bgcolor

bgcolor is an alias for backgroundColor

bggreen

This is a managed shortcut. It is a shortcut for backgroundColor[1]

bgred

This is a managed shortcut. It is a shortcut for backgroundColor[0]

property bitCombiningMode

This non-managed property allows high-dynamic-range rendering on specialized vision-science hardware. It can be set to:

0, or equivalently 'C24':

denotes standard 24-bit full-color mode (8 bits per channel, with dithering by default).

1, or equivalently 'M16’ or 'monoPlusPlus':

denotes 16-bit monochrome reinterpretation of frame-buffer contents (each pixel’s red channel is the more-significant byte, and its green channel is the less-signficiant byte, of a 16 bit value). As with look-up tables, Shady reads just the red channel to determine the monochrome target value. Dithering is disabled.

2, or equivalently 'C48' or 'colorPlusPlus':

denotes 48-bit color mode in which horizontal resolution is sacrificed. Dithering is disabled. Each pixel in the frame buffer is paired with its horizontal neighbor, and together they specify a 16-bit-per-channel value for the corresponding yoked pair of physical pixels. Shady also throws away vertical resolution at the same time, so that you can continue working with square pixels and sane geometry—if you do not want this, then the SetBitCombiningMode() method allows more control.

Values 3 and 4 are also reserved for debugging C48 mode (they will not generate correct C48 stimuli).

blue

This is a managed shortcut. It is a shortcut for clearColor[2]

bluegamma

This is a managed shortcut. It is a shortcut for gamma[2]

bluenoise

This is a managed shortcut. It is a shortcut for noiseAmplitude[2]

clearColor

This is a managed property. It is a triplet of numbers in the range 0 to 1. It specifies the color of the empty screen. Note that these values are never linearized or dithered. For more precise control over the background, construct your World with the argument canvas=True and then you can manipulate backgroundColor, gamma, ‘.ditheringDenominator’ and noiseAmplitude.

dd

dd is an alias for ditheringDenominator

ditheringDenominator

This is a managed property. It is a floating-point number. It should be 0 or negative to disable dithering, or otherwise equal to the maximum DAC value (255 for most video cards). It specifies the ditheringDenominator for the background canvas Stimulus, if present (the World needs to be constructed with the canvas=True, or you otherwise need to call MakeCanvas() ). When a canvas is created, this property of the World is automatically linked to the corresponding property of the canvas Stimulus instance; if there is no canvas then this property is unused, although you may wish to link it explicitly to other stimuli with ShareProperties() or LinkPropertiesWithMaster().

  • Default value: dithering enabled (value determined automatically)

  • Canonical name: ditheringDenominator

  • Other aliases: dd

property fakeFrameRate

By default, with fakeFrameRate equal to None, a World will try to update itself at the frame rate of the display hardware (though of couse it may end up being slower if you are doing too much computation between frames, or if there are other tasks that are using too many CPU or GPU resources). The time argument t that gets passed into animation callbacks and dynamic property evaluations will reflect the real wall-time at which each frame is drawn.

However, if you set fakeFrameRate to a positive value, now frames can take as long as they like in real time, and the t argument will reflect the theoretical amount of time passed based on the number of frames completed, assuming the specified frame rate. This allows you to run animations in slower-than-real time. One of the main applications would be for capturing stills or movies of World animation (the capture operation itself tends to be slow).

If you do not fully understand the explanation above, do not change this property. It should be left as None whenever you want to display time-accurate animation (which should be the case under nearly all circumstances).

gamma

This is a managed property. It is a triplet of numbers, each in the range 0 to 1, corresponding to red, green and blue channels. It specifies the gamma of the background canvas Stimulus, if present (the World needs to be constructed with the canvas=True, or you otherwise need to call MakeCanvas() ). When a canvas is created, this property of the World is automatically linked to the corresponding property of the canvas Stimulus instance; if there is no canvas then this property is unused, although you may wish to link it explicitly to other stimuli with ShareProperties() or LinkPropertiesWithMaster().

gamma = 1 is linear; gamma = -1 gives you the sRGB gamma profile (a piecewise function visually very similar to gamma = 2.2)

Note that gamma is ignored for stimuli that use a lut

green

This is a managed shortcut. It is a shortcut for clearColor[1]

greengamma

This is a managed shortcut. It is a shortcut for gamma[1]

greennoise

This is a managed shortcut. It is a shortcut for noiseAmplitude[1]

height

This is a managed shortcut. It is a shortcut for size[1]

property lut

The value of this property will either be None, or an instance of LookupTable. Assigning to this property is equivalent to calling the SetLUT() method—so you can assign any of the valid argument types accepted by that function. Assigning None disables look-up.

noise

noise is an alias for noiseAmplitude

noiseAmplitude

This is a managed property. It is a triplet of floating-point numbers corresponding to red, green and blue channels. It specifies the noiseAmplitude for the background canvas Stimulus, if present (the World needs to be constructed with the canvas=True, or you otherwise need to call MakeCanvas() ). When a canvas is created, this property of the World is automatically linked to the corresponding property of the canvas Stimulus instance; if there is no canvas then this property is unused, although you may wish to link it explicitly to other stimuli with ShareProperties() or LinkPropertiesWithMaster().

on

on is an alias for visible

origin

origin is an alias for anchor

origin_x

origin_x is an alias for ax_n

origin_y

origin_y is an alias for ay_n

outOfRangeAlpha

This is a managed property. It is a number in the range 0 to 1. It specifies the outOfRangeAlpha for the background canvas Stimulus, if present (the World needs to be constructed with the canvas=True, or you otherwise need to call MakeCanvas() ). When a canvas is created, this property of the World is automatically linked to the corresponding property of the canvas Stimulus instance; if there is no canvas then this property is unused, although you may wish to link it explicitly to other stimuli with ShareProperties() or LinkPropertiesWithMaster().

  • Default value: 1.0

outOfRangeColor

This is a managed property. It is a triplet of numbers, each in the range 0 to 1, corresponding to red, green and blue channels. It specifies the outOfRangeColor for the background canvas Stimulus, if present (the World needs to be constructed with the canvas=True, or you otherwise need to call MakeCanvas() ). When a canvas is created, this property of the World is automatically linked to the corresponding property of the canvas Stimulus instance; if there is no canvas then this property is unused, although you may wish to link it explicitly to other stimuli with ShareProperties() or LinkPropertiesWithMaster().

  • Default value: [1.0, 0.0, 1.0]

ox_n

ox_n is an alias for ax_n

oy_n

oy_n is an alias for ay_n

pixelGrouping

Developer note on pixelGrouping: Explicit rounding, in the shader, to the nearest screen pixel location, is disabled by default. However you can see the tiny rounding errors this creates if you create, for example, a signal function that hits exactly 0 at multiple places on top of a background value of 0.5, such as:

Shady.AddCustomSignalFunction('''
    float Tartan(vec2 p) { p = mod(p, 2.0); return 0.25 * (p.x - p.y); }
''')
w=Shady.World(fullScreenMode=0,bitCombiningMode=1);s=w.Stimulus(sigfunc=Shady.SIGFUNC.Tartan)

Compare this under w.pixelGrouping = 0 with w.pixelGrouping = 1 but be aware that (a) the differences are tiny, rounding to values 1/65535th apart and only visible because of the red-green split, (b) pixel rounding is not computationally trivial on the GPU - it requires a per-fragment matrix multiplication.

You can change the default with Shady.World.SetDefault( pixelGrouping=1 ) - then this setting will be respected automatically whenever you change to w.bitCombiningMode=0 or w.bitCombiningMode=1.

red

This is a managed shortcut. It is a shortcut for clearColor[0]

redgamma

This is a managed shortcut. It is a shortcut for gamma[0]

rednoise

This is a managed shortcut. It is a shortcut for noiseAmplitude[0]

size

This is a managed property. It is a pair of integers denoting the width and height of the World in pixels. Do not attempt to change these values - it will not alter the size of the window and may have unexpected side effects.

  • Default value: [-1, -1]

  • Subscripting shortcuts:
t

t is an alias for timeInSeconds

timeInSeconds

This is a managed property. It is a floating-point scalar value indicating the time in seconds since the World started rendering.

visible

This is a managed property. It is a scalar boolean value indicating whether or not the World should be visible or not. On Windows the transition from visible to invisible and back is reasonably instantaneous. On the Mac you may have to endure the minimize/restore animation (although this is backend-dependent).

  • Default value: 1

  • Canonical name: visible

  • Other aliases: on

width

This is a managed shortcut. It is a shortcut for size[0]

The Stimulus Class

class Shady.Stimulus(world, source=None, name=None, page=None, multipage=False, debugTiming=None, **kwargs)

Bases: LinkGL

A Stimulus is an entity that is drawn automatically within its parent World on every frame. It has a number of properties whose values govern its appearance independent of other stimuli.

Parameters:
  • world (World) – The required first argument is a World instance. To make this more readable you can alternatively call the Stimulus constructor as a World method:

    w = World( ... )
    s = w.Stimulus( source, ... )
    
  • source (str, list, numpy.ndarray, None) – The second argument is the source of the carrier texture. This may be omitted (or equivalently set to None) if your stimulus is just a blank patch, or if its carrier signal is defined purely by a function in the shader (see the signalFunction property). Alternatively source may be a string denoting an image filename or glob pattern for image filenames, a numpy array specifying texture data explicitly, or a list of strings and/or numpy arrays - see LoadTexture() for details.

  • name (str) – This is a string that will identify this Stimulus in the container w.stimuli of the World to which it belongs. To ensure uniqueness of the names in this dict, you may include a single numeric printf-style pattern in the name (the default name, for example, is 'stim%02d').

  • page (int) – You may optionally initialize the page property here.

  • multipage (bool) – If you set this to True, multiple image frames will loaded using the LoadPages() method: this transfers each frame to the graphics card as a separate texture, which you switch between using the page property.

    By contrast, if you leave it at the default value False, multiple image frames are handled by concatenating them horizontally into a single texture, and you switch between them using the frame property, which indirectly manipulates carrierTranslation.

    You may need to use multipage=True for animated images that have a large width and/or high number of frames, because the normal concatenation method may lead to the texture exceeding the maximum allowable width.

  • **kwargs – Managed property values can also be specified as optional keyword arguments during construction—for example:

    s = w.Stimulus( ..., foregroundColor=[1, 0, 0], ... )
    

    The width, height and/or size arguments (all shortcuts/aliases into the envelopeSize managed property) are worth mentioning specifically: these can be used to specify the dimensions of the envelope in pixels. For texture stimuli these can usually be omitted, since usually they will be dictated by the dimensions of the underlying source material. However, they can be specified explicitly if you want to crop or repeat an image texture. Note that you should specify dimensions in the same units as the underlying texture (i.e. unscaled pixels). If you want to change the physical size of the rendered stimulus by magnifying or shrinking the texture, manipulate the envelopeScaling property either directly, or indirectly via scaledSize (but remember this will produce interpolation artifacts).

classmethod AddCustomUniform(name=None, defaultValue=None, **kwargs)

Modifies the class (World or Stimulus) so that it possesses one or more new managed properties, whose values are then accessible from inside the fragment shader. This must be performed before World construction.

Example:

Shady.World.AddCustomUniform( 'spam', [1,2,3] )
Shady.Stimulus.AddCustomUniform( eggs=4, beans=[5,6] )

Either syntax can be used in either class. The keyword-argument syntax has the advantage of being able to define multiple new properties in one call.

The default values you supply dictate whether the new property is 1-, 2-, 3- or 4-dimensional. For a 1-dimensional property, the type of your default value also determines whether the property gets defined as an integer or floating-point variable. (2-, 3- or 4- dimensional properties are always re-cast as floating-point).

The corresponding uniform variables are then automatically made available in the fragment shader code, with the first letter of the property name getting capitalized and a ‘u’ prepended. So, as a consequence of the two lines in the example above, the modified shader would then contain these definitions:

uniform vec3 uSpam;
uniform int uEggs;
uniform vec2 uBeans;

…all of which is useless unless you actually write some custom shader functions that access the new variables. You might use the new variables in your own custom signal-function, modulation-function, windowing-function or color-transformation snippets.

AnimationCallback(func=None)

Decorator version of SetAnimationCallback()

Examples:

w = Shady.World()

@w.AnimationCallback
def anim( self, t ):
        print( t )
BoundingBox(worldCoordinates=False)

This method returns the bounding box of the Stimulus, in pixels.

Parameters:

worldCoordinates (bool) – If True, then the left and bottom coordinates are computed relative to the World’s anchor. If False, then [0,0] is considered to be the bottom-left corner of the World, regardless of its anchor.

Returns:

[left, bottom], [width, height] pixel coordinates for the Stimulus.

Known Issues:

The method takes account of scaling (due to envelopeScaling) and translation (due to envelopeOrigin, envelopeTranslation, Stimulus.anchor and World.anchor). But it does not take account of envelopeRotation

Capture(pil=False, saveas='', normalize='auto')

This captures the pixel data from a particular Stimulus. Note that it accomplishes this simply by calling World.Capture() with the appropriate bounding box. Therefore, two caveats: (1) if other stimuli overlap this one, the capture will contain image data composited from all of them; (2) the bounding- box method is smart enough to compensate for envelopeTranslation and envelopeScaling, but not envelopeRotation.

Parameters:
  • pil (bool) – If True, and if PIL or pillow is installed, return the image data as a PIL.Image.Image instance. If False, return the data as a numpy array.

  • saveas (str) – If PIL or pillow is installed, you can use this argument to specify an optional filename for immediately saving the image data.

  • normalize (bool or ‘auto’) – If False, return raw RGBA values as integers. If True, return floating-point values normalized in the range 0 to 1, and furthermore undo the effects of the current bitCombiningMode if any. If 'auto', the default is False except when all the following conditions are met: numpy is installed, pil is False, and self.bitCombiningMode is non-zero.

Returns:

Either a numpy.ndarray or a PIL.Image.Image instance, depending on the pil argument.

ClearDynamics()

Remove all property dynamics from the instance.

See also: GetDynamic(), GetDynamics(), SetDynamic()

Enter(**props)

If a Stimulus instance has previously left the stage with Leave(), the Enter() method allows it to come back.

You may simultaneously change its properties, using keyword arguments.

GetDynamic(name)

For dynamic properties, return the actual callable object that generates property values, rather than the current static value.

Parameters:

name (str) – Name of the property

Returns:

Callable object responsible for generating values for the named property (or None if there is no such dynamic).

See also: GetDynamics(), SetDynamic(), ClearDynamics()

GetDynamics()

Get an ordered list of (name, callable) tuples detailing all the dynamics of this instance.

See also: GetDynamic(), SetDynamic(), ClearDynamics()

Inherit(other)

Set the values of all managed properties of this instance to match those of another instance. Only properties common to both objects will be considered. Dynamic property values will be copied as dynamic properties.

Parameters:

other – instance whose property values will be copied to the properties of self

Returns:

self

Leave(deferAfterAdditionalFrames=0)

If a Stimulus instance stim is made invisible with stim.visible=False, it is not rendered on screen. However, it is still a member of the stimuli dictionary of the World to which it belongs, and its AnimationCallback (if any), and any dynamics attached to its individual properties, are still evaluated on every frame.

On the other hand, you tell a Stimulus instance to Leave() the stage entirely, it is removed from the stimuli dictionary of its World, it is not rendered (regardless of its visible setting), and none of its callbacks and dynamics are called—not until you tell it to Enter() again.

Returns:

the Stimulus instance itself.

LinkPropertiesWithMaster(master, *pargs, **kwargs)

See ShareProperties()

Returns:

self

LinkTextureWithMaster(master)

This is a wrapper around LinkPropertiesWithMaster() that allows Stimulus instances to share a texture—it shares the textureID, textureSlotNumber and useTexture managed properties.

LoadPages(sources, keys=0, updateEnvelopeSize=True, page=0, **kwargs)

This method prepares a Stimulus instance for animation using the page mechanism (rather than the default frame mechanism). It loads multiple textures in multiple pages, indexed by the specified keys (or by integers starting at keys, if keys is an integer). Subsequently, you can switch between pages by setting the page property or equivalently calling the SwitchTo() method.

This method is called when constructing a Stimulus instance with the multipage constructor option. It can also be called explicitly, which is especially useful if you want to re-use texture buffers that have already been allocated on the graphics card, for new stimulus content.

LoadSubTexture(source, x=0, y=0)

This is similar to LoadTexture() in its interpretation of the source argument. The difference is that the Stimulus must already have an existing texture, and the new source is pasted over the old one (or over part of it, depending on the new source’s size).

x and y are pixel coordinates relative to the existing texture’s lower-left corner. They specify the position of the lower-left corner of the incoming piece of the texture.

LoadTexture(source, updateEnvelopeSize=True, useTexture=True)

Loads texture data from source and associates it with this Stimulus.

Parameters:
  • source – the source of the carrier texture data. This may be:

    • omitted or set to None if there is no texture (just a constant carrier signal, or one defined by a function in the shader)

    • a string (possibly including glob characters '*' and/or '?') denoting one or more image files to be used as animation frames

    • a numpy array specifying the pixel values of a texture image, in which case:

      • source.dtype must be one of:
        • numpy.dtype('uint8') : 8-bit pixel values in the range 0 to 255

        • numpy.dtype('float32') : pixel value in the range 0.0 to 1.0

        • numpy.dtype('float64') : pixel value in the range 0.0 to 1.0 (will be converted to float32 automatically)

      • source.shape must be one of:
        • [height, width] : LUMINANCE image

        • [height, width, 1] : LUMINANCE image

        • [height, width, 2] : LUMINANCE_ALPHA image

        • [height, width, 3] : RGB image

        • [height, width, 4] : RGBA image

    • a list or tuple containing filenames and/or numpy arrays as above, to be used as multiple frames

  • updateEnvelopeSize (bool) – whether or not to update the envelope size to match the dimensions of the new texture

  • useTexture (bool) – new value for the useTexture property attribute that enables or disables the use of this texture

MakePropertiesIndependent(*pargs, **kwargs)

Undoes the effect of ShareProperties(). Optionally, set the values of the properties after unsharing.

Example:

a.ShareProperties( b, c, d, alpha=1, beta=2, gamma=3 )
b.MakePropertiesIndependent( alpha=4 )
# Now `a` and `b` share properties `.beta` and `.gamma`, but
# `b.alpha` is independent and already has the new value 4;
# by contrast `a`, `c` and `d` still share all three properties.

c.MakePropertiesIndependent( 'beta', 'gamma' )
# Now `c` does not share anything except `.alpha`, although the
# property values themselves have not yet been changed.

c.Set( beta=c, gamma=c )
# a syntactic shorthand for the same operation as in the previous
# line
Returns:

self

NewPage(source, key=None, updateEnvelopeSize=True, **kwargs)

Create a new page from the specified source. A page is a bundle of properties that determine texture, envelope size, and (via drawMode and points) envelope shape.

Note that this can be automated, to load multiple sources as multiple pages, either by specifying multipage=True when you construct the Stimulus instance, or by explicitly calling LoadPages().

Parameters:
  • source – Any valid input to LoadTexture(), including None.

  • key – By default, a new page is unlabelled, but if you specify a key here, SavePage( key ) will be called automatically to store and label the new texture settings. This will enable you to SwitchTo() this page in future.

  • **kwargs – Additional keyword arguments can be used to set property values at the same time, if you want. (NB: you can set any property at all this way, but remember that not all properties get paged in and out—apart from the ones whose values get inferred from source automatically, the others that are most meaningful here are drawMode and points)

See also: page, SavePage(), SwitchTo() and LoadPages()

Place(xp, yp=None, worldCoordinates=True, polar=False)

Convert 2-D normalized coordinates (relative to the instance, -1 to +1 in each dimension), into 2-D pixel coordinates, either relative to the World’s current anchor, or relative to the World’s bottom left corner irrespective of anchor.

Input coordinates may be given as one scalar argument, two scalar arguments, or one argument that is a sequence of two numbers. Depending on the polar argument, these will be interpreted as x, y Cartesian coordinates (where, if y omitted, it defaults to y=x) or theta, r polar coordinates (where, if r is omitted, it defaults to r=1).

Parameters:
  • worldCoordinates (bool) – If True, return pixel coordinates relative to the World’s own anchor position. If False, return pixel coordinates relative to the World’s bottom left corner irrespective of its anchor.

  • polar (bool) – If True, input coordinates are interpreted as an angle (in degrees) and an optional radius (0 denoting the center, 1 denoting the edge).

Examples:

instance.Place( [ -1, 1 ] )      # top left corner
instance.Place( -1, 1 )          # likewise, top left corner
instance.Place( 90, polar=True )  # middle of top edge (radius 1 assumed)
instance.Place( [ 90, 0.5 ], polar=True )  # halfway between center and top
instance.Place( 90, 0.5, polar=True )  # likewise, halfway between center and top
classmethod Properties(includeShortcuts=False)

Return a list of all managed properties of this class. (Class method)

Parameters:

includeShortcuts – Determines whether ManagedShortcut instances should be included (True) or not (False; default) along with ManagedProperty instances.

Returns:

A list of ManagedProperty (and optionally also ManagedShortcut) descriptor instances.

ResetClock(other=None)

A Stimulus may have callback functions that are called on every frame—for example, a function that you have registered as its AnimationCallback, or functions that you assign to its managed properties (dynamic value assignment). In all cases, the argument that is passed to these callbacks on each frame is t, time in seconds. By default, this t is the same as the World’s t, i.e. the number of seconds have elapsed since the World began rendering. However, if you wish, you can alter the t0 from which each Stimulus instance’s clock counts:

stim.ResetClock()            # resets the clock to 0
stim.ResetClock( otherStim ) # synchronizes with `otherStim`
SavePage(key)

Save the current “page” (a bundle of properties determining texture, envelope size and envelope shape) in the dictionary self.pages under the specified key.

See also: page, NewPage(), and SwitchTo()

Set(**kwargs)

Set the values of multiple managed properties in one call. An error will be raised if you try to set the value of a non-existent, or non-managed, property.

Returns:

self

Example:

instance.Set( rotation=90, color=(0, 0, 1),  contrast=0.5 )
SetAnimationCallback(callback)

Bind the callable object callback as the instance’s animation callback. Each object, whether it is a World or a Stimulus, may optionally have a single animation callback which is called on every frame. If the callback argument is None, any existing callback is removed.

The animation callback is installed as the attribute self.Animate. By default, this attribute is None.

The prototype for an animation callback can be callback(self, t) or just callback(t) (if it’s the latter then you can, alternatively, simply assign it as self.Animate = callback ).

Example:

def animate( self, t ):
        print( t )
my_world = Shady.World().SetAnimationCallback( animate )

There is also a decorator version of the same operation, simply called AnimationCallback():

stim = my_world.Stimulus()
@stim.AnimationCallback
def Animate( self, t ):
        print( t )
classmethod SetDefault(**kwargs)

Affects the class. Sets the default values of named properties. In instances of the class created subsequently, managed properties will have the new default values.

Example:

cls.SetDefault( color=(1,0,0), rotation=90 )
SetDynamic(name, func, order=-1, canonicalized=False)

Associate a “dynamic” (i.e. a function that can be called repeatedly to set an attribute) with the name of an attribute.

For example:

foo.Set( 'bar',  lambda t: t ** 2 )

This will set foo.bar to t ** 2 every time the method foo._RunDynamics( t ) is called (this call will happen automatically from the API user’s point of view, in the object’s infrastructure implementation).

A dynamic can be attached to any attribute name. If the bar attribute happens to be a ManagedProperty or ManagedShortcut, then it will also automatically support “dynamic value assignment”, i.e. you can do:

foo.bar = lambda t: t ** 2

as a syntactic shorthand for SetDynamic()—the setter will detect the fact that the value is callable, and divert it to the register of dynamics rather than assigning it directly (so the actual static value you get from querying foo.bar will not immediately change).

Parameters:
  • name (str) – name of the attribute

  • func – callable object, or None to remove any dynamic that may already be associated with name

  • order (int, float) – optional numeric ordering key that allows you to control the serial order in which dynamics are evaluated

  • canonicalized (bool) – for internal use only. ManagedProperty and ManagedShortcut descriptors can have multiple aliases. The default settings, canonicalized=False says that name hsa not necessarily been translated to the descriptor’s canonical name, so this should be attempted

Returns:

self

See also: GetDynamic(), GetDynamics(), ClearDynamics()

SetLUT(value)

Sets or unsets a LookupTable for a World or Stimulus. (For World`s, this will only be effective to the extent that the `World’s atmosphere properties are shared with Stimulus instances—for example, the canvas.)

Calling this method is the functional equivalent of setting the lut property:

stim.SetLUT( value )  # These are equivalent
stim.lut = value      #

Setting a look-up table disables automatic linearization via gamma and automatic dynamic-range enhancement via ditheringDenominator, and allows you to take direct control of these issues (although only for one dimension of luminance per pixel: using a look-up table is a form of indexed-color rendering).

See the Shady.Documentation.PreciseControlOfLuminance docstring or Gamma Correction, Dynamic Range Enhancement, and the Canvas for more details.

Parameters:

value (None, str, list, numpy.ndarray, LookupTable) – A pre-existing LookupTable instance may be used here. Alternatively, any valid constructor argument for a LookupTable may be used, and the instance will be constructed implicitly. That means you can use:

  • a numpy.ndarray of integers in n-by-3 or m-by-n-by-3 arrangement, or

  • a list of lists that can be implicitly converted into such an array by numpy, or

  • the filename of a npy or npz file that contains such an array (under the variable name lut in the latter case) to be loaded by Shady.Linearization.LoadLUT(), or

  • the filename of a png file containing the look-up table entries as RGB pixel values in column-first order, again to be loaded by Shady.Linearization.LoadLUT().

Finally you have the option of setting None, to disable the usage of look-up tables.

Returns:

A LookupTable instance.

ShareProperties(*pargs, **kwargs)

Share the underlying array storage of the named managed properties with other objects, so that a change to one affects all. Optionally, set the values at the same time. Calling syntax is flexible - the following examples all take certain property arrays associated with instance a and share them with instances b, c and d:

a.ShareProperties( 'gamma noiseAmplitude backgroundColor', b, c, d )
a.ShareProperties( ['gamma', 'noiseAmplitude', 'backgroundColor'], [b, c], d )
a.ShareProperties( b, c, d,  gamma=-1, noiseAmplitude=0.01, backgroundColor=0.5 ) # sets values at the same time

A similar effect can be obtained via b.LinkPropertiesWithMaster( a, ... )—although there you are limited to one sharee at a time—and a syntactic shorthand for this is to pretend you are assigning the master object instance itself to the target property:

b.gamma = a

Undo with MakePropertiesIndependent() (or b.gamma = b)

Returns:

self

ShareTexture(*others)

This is a wrapper around ShareProperties() that allows Stimulus instances to share a texture—it shares the textureID, textureSlotNumber, textureSize and useTexture managed properties.

SwitchTo(key)

Switch to the “page” associated with the given key. A page is a bundle of properties that determine texture, envelope size and envelope shape. The key argument must be one of the keys in the dictionary self.pages. Note that if the current settings have not been stored (via SavePage(), via the explicit specification of a key during NewPage(), or via the automated loop of NewPage() calls provided by LoadPages()) then this method will cause the current settings to be lost.

See also: page, NewPage(), LoadPages() and SavePage()

WaitFor(condition)

This function blocks the current thread until a specified condition becomes True. The condition will be checked repeatedly in between 1-millisecond sleeps. It can be one of two things:

  • a callable (function) which returns True to signal that the wait is over - e.g.:

    stim.WaitFor( lambda: stim.y < 0 )
    
  • a string that names a dynamic attribute belonging to the current instance. In this case, the wait ends when there is no longer a dynamic attached the the property or shortcut in question. A dynamic may be removed due to explicit action of another thread in your program, or explicit action in this instance’s AnimationCallback (or indeed any instance’s AnimationCallback). Alternatively, a dynamic may automatically remove itself, by raising a StopIteration exception (the Function object returned by Shady.Dynamics.Transition is an example of something that does this):

    stim.scaling = Shady.Transition( stim.scaling, 2, duration=5 )
    # the stimulus will now double in size over the course of 5 sec
    stim.WaitFor( 'scaling' )
    # wait until it's finished
    

All of this assumes that you are operating in a different thread from the World—if you call WaitFor from inside one of the World or Stimulus callbacks, then it will simply sleep indefinitely because nothing will get the chance to change.

addb

This is a managed shortcut. It is a shortcut for offset[2]

addg

This is a managed shortcut. It is a shortcut for offset[1]

addr

This is a managed shortcut. It is a shortcut for offset[0]

alpha

This is a managed property. It is a floating-point number from 0 to 1. It specifies the opacity of the Stimulus as a whole. Note that with psychophysical stimuli you should always ensure alpha == 1, and manipulate backgroundColor and normalizedContrast instead: this is because alpha-blending is carried out by the graphics card AFTER our linearization (via the gamma property or via a look-up table) is applied. Therefore a blended result will no longer be linearized.

anchor

This is a managed property. It is a sequence of two floating-point numbers expressed in normalized coordinates (from -1 to +1). It denotes where, on the surface of the envelope, the anchor point will be. This anchor point is the point whose coordinates are manipulated directly by the other properties, and it also serves as the origin of any scaling and rotation of the envelope. The default value is [0.0, 0.0] denoting the center, whereas [-1.0, -1.0] would be the bottom left corner.

The translation caused by changes to anchor is always rounded down to an integer number of pixels, to avoid it becoming an unforeseen cause of interpolation artifacts.

  • Default value: [0.0, 0.0]

  • Subscripting shortcuts:
anchor_x

anchor_x is an alias for ax_n

anchor_y

anchor_y is an alias for ay_n

angle

angle is an alias for envelopeRotation

property aspect

Non-managed property that affects the behavior of scaledWidth and scaledHeight manipulations. The value may be a None, the string 'fixed', or a floating-point number.

Assigning a floating-point number to scaledAspectRatio will immediately reduce either the horizontal or the vertical component of envelopeScaling as necessary to achieve the target aspect ratio.

Once the value is set to something other than None, future manipulation of either the scaledWidth property or the scaledHeight property will cause the other property to be adjusted simultaneously, to maintain the target aspect ratio. If the value is 'fixed', then the target value is simply “whatever it was before you made the manipulation”.

This prospective control only applies to the scaled* properties, however—if you directly manipulate envelopeSize or envelopeScaling, the scaledAspectRatio setting will not be automatically reasserted.

property atmosphere

This property actually encompasses multiple managed properties, all related to linearization and dynamic-range enhancement.

If you query this property, you will get a dict of the relevant property names and values. You can also assign such a dictionary to it.

More usefully, you can use it to link all the properties between instances in one go:

stim.atmosphere = world   # hard-links all the "atmosphere" properties
                          # at once

For more information, see the Shady.Documentation.PreciseControlOfLuminance docstring or click here.

ax_n

This is a managed shortcut. It is a shortcut for anchor[0]

ay_n

This is a managed shortcut. It is a shortcut for anchor[1]

backgroundAlpha

This is a managed property. It is a floating-point number from 0 to 1, indicating the opacity at locations where the signal has been attenuated away completely (by windowing via plateauProportion, by a custom modulationFunction, or by manipulation of overall normalizedContrast).

For psychophysical stimuli, ensure backgroundAlpha is 1.0 and manipulate backgroundColor instead: although alpha can be used for windowing in this way, alpha-blending is applied post- linearization so the result will not be well linearized, except in very fragile special cases.

backgroundColor

This is a managed property. It is a triplet of numbers, each in the range 0 to 1, corresponding to red, green and blue channels. It specifies the color at locations in which the carrier signal (texture and/or foreground color and/or functionally generated carrier signal) has been attenuated away completely (by contrast scaling, windowing, or custom contrast modulation pattern).

bg

bg is an alias for backgroundColor

bgalpha

bgalpha is an alias for backgroundAlpha

bgblue

This is a managed shortcut. It is a shortcut for backgroundColor[2]

bgcolor

bgcolor is an alias for backgroundColor

bggreen

This is a managed shortcut. It is a shortcut for backgroundColor[1]

bgred

This is a managed shortcut. It is a shortcut for backgroundColor[0]

blue

This is a managed shortcut. It is a shortcut for color[2]

bluegamma

This is a managed shortcut. It is a shortcut for gamma[2]

bluenoise

This is a managed shortcut. It is a shortcut for noiseAmplitude[2]

carrierRotation

This is a managed property. It is a scalar number, expressed in degrees. The carrier will be rotated counter-clockwise by this number of degrees around the center of the envelope. Note that if your rotation values is not divisible by 90, this will introduce interpolation artifacts into stimuli that use textures. (Unlike envelopeRotation, however, this will not compromise pure functionally-generated stimuli.)

carrierScaling

This is a managed property. It is a sequence of two floating-point numbers denoting horizontal and vertical scaling factors. The carrier will be magnified by these factors relative to an origin the center of the envelope. Note that scaling values != 1.0 will introduce interpolation artifacts into stimuli that use textures (but unlike envelopeScaling, this will not compromise pure functionally-generated stimuli).

carrierTranslation

This is a managed property. It is a sequence of two numbers, expressed in pixels, corresponding to x and y dimensions. It shifts the carrier (texture stimulus and/or shader function) relative to the envelope. Note that non-integer translation values will introduce interpolation artifacts into stimuli that use textures (but unlike envelopeTranslation, this should not compromise pure functionally-generated stimuli).

color

This is a managed property. It is a triplet of numbers, each in the range 0 to 1, corresponding to red, green and blue channels. Values may also be negative, in which case no colorization is applied in the corresponding channel.

Where non-negative, foreground color plays slightly different roles depending on other parameters:

If the Stimulus uses a texture, the pixel values from the texture are tinted via multiplication with the color values.

If the Stimulus uses a signalFunction then the signal is also multiplied by the color before being added.

If there is no texture and no signalFunction, the carrier image consists of just the specified uniform solid color. The Stimulus color may still be attenuated towards the backgroundColor—uniformly by setting normalizedContrast < 1.0, and/or as a function of space by setting plateauProportion >= 0.0 or by using a modulationFunction).

colorTransformation

This is a managed property. It is an integer specifying the index of a shader-side color transformation function. If it is set to 0, no special color transformation is performed (but the standard gamma transformation can still be independently applied).

Further values may be supported if you add support for them yourself using AddCustomColorTransformation().

In the shader, the color transformation function takes one input argument (a vec4 containing pre-linearization RGBA values) and return a transformed vec4. The return value will then be passed on to the standard gamma linearization step, if used.

See also: gamma

  • Default value: 0

contrast

contrast is an alias for normalizedContrast

cr

cr is an alias for carrierRotation

cscale

cscale is an alias for carrierScaling

cscaling

cscaling is an alias for carrierScaling

cx

This is a managed shortcut. It is a shortcut for carrierTranslation[0]

cxscale

This is a managed shortcut. It is a shortcut for carrierScaling[0]

cxscaling

cxscaling is an alias for cxscale

cy

This is a managed shortcut. It is a shortcut for carrierTranslation[1]

cyscale

This is a managed shortcut. It is a shortcut for carrierScaling[1]

cyscaling

cyscaling is an alias for cyscale

dd

dd is an alias for ditheringDenominator

depth

depth is an alias for z

depthPlane

depthPlane is an alias for z

ditheringDenominator

This is a managed property. It is a floating-point number. It should be 0 or negative to disable dithering, or otherwise equal to the maximum DAC value (255 for most video cards). It allows implementation of the “noisy-bit” dithering approach (Allard & Faubert, 2008) for increasing effective dynamic range.

This is distinct from the noiseAmplitude property, which specifies noise that is (a) applied pre-gamma-correction, (b) can be scaled differently in different color channels, but (c) is otherwise perfectly correlated across color channels. Noisy-bit dithering, on the other hand (a) is applied post-gamma-correction, (b) has the same amplitude in all color channels but (c) is indepdent in each color channel.

Note that, like gamma-correction, noisy-bit dithering is disabled for stimuli that use a lut

  • Default value: dithering enabled (value determined automatically)

  • Canonical name: ditheringDenominator

  • Other aliases: dd

drawMode

This is a managed property. It is an integer that selects between different drawing behaviors. The different values are given meaningful names in the namespace DRAWMODE, whose documentation also explains the behavior of the different draw modes.

  • Default value: 1

envelopeOrigin

This is a managed property. It is a triplet of numbers, expressed in pixels, denoting the starting-point, in the coordinate system of the parent World, of the offsets envelopeTranslation (which is composed of x and y) and depth coordinate z. The actual rendered position of the anchor point of Stimulus s will be:

[ int(s.x) + s.ox,   int(s.y) + s.oy,  s.z + s.oz ]

relative to the World’s own origin.

You can manipulate envelopeOrigin exclusively and leave envelopeTranslation at 0, as an alternative way of specifying Stimulus position. This is the way to go if you prefer to work in 3D floating-point coordinates instead of 2D integers: unlike envelopeTranslation, this property gives you the opportunity to represent non-integer coordinate values. With that flexibility comes a caveat: non-whole-number values of ox and oy may result in artifacts in any kind of Stimulus (textured or not) due to linear interpolation during rendering. You may therefore wish to take care to round any values you assign, if you choose to use this property. If you exclusively use envelopeTranslation instead, this pitfall is avoided (as you can see in the formula above, its components x and y are rounded down to integer values when they are applied).

Note also that for all stimuli, you should ensure that the total depth coordinate, s.z + s.oz, is in the range ( -1, +1 ].

envelopePosition

envelopePosition is an alias for envelopeTranslation

envelopeRotation

This is a managed property. It is a scalar number, expressed in degrees. The envelope will be rotated counter-clockwise by this number of degrees around its anchor. Note that such transformations of the envelope (except at multiples of 90 degrees) will introduce small artifacts into any stimulus, due to linear interpolation.

envelopeScaling

This is a managed property. It is a sequence of two floating-point numbers denoting horizontal and vertical scaling factors. The actual rendered size, in pixels, of the scaled Stimulus s will be s.envelopeScaling * s.envelopeSize Note that such transformations of the envelope will introduce small artifacts into any stimulus, due to linear interpolation.

envelopeSize

This is a managed property. It is a sequence of two numbers denoting the unscaled width and height of the envelope (i.e. width and height in texel units). Change these numbers if, for example, you want to crop or repeat a texture, or to load new differently-shaped texture (the easiest way to do the latter is to call LoadTexture() method with argument updateEnvelopeSize=True). To change the size of the envelope by stretching the image content to fit, manipulate envelopeScaling or scaledSize instead.

envelopeTranslation

This is a managed property. It is a pair of numbers, expressed in pixels. It dictates the two- dimensional coordinates of the Stimulus location within the drawing area. The values are rounded down to integer values when they are applied, to avoid artifacts that might otherwise be introduced inadvertently due to linear interpolation during rendering.

See also: envelopeOrigin

fg

fg is an alias for color

fgalpha

fgalpha is an alias for alpha

fgblue

fgblue is an alias for blue

fgcolor

fgcolor is an alias for color

fggreen

fggreen is an alias for green

fgred

fgred is an alias for red

foregroundColor

foregroundColor is an alias for color

property frame

This non-managed property is an integer denoting the index of the current frame of a multi-frame stimulus. Note that frames are concatenated horizontally in the underlying carrier texture, so a change in frame is actually achieved by manipulating cx, otherwise known as carrierTranslation[0].

gamma

This is a managed property. It is a triplet of values denoting the screen gamma that should be corrected-for in each of the red, green and blue channels. A gamma value of 1 corresponds to the assumption that your screen is already linear. Setting gamma values other than 1 is an alternative to using a pre-linearized lookup-table or lut.

Any value less than or equal to 0.0 is interpreted to denote the sRGB function, which is a standard piecewise function that follows the gamma=2.2 curve quite closely (although the exponent it uses is actually slightly higher).

Note that gamma is ignored for stimuli that use a lut

green

This is a managed shortcut. It is a shortcut for color[1]

greengamma

This is a managed shortcut. It is a shortcut for gamma[1]

greennoise

This is a managed shortcut. It is a shortcut for noiseAmplitude[1]

height

This is a managed shortcut. It is a shortcut for envelopeSize[1]

property linearMagnification

This property governs the interpolation behavior applied when a textured Stimulus is enlarged (i.e. when its envelopeScaling or carrierScaling are greater than 1).

If True (default), interpolate pixel values linearly when enlarged.

If False, take the “nearest pixel” method when enlarged.

property lut

The value of this property will either be None, or an instance of LookupTable. Assigning to this property is equivalent to calling the SetLUT() method—so you can assign any of the valid argument types accepted by that function. Assigning None disables look-up.

moda

moda is an alias for modulationDepth

modd

modd is an alias for modulationDepth

modf

modf is an alias for modulationFrequency

modfunc

modfunc is an alias for modulationFunction

modo

modo is an alias for modulationOrientation

modp

modp is an alias for modulationPhase

modulationAmplitude

modulationAmplitude is an alias for modulationDepth

modulationDepth

This is a managed shortcut. It is a shortcut for modulationParameters[0] (amplitude, i.e. modulation depth, from 0 to 1)

modulationFrequency

This is a managed shortcut. It is a shortcut for modulationParameters[1] (frequency in cycles/pixel)

modulationFunction

This is a managed property. It is an integer specifying the index of a shader-side contrast modulation function. If it is left at 0, no function is used: the stimulus contrast is then dependent only on the overall normalizedContrast and on the window applied according to plateauProportion.

A value of 1 (which can also be referenced as the constant MODFUNC.SinewaveModulation) corresponds to the one and only shader- side modulation function that we provide out of the box, namely SinewaveModulation which performs sinusoidal contrast modulation. The parameters of this modulation pattern are determined by the modulationParameters property.

Further values, and hence further functions, may be supported if you add them yourself using AddCustomModulationFunction().

In the shader, the return value of a modulation function is a float. This return value is used as a multiplier for stimulus contrast.

See also: modulationParameters, signalFunction, signalParameters, windowingFunction

modulationOrientation

This is a managed shortcut. It is a shortcut for modulationParameters[2] (orientation in degrees)

modulationParameters

This is a managed property. It is a 4-element vector that can be used to pass parameters to the shader-side contrast-modulation function chosen by modulationFunction.

If modulationFunction is left at its default value of 0, these four values are ignored. For the one built-in shader modulation function (modulationFunction=1 corresponding to the shader function SinewaveModulation), the values are interpreted as depth, frequency, orientation and phase of the desired sinusoidal modulation pattern.

If you’re adding your own custom shader function via AddCustomModulationFunction(), your implementation of that function may choose to ignore or reinterpret this property as you wish. If you choose to use it, your shader code can access it as the uniform vec4 variable uModulationParameters.

See also: modulationFunction, signalFunction, signalParameters

modulationPhase

This is a managed shortcut. It is a shortcut for modulationParameters[3] (phase in degrees)

property nFrames

Read-only non-managed property that returns the width of each frame, if the texture pixel data is divided up horizontally into multiple frames.

noise

noise is an alias for noiseAmplitude

noiseAmplitude

This is a managed property. It is a triplet of floating-point numbers corresponding to red, green and blue channels. Negative values lead to uniform noise in the range [s.noiseAmplitude[i], -s.noiseAmplitude[i]]. Positive values lead to Gaussian noise with standard deviation equal to the noiseAmplitude value.

The noise specified in this way is applied before gamma-correction, and is monochromatic (i.e. perfectly correlated across color channels, though it may have different amplitudes in each channel). It is particularly useful for applying very-low-amplitude noise prior to look-up in a high-dynamic-range bit-stealing look-up table. It can also be useful at higher amplitudes, to create visible noise effects.

It is not to be confused with noisy-bit dithering, which is applied post-gamma-correction (and only when a look-up table is not used), and which is controlled independently by the ditheringDenominator property.

normalizedContrast

This is a managed property. It is a scalar floating-point value in the range 0.0 to 1.0 which scales the overall contrast of the Stimulus. At a contrast of 0, the Stimulus is reduced to its backgroundColor.

offset

This is a managed property. It is a triplet of numbers, each in the range 0.0 to 1.0, corresponding to red, green and blue channels. This is added uniformly to all pixel values before they are scaled by normalizedContrast or by windowing (via plateauProportion and windowingFunction) and/or custom modulation (via modulationFunction).

  • Default value: [0.0, 0.0, 0.0]

  • Subscripting shortcuts:
on

on is an alias for visible

opacity

opacity is an alias for alpha

orientation

orientation is an alias for envelopeRotation

outOfRangeAlpha

This is a managed property. It is a floating-point number from 0 to 1. It specifies the color that should be used for pixels whose values go out of range. A negative value disables this feature (the alpha value of an out-of- range pixel is left unchanged).

  • Default value: 1.0

outOfRangeColor

This is a managed property. It is a triplet of numbers, each in the range 0 to 1, corresponding to red, green and blue channels. It specifies the color that should be used for pixels whose values go out of range. A negative value means “do not flag out-of-range values in this color channel” (values are merely clipped in the range 0 to 1 in that case).

  • Default value: [1.0, 0.0, 1.0]

ox

This is a managed shortcut. It is a shortcut for envelopeOrigin[0]

oy

This is a managed shortcut. It is a shortcut for envelopeOrigin[1]

oz

This is a managed shortcut. It is a shortcut for envelopeOrigin[2]

property page

A Stimulus may optionally have more than one “page”. A page is a label attached to a particular set of property values that determine texture, envelope size and envelope shape.

A page may have any hashable label—integers and strings are usually the most meaningful. The page property allows you to query the label of the current page, or switch to a different page according to its label. It is not a managed property, but it does support dynamic value assignment.

See also: LoadPages(), NewPage(), SavePage(), and SwitchTo()

penThickness

This is a managed property. It is a scalar floating-point value that determines the width, in pixels, of of lines and points drawn when you set drawMode to DRAWMODE.POINTS, DRAWMODE.LINES, DRAWMODE.LINE_STRIP or DRAWMODE.LINE_LOOP. Implementation is driver-dependent however: many graphics cards seem to consider line-thickness to be a legacy feature and have dropped support for it (for lines, but maybe not for points) in their pure modern-OpenGL contexts.

  • Default value: 1.0

plateauProportion

This is a managed property. It is sequence of two floating-point values corresponding to the x and y dimensions.

A negative value indicates that no windowing is to be performed in the corresponding dimension.

A value in the range [0, 1] causes windowing to occur: with the default (raised cosine) windowingFunction, a plateauProportion of 0 gives a Hann window, 1 gives a boxcar window, and intermediate values combine the specified amount of constant plateau with a raised- cosine falloff to the edge of the envelope (i.e., a Tukey window).

A non-zero plateau proportion can also be combined with other (custom self-written) windowingFunction implementations.

Note that this means [1, 1] gives a circular or elliptical envelope with sharp edges, in contrast to [-1, -1] which gives a square or rectangular envelope.

property points

This is a view into the Stimulus instance’s optional array of coordinates. If you do not have the third-party package numpy installed, then this is fairly limited (although you can assign a list of alternating x and y coordinates to it, you will not be able to do any array arithmetic operations with it, which is where its power lies). With numpy, your points will appear as an n-by-2 array of coordinates (you can also view the same data as a sequence of complex numbers, by accessing as pointsComplex). These coordinates are not used in the default drawMode, which is DRAWMODE.QUAD. To learn how other draw modes use the coordinates, see the documentation for the DRAWMODE namespace.

Assignment to points is interchangeable with assignment to pointsComplex—in either case, the input can be a sequence of n complex numbers OR an n-by-2 array of real-valued coordinates. Under the hood, assignment to points changes the content of the managed property pointsXY and also adjusts the managed property nPoints

The coordinate system of the points is always in pixels relative to the bottom-left corner of the Stimulus bounding-box—i.e. the bottom-left corner of the rectangle you would see if you were to switch the Stimulus back to drawMode=Shady.DRAWMODE.QUAD. The World.anchor, Stimulus.position, Stimulus.anchor and Stimulus.size determine where this local origin actually lies on screen, but the points themselves are expressed independently of these, and points defined outside the bounding-box (e.g. with negative coordinates) still get drawn.

property pointsComplex

This is a view into the Stimulus instance’s optional array of coordinates. The same data are also accessible as points. The only difference is that when you ask for pointsComplex, the data are interpreted as a sequence of complex numbers (requires the third-party package numpy).

Assignment to pointsComplex is interchangeable with assignment to points—in either case, the input can be a sequence of n complex numbers OR an n-by-2 array of real-valued coordinates.

pos

pos is an alias for envelopeTranslation

position

position is an alias for envelopeTranslation

pp

pp is an alias for plateauProportion

ppx

This is a managed shortcut. It is a shortcut for plateauProportion[0]

ppy

This is a managed shortcut. It is a shortcut for plateauProportion[1]

red

This is a managed shortcut. It is a shortcut for color[0]

  • Canonical name: red

  • Other aliases: fgred

redgamma

This is a managed shortcut. It is a shortcut for gamma[0]

rednoise

This is a managed shortcut. It is a shortcut for noiseAmplitude[0]

rotation

rotation is an alias for envelopeRotation

scale

scale is an alias for envelopeScaling

property scaledAspectRatio

Non-managed property that affects the behavior of scaledWidth and scaledHeight manipulations. The value may be a None, the string 'fixed', or a floating-point number.

Assigning a floating-point number to scaledAspectRatio will immediately reduce either the horizontal or the vertical component of envelopeScaling as necessary to achieve the target aspect ratio.

Once the value is set to something other than None, future manipulation of either the scaledWidth property or the scaledHeight property will cause the other property to be adjusted simultaneously, to maintain the target aspect ratio. If the value is 'fixed', then the target value is simply “whatever it was before you made the manipulation”.

This prospective control only applies to the scaled* properties, however—if you directly manipulate envelopeSize or envelopeScaling, the scaledAspectRatio setting will not be automatically reasserted.

property scaledHeight

Non-managed property that reflects the product of envelopeSize[1] and envelopeScaling[1]. Assigning to this property will change envelopeScaling[1] accordingly. Dynamic value assignment is supported.

If the scaledAspectRatio property is either 'fixed' or a numeric value, then the value of scaledWidth will simultaneously be adjusted to ensure aspect ratio is preserved. If scaledAspectRatio is None, then scaledWidth will not be adjusted.

property scaledSize

Non-managed property that reflects the product of envelopeSize and envelopeScaling. Assigning to this property will change envelopeScaling accordingly. Dynamic value assignment is supported. Note that you can also address scaledWidth and scaledHeight separately if you wish.

If the scaledAspectRatio property is either None or 'fixed', then the explicitly-specified scaledSize value will be respected exactly. However, if you have previously set the value of scaledAspectRatio to a numeric constant, then the requested scaledSize values may be adjusted (one of them will be reduced) if necessary to preserve that aspect ratio.

property scaledWidth

Non-managed property that reflects the product of envelopeSize[0] and envelopeScaling[0]. Assigning to this property will change envelopeScaling[0] accordingly. Dynamic value assignment is supported.

If the scaledAspectRatio property is either 'fixed' or a numeric value, then the value of scaledHeight will simultaneously be adjusted to ensure aspect ratio is preserved. If scaledAspectRatio is None, then scaledHeight will not be adjusted.

scaling

scaling is an alias for envelopeScaling

siga

siga is an alias for signalAmplitude

sigf

sigf is an alias for signalFrequency

sigfunc

sigfunc is an alias for signalFunction

signalAmplitude

This is a managed shortcut. It is a shortcut for signalParameters[0] (amplitude from 0 to 1)

signalFrequency

This is a managed shortcut. It is a shortcut for signalParameters[1] (frequency in cycles/pixel)

signalFunction

This is a managed property. It is an integer specifying the index of a shader-side signal function. If it is left at 0, no function is used: the carrier content is then dependent purely on the texture, if any, or is blank if no texture was specified. A value of 1 (which can also be referenced as the constant SIGFUNC.SinewaveSignal) corresponds to the one and only shader-side signal function that we provide out of the box, namely SinewaveSignal which generates a sinusoid. The parameters of the sinusoid are determined by the signalParameters property.

Further values, and hence further functions, may be supported if you add them yourself using AddCustomSignalFunction().

In the shader, the return value of a signal functions is either a vec3 or a float. This return value gets multiplied by the color of the Stimulus, and added to its backgroundColor (and/or its texture, if any). If color is negative (i.e. disabled, as it is by default) then the function output is multiplied by vec3(1.0, 1.0, 1.0).

See also: signalParameters, modulationFunction, modulationParameters, windowingFunction

signalOrientation

This is a managed shortcut. It is a shortcut for signalParameters[2] (orientation in degrees)

signalParameters

This is a managed property. It is a 4-element vector that can be used to pass parameters to the shader-side carrier-signal-generating function chosen by signalFunction. If signalFunction is left at its default value of 0, the signalParameters are ignored. For the one built-in shader signal function (signalFunction=1 corresponding to the shader function SinewaveSignal), these parameters are interpreted as amplitude, frequency, orientation and phase of the sinusoidal pattern. Signal functions are additive to your background and/or texture, so if you have no texture and a background color of, for example, 0.3 or 0.7, a sinewave amplitude greater than 0.3 will go out of range at full contrast. (Beware also the additive effect of noise, if your noiseAmplitude is not 0.)

If you’re adding your own custom shader function via AddCustomSignalFunction(), your implementation of that function may choose to ignore or reinterpret this property as you wish. If you choose to use it, your shader code can access it as the uniform vec4 variable uSignalParameters.

See also: signalFunction, modulationFunction, modulationParameters

signalPhase

This is a managed shortcut. It is a shortcut for signalParameters[3] (phase in degrees)

sigo

sigo is an alias for signalOrientation

sigp

sigp is an alias for signalPhase

size

size is an alias for envelopeSize

smoothing

This is a managed property. It is an integer value that determines whether lines, dots and polygons should be drawn with or without smoothing. The results are unfortunately unpredictable as they vary according to your graphics card and drivers. Example: with DRAWMODE.POINTS, setting smoothing=1 causes the dots to be round rather than square—but with some graphics drivers, they will remain square regardless (to guarantee round dots, it’s better to draw tiny many-sided polygons). A value of 0 means no smoothing. A value of 1 means points and lines should be smoothed. A value of 2 means lines, points and polygons are smoothed (note that polygon smoothing causes diagonal “crack” artifacts on many graphics cards/drivers, so this option is not enabled by default).

  • Default value: 1

property text

To use the text property of the Stimulus class, you must first explicitly import Shady.Text (see Shady.Text for documentation on this property).

textureChannels

This is a managed property. It is an integer value denoting whether the texture has 1, 2, 3 or 4 channels. If there is no texture, the value is -1. You should consider this property read-only—do not change the value by hand.

  • Default value: -1

textureSize

This is a managed property. It is a pair of values denoting the width and height of the texture data, in pixels. If there is no texture, both values are -1. You should consider this property read-only—do not change the values by hand.

  • Default value: [-1, -1]

useTexture

This is a managed property. It is a boolean value. The default value is True if a texture has been specified, and this causes pixel values to be drawn from the texture specified by the constructor argument source (or the source argument to a subsequent NewPage() or LoadTexture() call). If useTexture is set to False, the pixel values are determined by backgroundColor and/or foregroundColor (as well as offset, normalizedContrast, and any windowing and shader-side signalFunction or modulationFunction).

  • Default value: 1

property video

To use the video property of the Stimulus class, you must first explicitly import Shady.Video (see Shady.Video for documentation on this property).

visible

This is a managed property. It is a boolean value that determines whether the Stimulus is rendered or not.

  • Default value: 1

  • Canonical name: visible

  • Other aliases: on

width

This is a managed shortcut. It is a shortcut for envelopeSize[0]

windowFunction

windowFunction is an alias for windowingFunction

windowingFunction

This is a managed property. It is an integer specifying the index of a shader-side windowing function. If it is set to 0 (or if the plateauProportion property is negative), no spatial windowing is used.

The default value of 1 (which can also be referenced as the constant WINFUNC.Hann) corresponds to the one and only shader-side windowing function that we provide out of the box, namely Hann, which causes contrast to fall off according to a raised cosine function of radial distance.

Further values, and hence further functions, may be supported if you add them yourself using AddCustomWindowingFunction().

In the shader, the windowing function takes one input argument (a float whose value will range from 0 at peak of the window to 1 at the edge) and return a float. This return value is used as a multiplier for stimulus contrast.

See also: signalFunction, modulationFunction

winfunc

winfunc is an alias for windowingFunction

x

This is a managed shortcut. It is a shortcut for envelopeTranslation[0]

xscale

xscale is an alias for xscaling

xscaling

This is a managed shortcut. It is a shortcut for envelopeScaling[0]

xy

xy is an alias for envelopeTranslation

y

This is a managed shortcut. It is a shortcut for envelopeTranslation[1]

yscale

yscale is an alias for yscaling

yscaling

This is a managed shortcut. It is a shortcut for envelopeScaling[1]

z

This is a managed property. It is a floating-point number that determines the depth plane of the Stimulus. The convention is that negative values put you closer to the camera, and positive further away; also, you must ensure -1 <= z <= +1. Since the projection is orthographic, the value is purely used for depth-sorting of stimuli: therefore, setting z to a non-integer value will not cause interpolation artifacts.

Global Functions and Constants

Shady.AddCustomSignalFunction(code)

Defines a new signal function in the fragment shader.

Must be called, before construction of your World. The code argument is a (usually triple-quoted multi-line) string containing the complete definition of a function in GLSL.

The protoype for the function may be one of the following:

float MySignalFunction( vec2 xy ) { ... }
vec3  MySignalFunction( vec2 xy ) { ... }

where xy are coordinates measured in pixels relative to the center of the stimulus. Obviously we’re using MySignalFunction here as a placeholder—use your own descriptive name for the new function. Your function must return either a floating-point signal value, or three-dimensional (red, green, blue) signal value. If non-negative values have been supplied in a Stimulus instance’s color property, your signal function will get multiplied by these. Then, any desired contrast modulation and windowing will be applied. Then, the result will be added to the backgroundColor.

Your new function can be applied to a Stimulus instance stim as follows:

stim.signalFunction = Shady.SIGFUNC.MySignalFunction

If you want to parameterize your new function further, you can use the existing uniform vec4 uSignalParameters variable, which is linked to the Stimulus.signalParameters property. You can also define your own additional properties/uniform variables with the AddCustomUniform() class method.

Shady.AddCustomModulationFunction(code)

Defines a new contrast-modulation function in the fragment shader.

Must be called before construction of your World. The code argument is a (usually triple-quoted multi-line) string containing the complete definition of a function in GLSL.

The protoype for the function must be:

float MyModulationFunction( vec2 xy ) { ... }

where xy are coordinates measured in pixels relative to the center of the stimulus. Obviously we’re using MyModulationFunction here as a placeholder—use your own descriptive name for the new function. Your function must return a floating-point contrast multiplier.

Your new function can be applied to a Stimulus instance stim as follows:

stim.modulationFunction = Shady.MODFUNC.MyModulationFunction

If you want to parameterize your new function further, you can use the existing uniform vec4 uModulationParameters variable, which is linked to the Stimulus.modulationParameters property. You can also define your own additional properties/uniform variables with the AddCustomUniform() class method.

Shady.AddCustomWindowingFunction(code)

Defines a new spatial windowing function in the fragment shader.

Must be called before construction of your World. The code argument is a (usually triple-quoted multi-line) string containing the complete definition of a function in GLSL.

The protoype for the function must be:

float MyWindowingFunction( float r ) { ... }

where the domain of r if from 0 (in the center of the Stimulus, or indeed anywhere on its plateau if it has one) to 1 (at outer edge of the largest oval that fits in the Stimulus bounding box). Obviously we’re using MyWindowingFunction here as a placeholder— use your own descriptive name for the new function. Your function must return a floating-point contrast multiplier.

Your new function can be applied to a Stimulus instance stim as follows (bearing in mind that a negative plateauProportion value disables windowing entirely):

stim.windowingFunction = Shady.WINFUNC.MyWindowingFunction
stim.plateauProportion = 0

If you want to parameterize your new function further, you can define your own additional properties/uniform variables with the AddCustomUniform() class method.

Shady.AddCustomColorTransformation(code)

Defines a new color transformation function in the fragment shader.

Must be called before construction of your World. The code argument is a (usually triple-quoted multi-line) string containing the complete definition of a function in GLSL.

The protoype for the function must be:

vec4 MyColorTransformation( vec4 color ) { ... }

where the input and output arguments are both RGBA vectors in the domain [0, 1]. The custom transformation step is applied to the pixel color immediately before standard gamma linearization, if any.

Your new function can be applied to a Stimulus instance stim as follows:

stim.colorTransformation = Shady.COLORTRANS.MyColorTransformation

If you want to parameterize your new function further, you can define your own additional properties/uniform variables with the AddCustomUniform() class method.

Shady.BackEnd(windowing=None, acceleration=None)

Globally specify the back-end windowing and rendering systems that future World instances should use.

Parameters:
  • windowing – specifies the windowing system. Possible values are as follows:

    'default':

    use the ShaDyLib dynamic library if available, else fall back on pyglet.

    'shadylib', 'accel', or 'glfw':

    use the ShaDyLib dynamic library (windowing is handled via the GLFW library from http://glfw.org ).

    'pyglet':

    use pyglet (a third-party package that you will need to install separately if you want to use this option)

    'pygame':

    use pygame (a third-party package that you will need to install separately if you want to use this option)

  • acceleration – specifies the rendering implementation, i.e. whether to use the ShaDyLib dynamic library (and if so, whether to use the “development” copy of ShaDyLib in cases where you have the entire Shady repository including the C++ sources for ShaDyLib) or whether to fall back on Python code for rendering (not recommended). Possible values are:

    None:

    leave things as they were (default).

    False:

    disable ShaDyLib and fall back on the pyglet or PyOpenGL code in the PyEngine submodule (this option is not recommended for time-critical presentation).

    True:

    if ShaDyLib is already imported, leave things as they are; if not, import either version of ShaDyLib or die trying. Prefer the development version, if available. Print the outcome.

    'bundled':

    silently import the bundled version of ShaDyLib from the Shady.accel sub-package, or die trying.

    'devel':

    silently import the development version of ShaDyLib from /../accel-src/release/, or die trying.

    'auto':

    try to import ShaDyLib. Prefer the development version, if available. Don’t die in the attempt. Whatever happens, print the outcome.

Returns:

If both input arguments are None, the name of the current windowing back-end is returned. Otherwise, returns None.

Shady.Screens(pretty_print=False)

Get details of any attached screens using the Screens() method of whichever windowing backend is enabled.

Parameters:

pretty_print (bool) – determines the type of the return value

Returns:

If pretty_print is True, returns a human-readable string. If pretty_print is False, returns a dict.

class Shady.SIGFUNC

This class is an enum container: a namespace of values that can be assigned to the signalFunction property of a Stimulus instance. Its members are named according to the signal functions available in the shader. The only built-in function is SinewaveSignal which has a value of 1. So, when you set the signalFunction property of a Stimulus to 1, the SinewaveSignal function is selected in the shader, and when you set it to 0, no signal function is selected.

You can define further signal functions, and hence add names and values to this namespace, with AddCustomSignalFunction.

See also: MODFUNC, WINFUNC

class Shady.MODFUNC

This class is an enum container: a namespace of values that can be assigned to the modulationFunction property of a Stimulus instance. Its members are named according to the modulation functions available in the shader. The only built-in function is SinewaveModulation which has a value of 1. So, when you set the modulationFunction property of a Stimulus to 1, the SinewaveModulation function is selected in the shader, and when you set it to 0, no modulation function is selected.

You can define further modulation functions, and hence add names and values to this namespace, with AddCustomModulationFunction.

See also: SIGFUNC, WINFUNC

class Shady.WINFUNC

This class is an enum container: a namespace of values that can be assigned to the windowingFunction property of a Stimulus instance. Its members are named according to the windowing functions available in the shader. The only built-in function is Hann which has a value of 1. So, when you set the windowingFunction property of a Stimulus to 1, the Hann function is selected in the shader, and when you set it to 0, no windowing function is selected.

You can define further windowing functions, and hence add names and values to this namespace, with AddCustomWindowingFunction.

See also: SIGFUNC, MODFUNC

class Shady.COLORTRANS

This class is an enum container: a namespace of values that can be assigned to the colorTransformation property of a Stimulus instance.

You can define color transformations, and hence add names and values to this namespace, with AddCustomColorTransformation.

class Shady.DRAWMODE

This class is an enum container: a namespace of values that can be assigned to the drawMode property of a Stimulus instance. The default drawMode is DRAWMODE.QUAD: that means that each Stimulus is automatically drawn as a rectangle according to its envelopeSize.

Other modes rely on the points property, which contains a sequence of two-dimensional coordinates:

  • DRAWMODE.POINTS draws a disconnected dot at each location.

  • DRAWMODE.LINES takes two locations at a time and connects each pair with a line segment, disconnected from previous or subsequent line segments.

  • DRAWMODE.LINE_STRIP draws line segments continuously from one location to the next, only taking the pen off the paper if it enounters a NaN coordinate.

  • DRAWMODE.LINE_LOOP is like LINE_STRIP, but the last point in each group is joined back to the first (where a “group” is delimited by NaNs in the sequence of coordinates).

  • DRAWMODE.POLYGON also connects successive locations continuously, and fills the area bounded by the lines thus drawn (a NaN coordinate is a way to delimit multiple polygons)