Opticka 2.18.1
Opticka is an experiment manager for behavioral research.
Loading...
Searching...
No Matches
baseStimulus Class Reference

baseStimulus is the superclass for all stimulus objects More...

Detailed Description

baseStimulus is the superclass for all stimulus objects

Superclass providing basic structure for all stimulus classes. This is a dynamic properties descendant, allowing for the temporary run variables used, which get appended "name"Out, i.e. speed is duplicated to a dymanic property called speedOut; it is the dynamic propertiy which is used during runtime, and whose values are converted from definition units like degrees to pixel values that PTB uses. The transient copies are generated on setup and removed on reset.

Todo:
build up animatorManager functions

Copyright ©2014-2022 Ian Max Andolina — released: LGPL3, see LICENCE.md

Inheritance diagram for baseStimulus:

Public Types

enum  Events { readPanelUpdate }
 

Public Member Functions

virtual setup (in runObject)
 ALL Children must implement these 5 methods!
 
virtual draw (in runObject)
 draw to the screen buffer, ready for flip()
 
virtual animate (in runObject)
 animate the stimulus, normally called after a draw
 
virtual update (in runObject)
 
virtual reset (in runObject)
 
function baseStimulus (in varargin)
 Class constructor.
 
function set colour (in me, in value)
 colour set method Allow 1 (R=G=B) 3 (RGB) or 4 (RGBA) value colour
 
function set alpha (in me, in value)
 alpha set method
 
function get delta (in me)
 delta Get method delta is the normalised number of pixels per frame to move a stimulus
 
function get dX (in me)
 dX Get method X position increment for a given delta and angle
 
function get dY (in me)
 dY Get method Y position increment for a given delta and angle
 
function show (in me)
 Method to set isVisible=true.
 
function hide (in me)
 Method to set isVisible=false.
 
function setOffTime (in me, in time)
 set offTime
 
function setDelayTime (in me, in time)
 set delayTime
 
function resetTicks (in me)
 reset the various tick counters for our stimulus
 
function getMousePosition (in me)
 get mouse position we make sure this is only called once per animation tick to improve performance and ensure all stimuli that are following mouse position have consistent X and Y per frame update This sets mouseX and mouseY and mouseValid if mouse is within PTB screen (useful for mouse override positioning for stimuli)
 
function run (in me, in benchmark, in runtime, in s, in forceScreen, in showVBL)
 Run stimulus in a window to preview it.
 
function makePanel (in me, in parent)
 make a GUI properties panel for this object
 
function selectFilePanel (in me, in varargin)
 read values from a GUI properties panel for this object
 
function readPanel (in me, in varargin)
 read values from a GUI properties panel for this object
 
function showPanel (in me)
 show GUI properties panel for this object
 
function hidePanel (in me)
 hide GUI properties panel for this object
 
function closePanel (in me, in varargin)
 close GUI panel for this object
 
function cleanHandles (in me, in ignoredArg)
 clean any handles
 
function getP (in me, in name, in range)
 gets a property copy or original property
 
function setP (in me, in name, in value)
 sets a property copy or original property
 
function updateXY (in me, in x, in y, in useDegrees)
 Update only position info, faster and doesn't reset image etc.
 
- Public Member Functions inherited from optickaCore
function optickaCore (in varargin)
 Class constructor.
 
function get fullName (in me)
 
function getALF (in me, in subject, in lab, in create)
 get the ALF path
 
function makeReport (in me, in rpt)
 Generate a detailed report for the opticka object.
 
function findAttributes (in me, in attrName, in attrValue)
 
function findAttributesandType (in me, in attrName, in attrValue, in type)
 find properties of object with specific attributes, for example all properties whose GetAcccess attribute is public and type is logical.
 
function findPropertyDefault (in me, in propName)
 Return the declared default value for a class property.
 
function clone (in me)
 Use this syntax to make a deep copy of the object, i.e. OBJ_OUT has the same field values, but will not behave as a handle-copy of me anymore.
 
function checkSuperclasses (in List)
 
function editProperties (in me, in properties)
 method to modify a set of properties
 
function setProp (in me, in property, in value)
 method to fast change a particular value. This is useful for use in anonymous functions, like in the state machine.
 
function initialiseSaveFile (in me, in varargin)
 just get date fragment for backwards compatibility
 
function checkPaths (in me)
 checks the paths are valid
 

Static Public Member Functions

static function mixColour (in c1, in c2, in contrast)
 linear interpolation between two colour arrays based on a contrast
 
static function d2r (in degrees)
 degrees2radians
 
static function r2d (in r)
 radians2degrees
 
static function findDistance (in x1, in y1, in x2, in y2)
 findDistance in X and Y coordinates
 
static function updatePosition (in delta, in angle)
 updatePosition returns dX and dY given an angle and delta
 
- Static Public Member Functions inherited from optickaCore
static function initialiseGlobals (in doReset, in doOpen)
 
static function makeArgs (in args)
 Converts cell args to structure array.
 
static function addDefaults (in args, in defs)
 regularises and adds default options to arg input
 
static function hasKey (in in, in key)
 check if a struct / object has a propery / field
 
static function getKeys (in device)
 PTB Get key presses, stops key bouncing.
 

Public Attributes

Property type
 stimulus type
 
Property xPosition
 
Property yPosition
 
Property size
 
Property colour
 
Property alpha
 Alpha (opacity) [0-1], this gets combined with the RGB colour.
 
Property startPosition
 
Property speed
 
Property angle
 angle in degrees (0 - 360)
 
Property delayTime
 
Property offTime
 time to turn stimulus off, relative to stimulus onset
 
Property isVisible
 true or false, whether to draw() this object
 
Property mouseOverride
 override X and Y position with mouse input? Useful for RF mapping
 
Property showOnTracker
 show the position on the Eyetracker display?
 
Property verbose
 Do we log extra details to the command-line?
 
Property xFinal
 
Property yFinal
 
Property mvRect
 
Property szIsPx
 
Property szPx
 computed size in pixels
 
Property szD
 computed size in °
 
Property xFinalD
 X and Y position in °
 
Property yFinalD
 
Property isRect
 
Property tick
 tick updates +1 on each call of draw (even if delay or off is true and no stimulus is drawn, resets on each update
 
Property drawTick
 draw tick only updates when a draw is actually performed, resets on each update
 
Property ppd
 pixels per degree (normally inhereted from screenManager)
 
Property delta
 What our per-frame motion delta is.
 
Property dX
 X update which is computed from our speed and angle.
 
Property dY
 X update which is computed from our speed and angle.
 
- Public Attributes inherited from optickaCore
Property name
 object name
 
Property comment
 comment
 
Property verbose
 verbose logging, subclasses must assign this. This is normally logical true/false
 
Property optickaVersion
 version number
 
Property dateStamp
 clock() dateStamp set on construction
 
Property uuid
 universal ID
 
Property paths
 storage of various paths
 
Property fullName
 The fullName is the object name combined with its uuid and class name.
 

Protected Member Functions

function addRuntimeProperties (in me)
 These are transient properties that specify actions during runtime.
 
function updateRuntimeProperties (in me)
 Update transient properties that specify actions during runtime.
 
function computePosition (in me)
 compute xFinal and yFinal (in pixels) taking startPosition, xPosition, yPosition and direction/angle into account
 
function setAnimationDelta (in me)
 
function setRect (in me)
 
function toStructure (in me, in tmp)
 Converts properties to a structure.
 
function removeTmpProperties (in me)
 Finds and removes dynamic properties.
 
function delete (in me)
 
- Protected Member Functions inherited from optickaCore
function parseArgs (in me, in args, in allowedProperties)
 Sets properties from a structure or normal arguments pairs, ignores invalid or non-allowed properties.
 
function addArgs (in me, in args)
 
function setPaths (in me)
 Sets properties from a structure or normal arguments pairs, ignores invalid or non-allowed properties.
 
function getFonts (in me)
 set paths for object
 
function toStructure (in me)
 Converts properties to a structure.
 
function getType (in me, in in)
 
function logOutput (in me, in in, in message, in override)
 Give a metaproperty return the likely property class.
 
function salutation (in me, in varargin)
 Prints messages dependent on verbosity.
 

Protected Attributes

Property family
 the stimulus family (grating, dots etc.)
 
Property texture
 Our texture pointer for texture-based stimuli.
 
Property handles
 handles for the GUI
 
Property sM
 our screen manager
 
Property animator
 animation manager
 
Property screenVals
 screen settings generated by sM on setup
 
Property isSetup
 
Property isGUI
 is panel constructed?
 
Property dstRect
 initial screen rectangle position [LEFT TOP RIGHT BOTTOM]
 
Property mouseValid
 is mouse position within screen co-ordinates?
 
Property mouseX
 mouse X position
 
Property mouseY
 mouse Y position
 
Property delayTicks
 delay ticks to wait until display
 
Property offTicks
 ticks before stimulus turns off
 
Property inSetup
 are we setting up?
 
Property delta_
 delta cache
 
Property dX_
 dX cache
 
Property dY_
 dY cache
 
Property isInSetColour
 deal with interaction of colour and alpha
 
Property setLoop
 
Property ignorePropertiesBase
 Which properties to ignore cloning when making transient copies in setup.
 
Property ignorePropertiesUIBase
 Which properties to not draw in the UI panel.
 
- Protected Attributes inherited from optickaCore
Property cloning
 are we cloning this from another object
 
Property mversion
 Matlab version number, this is transient so it is not saved.
 
Property sansFont
 sans font
 
Property monoFont
 monoFont
 
Property className
 class name
 
Property savePrefix
 save prefix generated from clock time
 
Property fullName_
 cached full name
 

Member Enumeration Documentation

◆ Events

Enumerator
readPanelUpdate 

triggered when reading from a UI panel,

252 :3) + me.colour2(1:3))/2;
253 end
254 me.isInSetColour = false;
255 end
256
257 % ===================================================================
258 %> @brief alpha set method
259 %>
260 % ===================================================================
261 function set.alpha(me, value)
262 if me.isSetup; warning('You should set alphaOut to affect drawing...'); end
263 if value<0; value=0;elseif value>1; value=1; end
264 me.alpha = value;
265 if ~me.isInSetColour
266 me.colour = me.colour(1:3); %force colour to be regenerated
267 if ~isempty(findprop(me,'colour2')) && ~isempty(me.colour2)
268 me.colour2 = [me.colour2(1:3) me.alpha];
269 end
270 if ~isempty(findprop(me,'baseColour')) && ~isempty(me.baseColour)
271 me.baseColour = [me.baseColour(1:3) me.alpha];
272 end
273 end
274 end
275
276 % ===================================================================
277 %> @brief delta Get method
278 %> delta is the normalised number of pixels per frame to move a stimulus
279 % ===================================================================
280 function value = get.delta(me)
281 value = (getP(me,'speed') * me.ppd) * me.screenVals.ifi;
282 end
283
284 % ===================================================================
285 %> @brief dX Get method
286 %> X position increment for a given delta and angle
287 % ===================================================================
288 function value = get.dX(me)
289 value = 0;
290 if ~isempty(findprop(me,'directionOut'))
291 [value,~]=me.updatePosition(me.delta,me.directionOut);
292 elseif ~isempty(findprop(me,'angleOut'))
293 [value,~]=me.updatePosition(me.delta,me.angleOut);
294 end
295 end
296
297 % ===================================================================
298 %> @brief dY Get method
299 %> Y position increment for a given delta and angle
300 % ===================================================================
301 function value = get.dY(me)
302 value = 0;
303 if ~isempty(findprop(me,'directionOut'))
304 [~,value]=me.updatePosition(me.delta,me.directionOut);
305 elseif ~isempty(findprop(me,'angleOut'))
306 [~,value]=me.updatePosition(me.delta,me.angleOut);
307 end
308 end
309
310 % ===================================================================
311 %> @brief Method to set isVisible=true.
312 %>
313 % ===================================================================
314 function show(me)
315 me.isVisible = true;
316 end
317
318 % ===================================================================
319 %> @brief Method to set isVisible=false.
320 %>
321 % ===================================================================
322 function hide(me)
323 me.isVisible = false;
324 end
325
326 % ===================================================================
327 %> @brief set offTime
328 %>
329 % ===================================================================
330 function setOffTime(me, time)
331 me.offTime = time;
332 end
333
334 % ===================================================================
335 %> @brief set delayTime
336 %>
337 % ===================================================================
338 function setDelayTime(me, time)
339 me.delayTime = time;
340 end
341
342 % ===================================================================
343 %> @brief reset the various tick counters for our stimulus
344 %>
345 % ===================================================================
346 function resetTicks(me)
347 global mouseTick %#ok<*GVMIS> %shared across all stimuli
348 if max(me.delayTime) > 0 %delay display a number of frames
349 if length(me.delayTime) == 1
350 me.delayTicks = round(me.delayTime/me.screenVals.ifi);
351 elseif length(me.delayTime) == 2
352 time = randi([me.delayTime(1)*1000 me.delayTime(2)*1000])/1000;
353 me.delayTicks = round(time/me.screenVals.ifi);
354 end
355 else
356 me.delayTicks = 0;
357 end
358 if min(me.offTime) < Inf %delay display a number of frames
359 if length(me.offTime) == 1
360 me.offTicks = round(me.offTime/me.screenVals.ifi);
361 elseif length(me.offTime) == 2
362 time = randi([me.offTime(1)*1000 me.offTime(2)*1000])/1000;
363 me.offTicks = round(time/me.screenVals.ifi);
364 end
365 else
366 me.offTicks = Inf;
367 end
368 mouseTick = 0;
369 me.tick = 0;
370 me.drawTick = 0;
371 end
372
373 % ===================================================================
374 %> @brief get mouse position
375 %> we make sure this is only called once per animation tick to
376 %> improve performance and ensure all stimuli that are following
377 %> mouse position have consistent X and Y per frame update
378 %> This sets mouseX and mouseY and mouseValid if mouse is within
379 %> PTB screen (useful for mouse override positioning for stimuli)
380 % ===================================================================
381 function getMousePosition(me)
382 global mouseTick mouseGlobalX mouseGlobalY mouseValid
383 if me.tick > mouseTick
384 if ~isempty(me.sM) && isa(me.sM,'screenManager') && me.sM.isOpen
385 [me.mouseX, me.mouseY] = GetMouse(me.sM.win);
386 else
387 [me.mouseX, me.mouseY] = GetMouse;
388 end
389 if me.mouseX > -1 && me.mouseY > -1
390 me.mouseValid = true;
391 else
392 me.mouseValid = false;
393 end
394 mouseTick = me.tick; %set global so no other object with same tick number can call this again
395 mouseValid = me.mouseValid;
396 mouseGlobalX = me.mouseX; mouseGlobalY = me.mouseY;
397 else
398 if ~isempty(mouseGlobalX) && ~isempty(mouseGlobalY)
399 me.mouseX = mouseGlobalX; me.mouseY = mouseGlobalY;
400 me.mouseValid = mouseValid;
401 end
402 end
403 end
404
405 % ===================================================================
406 %> @fn run
407 %> @brief Run stimulus in a window to preview it
408 %>
409 %> @param benchmark true|false [optional, default = false]
410 %> @param runtime time to show stimulus [optional, default = 2]
411 %> @param screenManager to use [optional]
412 %> @param forceScreen for a particulr monitor/screen to use
413 %> @param showVBL show a plot of the VBL times
414 % ===================================================================
415 function run(me, benchmark, runtime, s, forceScreen, showVBL)
416 arguments(Input)
417 me %self
418 benchmark logical = false
419 runtime double = 2
420 s = []
421 forceScreen double = -1
422 showVBL logical = false
423 end
424
425 if isempty(benchmark); benchmark = false; end
426 if isempty(runtime); runtime = 2; end
427 if isempty(forceScreen); forceScreen = -1; end
428 if isempty(showVBL); showVBL = false; end
429
430 try
431
432 if ~isa(s,'screenManager')
433 if isempty(me.sM); me.sM=screenManager; end
434 s = me.sM;
435 s.blend = true;
436 s.disableSyncTests = true;
437 s.visualDebug = true;
438 s.bitDepth = '8bit';
439 end
440
441 oldscreen = s.screen;
442 oldbitdepth = s.bitDepth;
443 oldwindowed = s.windowed;
444 if forceScreen >= 0
445 s.screen = forceScreen;
446 if forceScreen == 0
447 s.bitDepth = '8bit';
448 end
449 end
450 prepareScreen(s);
451
452 if benchmark
453 s.windowed = false;
454 elseif forceScreen > -1
455 if ~isempty(s.windowed) && (length(s.windowed) == 2 || length(s.windowed) == 4)
456 % use existing setting
457 else
458 s.windowed = [0 0 s.screenVals.screenWidth/2 s.screenVals.screenHeight/2]; %half of screen
459 end
460 end
461
462 if me.verbose; s.debug = true; end
463
464 if ~s.isOpen; open(s);end
465 sv = s.screenVals;
466 setup(me,s); %setup our stimulus object
467
468 Priority(MaxPriority(s.win)); %bump our priority to maximum allowed
469
470 if ~any(strcmpi(me.family,{'movie','revcor'})); draw(me); resetTicks(me); end
471 if benchmark
472 drawText(s, 'BENCHMARK: screen won''t update properly, see FPS in command window at end.');
473 else
474 drawGrid(s); %draw degree dot grid
475 drawScreenCenter(s);
476 drawText(s, ['Preview ALL with grid = ±1°; static for 1 seconds, then animate for ' num2str(runtime) ' seconds...'])
477 end
478 if ismethod(me,'resetLog'); resetLog(me); end
479 flip(s);
480 if ~any(strcmpi(me.family,{'movie','revcor'})); update(me); end
481 if benchmark
482 WaitSecs('YieldSecs',0.25);
483 else
484 WaitSecs('YieldSecs',2);
485 end
486 if runtime < sv.ifi; runtime = sv.ifi; end
487 nFrames = 0;
488 notFinished = true;
489 benchmarkFrames = floor(sv.fps * runtime);
490 vbl = zeros(benchmarkFrames+1,1);
491 startT = GetSecs; lastvbl = startT;
492 while notFinished
493 nFrames = nFrames + 1;
494 draw(me); %draw stimulus
495 if ~benchmark && s.debug; drawGrid(s); end
496 finishDrawing(s); %tell PTB/GPU to draw
497 animate(me); %animate stimulus, will be seen on next draw
498 if benchmark
499 Screen('Flip',s.win,0,2,2);
500 notFinished = nFrames < benchmarkFrames;
501 else
502 vbl(nFrames) = flip(s, lastvbl + sv.halfifi); %flip the buffer
503 lastvbl = vbl(nFrames);
504 % the calculation needs to take into account the
505 % first and last frame times, so we subtract ifi*2
506 notFinished = lastvbl < ( vbl(1) + ( runtime - (sv.ifi * 2) ) );
507 end
508 end
509 endT = flip(s);
510 if ~benchmark;startT = vbl(1);end
511 diffT = endT - startT;
512 WaitSecs(0.5);
513 vbl = vbl(1:nFrames);
514 if showVBL && ~benchmark
515 figure;
516 plot(diff(vbl)*1e3,'k*');
517 line([0 length(vbl)-1],[sv.ifi*1e3 sv.ifi*1e3],'Color',[0 0 0]);
518 title(sprintf('VBL Times, should be ~%.4f ms',sv.ifi*1e3));
519 ylabel('Time (ms)')
520 xlabel('Frame #')
521 end
522 Priority(0); ShowCursor; ListenChar(0);
523 reset(me); %reset our stimulus ready for use again
524 close(s); %close screen
525 s.screen = oldscreen;
526 s.windowed = oldwindowed;
527 s.bitDepth = oldbitdepth;
528 fps = nFrames / diffT;
529 fprintf('\n\n======>>> Stimulus: %s\n',me.fullName);
530 fprintf('======>>> <strong>SPEED</strong> (%i frames in %.3f secs) = <strong>%g</strong> fps\n\n',nFrames, diffT, fps);
531 if ~benchmark;fprintf('\b======>>> First - Last frame time: %.3f\n\n',vbl(end)-startT);end
532 clear s fps benchmark runtime b bb i vbl; %clear up a bit
533 catch ERR
534 try getReport(ERR); end
535 try Priority(0); end
536 if exist('s','var') && isa(s,'screenManager')
537 try close(s); end
538 end
539 clear fps benchmark runtime b bb i; %clear up a bit
540 reset(me); %reset our stimulus ready for use again
541 rethrow(ERR)
542 end
543 end
544
545 % ===================================================================
546 %> @brief make a GUI properties panel for this object
547 %>
548 % ===================================================================
549 function handles = makePanel(me, parent)
550 if ~isempty(me.handles) && isfield(me.handles, 'root') && isa(me.handles.root,'matlab.ui.container.Panel')
551 fprintf('---> Panel already open for %s\n', me.fullName);
552 return
553 end
554
555 handles = [];
556 setPaths(me); % refresh our paths for the current machine
557 if isempty(me.sansFont); getFonts(me); end
558
559 if ~exist('parent','var')
560 parent = uifigure('Tag','gFig',...
561 'Name', [me.fullName 'Properties'], ...
562 'Position', [ 10 10 800 500 ],...
563 'MenuBar', 'none', ...
564 'CloseRequestFcn', @me.closePanel, ...
565 'NumberTitle', 'off');
566 me.handles(1).parent = parent;
567 handles(1).parent = parent;
568 end
569
570 bgcolor = [0.95 0.95 0.95];
571 bgcoloredit = [1 1 1];
572 fsmall = 11;
573 fmed = 12;
574 handles.root = uipanel('Parent', parent,...
575 'Units', 'normalized',...
576 'Position', [0 0 1 1],...
577 'Title', me.fullName,...
578 'TitlePosition','centertop',...
579 'FontName', me.sansFont,...
580 'FontSize', fmed,...
581 'FontAngle', 'italic',...
582 'BackgroundColor', [0.94 0.94 0.94]);
583 handles.grid = uigridlayout(handles.root,[1 3],'ColumnSpacing',2,'RowSpacing',2,'Padding',[2 2 2 2],'BackgroundColor',bgcolor);
584 handles.grid1 = uigridlayout(handles.grid,'ColumnSpacing',2,'RowSpacing',2,'Padding',[2 2 2 2],'BackgroundColor',bgcolor);
585 handles.grid2 = uigridlayout(handles.grid,'ColumnSpacing',2,'RowSpacing',2,'Padding',[2 2 2 2],'BackgroundColor',bgcolor);
586 handles.grid.ColumnWidth = {'1x','1x',130};
587 handles.grid1.ColumnWidth = {'2x','1x'};
588 handles.grid2.ColumnWidth = {'2x','1x'};
589
590 idx = {'handles.grid1','handles.grid2','handles.grid3'};
591
592 disableList = 'fullName';
593
594 mc = metaclass(me);
595 pl = string({mc.PropertyList.Name});
596 d1 = {mc.PropertyList.Description};
597 d2 = {mc.PropertyList.DetailedDescription};
598 for i = 1:length(d1)
599 a = regexprep(d1{i},'^\s*>\s*','');
600 a = regexprep(a,'''','`');
601 b = d2{i};
602 if isempty(b)
603 dl{i} = string(a);
604 else
605 b = strsplit(b,'\n');
606 for j = 1:length(b)
607 b{j} = regexprep(b{j},'^\s*>\s*','');
608 b{j} = regexprep(b{j},'''','`');
609 end
610 dl{i} = string([{a} b(:)']);
611 end
612 end
613 pr = findAttributesandType(me,'SetAccess','public','notlogical');
614 pr = sort(pr);
615 igA = {}; igB = {};
616 val = findPropertyDefault(me,'ignorePropertiesUI');
617 if ~isempty(val)
618 igA = val;
619 elseif isprop(me,'ignorePropertiesUI')
620 igA = me.ignorePropertiesUI;
621 end
622 if isprop(me,'ignorePropertiesUIBase'); igB = findPropertyDefault(me,'ignorePropertiesUIBase'); end
623 if ischar(igA);igA = strsplit(igA,'|');end
624 if ischar(igB); igB = {'animator','fullName','mvRect','xFinal','yFinal','xFinalD','yFinalD'}; end
625 excl = [igA igB];
626 eidx = [];
627 for i = 1:length(pr)
628 if matches(pr{i}, excl)
629 eidx = [eidx i];
630 end
631 end
632 pr(eidx) = [];
633 lp = ceil(length(pr)/2);
634
635 pr2 = findAttributesandType(me,'SetAccess','public','logical');
636 pr2 = sort(pr2);
637 eidx = [];
638 for i = 1:length(pr2)
639 if matches(pr2{i},excl)
640 eidx = [eidx i];
641 end
642 end
643 pr2(eidx) = [];
644 lp2 = length(pr2);
645 if lp2 > 0; handles.grid3 = uigridlayout(handles.grid,[lp2 1],'Padding',[1 1 1 1],'BackgroundColor',bgcolor); end
646
647 for i = 1:2
648 for j = 1:lp
649 cur = lp*(i-1)+j;
650 if cur <= length(pr)
651 nm = pr{cur};
652 val = me.(nm);
653 % this gets descriptions
654 ix = find(pl == nm);
655 if ~isempty(ix)
656 desc = dl{ix};
657 else
658 desc = nm;
659 end
660 if ischar(val)
661 if isprop(me,[nm 'List'])
662 if strcmp(me.([nm 'List']),'filerequestor')
663 val = regexprep(val,'\s+',' ');
664 handles.([nm '_char']) = uieditfield(...
665 'Parent',eval(idx{i}),...
666 'Tag',[nm '_char'],...
667 'HorizontalAlignment','center',...
668 'ValueChangedFcn',@me.readPanel,...
669 'Value',val,...
670 'FontName',me.monoFont,...
671 'Tooltip', desc, ...
672 'BackgroundColor',bgcoloredit);
673 if ~isempty(regexpi(nm,disableList,'once'))
674 handles.([nm '_char']).Enable = false;
675 end
676 else
677 txt=findPropertyDefault(me,[nm 'List']);
678 if contains(val,txt)
679 handles.([nm '_list']) = uidropdown(...
680 'Parent',eval(idx{i}),...
681 'Tag',[nm '_list'],...
682 'Items',txt,...
683 'ValueChangedFcn',@me.readPanel,...
684 'Value',val,...
685 'Tooltip', desc, ...
686 'BackgroundColor',bgcolor);
687 if ~isempty(regexpi(nm,disableList,'once'))
688 handles.([nm '_list']).Enable = false;
689 end
690 else
691 handles.([nm '_list']) = uidropdown(...
692 'Parent',eval(idx{i}),...
693 'Tag',[nm '_list'],...
694 'Items',txt,...
695 'Tooltip', desc, ...
696 'ValueChangedFcn',@me.readPanel,...
697 'BackgroundColor',bgcolor);
698 end
699 end
700 else
701 val = regexprep(val,'\s+',' ');
702 handles.([nm '_char']) = uieditfield(...
703 'Parent',eval(idx{i}),...
704 'Tag',[nm '_char'],...
705 'HorizontalAlignment','center',...
706 'ValueChangedFcn',@me.readPanel,...
707 'Value',val,...
708 'Tooltip', desc, ...
709 'BackgroundColor',bgcoloredit);
710 if ~isempty(regexpi(nm,disableList,'once'))
711 handles.([nm '_char']).Enable = false;
712 end
713 end
714 elseif isnumeric(val)
715 val = num2str(val);
716 val = regexprep(val,'\s+',' ');
717 handles.([nm '_num']) = uieditfield('text',...
718 'Parent',eval(idx{i}),...
719 'Tag',[nm '_num'],...
720 'HorizontalAlignment','center',...
721 'Value',val,...
722 'Tooltip', desc, ...
723 'ValueChangedFcn',@me.readPanel,...
724 'FontName',me.monoFont,...
725 'BackgroundColor',bgcoloredit);
726 if ~isempty(regexpi(nm,disableList,'once'))
727 handles.([nm '_num']).Enable = false;
728 end
729 else
730 uilabel('Parent',eval(idx{i}),'Text','','BackgroundColor',bgcolor,'Enable','off');
731 end
732 if isprop(me,[nm 'List'])
733 if strcmp(me.([nm 'List']),'filerequestor')
734 handles.([nm '_button']) = uibutton(...
735 'Parent',eval(idx{i}),...
736 'HorizontalAlignment','left',...
737 'Text','Select file...',...
738 'FontName',me.sansFont,...
739 'Tag',[nm '_button'],...
740 'Tooltip', desc, ...
741 'Icon', [me.paths.root '/ui/images/edit.svg'],...
742 'ButtonPushedFcn', @me.selectFilePanel,...
743 'FontSize', 8);
744 else
745 uilabel(...
746 'Parent',eval(idx{i}),...
747 'HorizontalAlignment','left',...
748 'Text',nm,...
749 'FontName',me.sansFont,...
750 'FontSize', fsmall,...
751 'BackgroundColor',bgcolor);
752 end
753 else
754 uilabel(...
755 'Parent',eval(idx{i}),...
756 'HorizontalAlignment','left',...
757 'Text',nm,...
758 'FontName',me.sansFont,...
759 'FontSize', fsmall,...
760 'BackgroundColor',bgcolor);
761 end
762 else
763 uilabel('Parent',eval(idx{i}),'Text','','BackgroundColor',bgcolor,'Enable','off');
764 end
765 end
766 end
767 for j = 1:lp2
768 nm = pr2{j};
769 val = me.(nm);
770 % this gets descriptions
771 ix = find(pl == nm);
772 if ~isempty(ix)
773 desc = dl{ix};
774 else
775 desc = nm;
776 end
777 if j <= length(pr2)
778 handles.([nm '_bool']) = uicheckbox(...
779 'Parent',eval(idx{end}),...
780 'Tag',[nm '_bool'],...
781 'Text',nm,...
782 'Tooltip', desc, ...
783 'FontName',me.sansFont,...
784 'FontSize', fsmall,...
785 'ValueChangedFcn',@me.readPanel,...
786 'Value',val);
787 end
788 end
789 %handles.readButton = uibutton(...
790 % 'Parent',eval(idx{end}),...
791 % 'Tag','readButton',...%'Callback',@me.readPanel,...
792 % 'Text','Update');
793 me.handles = handles;
794 me.isGUI = true;
795 end
796
797 % ===================================================================
798 %> @brief read values from a GUI properties panel for this object
799 %>
800 % ===================================================================
801 function selectFilePanel(me,varargin)
802 if nargin > 0
803 hin = varargin{1};
804 if ishandle(hin)
805 [f,p] = uigetfile('*.*','Select File:');
806 re = regexp(get(hin,'Tag'),'(.+)_button','tokens','once');
807 hout = me.handles.([re{1} '_char']);
808 if ishandle(hout)
809 set(hout,'Value', [p f]);
810 me.readPanel(hout);
811 end
812 end
813 end
814 end
815
816 % ===================================================================
817 %> @brief read values from a GUI properties panel for this object
818 %>
819 % ===================================================================
820 function readPanel(me,varargin)
821 if isempty(me.handles) || ~(isfield(me.handles, 'root') && isa(me.handles.root,'matlab.ui.container.Panel'))
822 return
823 end
824 if isempty(varargin) || isempty(varargin{1}); return; end
825 source = varargin{1};
826 tag = source.Tag;
827 if isempty(tag); return; end
828 tagName = regexprep(tag,'_.+$','');
829 tagType = regexprep(tag,'^.+_','');
830
831 pList = findAttributes(me,'SetAccess','public'); %our public properties
832
833 if ~any(contains(pList,tagName)); return; end
834
835 switch tagType
836 case 'list'
837 me.(tagName) = source.Value;
838 case 'bool'
839 me.(tagName) = logical(source.Value);
840 case 'num'
841 me.(tagName) = str2num(source.Value);
842 case 'char'
843 me.(tagName) = source.Value;
844 otherwise
845 warning('Can''t set property');
846 end
847
848 if strcmpi(tagName,'name')
849 me.handles.fullName_char.Value = me.fullName;
850 me.handles.root.Title = me.fullName;
851 end
852
853 if strcmpi(tagName,'alpha')
854 me.handles.colour_num.Value = num2str(me.colour, '%g ');
855 if isprop(me,'colour2')
856 me.handles.colour2_num.Value = num2str(me.colour2, '%g ');
857 end
858 if isprop(me,'baseColour')
859 me.handles.baseColour_num.Value = num2str(me.baseColour, '%g ');
860 end
861 end
862
863 if strcmpi(tagName,'alpha2')
864 if isprop(me,'colour2');me.handles.colour2_num.Value = num2str(me.colour2, '%g ');end
865 end
866
867 if strcmpi(tagName,'colour')
868 me.handles.alpha_num.Value = num2str(me.alpha, '%g ');
869 if isprop(me,'correctBaseColour') && me.correctBaseColour
870 me.handles.baseColour_num.Value = num2str(me.baseColour, '%g ');
871 end
872 end
873
874 if strcmpi(tagName,'colour2')
875 if isprop(me,'alpha2');me.handles.alpha2_num.Value = num2str(me.alpha2, '%g ');end
876 if isprop(me,'correctBaseColour') && me.correctBaseColour
877 me.handles.baseColour_num.Value = num2str(me.baseColour, '%g ');
878 end
879 end
880
881 if strcmpi(tagName,'filePath') && isa(me,'imageStimulus')
882 me.checkfilePath();
883 end
884
885 notify(me,'readPanelUpdate');
886 end
887
888 % ===================================================================
889 %> @brief show GUI properties panel for this object
890 %>
891 % ===================================================================
892 function showPanel(me)
893 if isempty(me.handles)
894 return
895 end
896 set(me.handles.root,'Enable','on');
897 set(me.handles.root,'Visible','on');
898 end
899
900 % ===================================================================
901 %> @brief hide GUI properties panel for this object
902 %>
903 % ===================================================================
904 function hidePanel(me)
905 if isempty(me.handles)
906 return
907 end
908 set(me.handles.root,'Enable','off');
909 set(me.handles.root,'Visible','off');
910 end
911
912 % ===================================================================
913 %> @brief close GUI panel for this object
914 %>
915 % ===================================================================
916 function closePanel(me,varargin)
917 if isfield(me.handles,'root') && isgraphics(me.handles.root)
918 delete(me.handles.root);
919 end
920 if isfield(me.handles,'parent') && isgraphics(me.handles.parent,'figure')
921 delete(me.handles.parent)
922 end
923 me.cleanHandles();
924 me.isGUI = false;
925 end
926
927 % ===================================================================
928 %> @fn cleanHandles
929 %> @brief clean any handles
930 %>
931 %> @param
932 %> @return
933 % ===================================================================
934 function cleanHandles(me, ~)
935 if isprop(me,'handles')
936 me.handles = [];
937 end
938 if isprop(me,'h')
939 me.h = [];
940 end
941 fprintf('===>>> baseStimulus: Ran clean handles!!!\n')
942 me.isGUI = false;
943 end
944
945 % ===================================================================
946 %> @fn getP
947 %> @brief gets a property copy or original property
948 %>
949 %> When stimuli are run, their properties are copied, so e.g. angle
950 %> is copied to angleOut and this is used during the task. This
951 %> method checks if the copy is available and returns that, otherwise
952 %> return the original.
953 %>
954 %> @param name of property
955 %> @param range of property to return
956 %> @return value of property
957 % ===================================================================
958 function [value, nameOut] = getP(me, name, range)
959 arguments(Input)
960 me
961 name char
962 range = []
963 end
964 arguments(Output)
965 value
966 nameOut char
967 end
968 value = []; nameOut = [];
969 if isprop(me, [name 'Out']) && ~isempty(me.([name 'Out']))
970 nameOut = [name 'Out'];
971 value = me.(nameOut);
972 if ~isempty(range); value = value(range); end
973 elseif isprop(me, name)
974 value = me.(name);
975 nameOut = name;
976 if ~isempty(range); value = value(range); end
977 elseif me.verbose
978 fprintf('!!! Property %s doesn''t exist...\n',name)
979 end
980 end
981
982 % ===================================================================
983 %> @fn setP
984 %> @brief sets a property copy or original property
985 %>
986 %> When stimuli are run, their properties are copied, so e.g. angle
987 %> is copied to angleOut and this is used during the task. This
988 %> method checks if the copy is available and returns that, otherwise
989 %> return the original.
990 %>
991 %> @param name of property
992 %> @param range of property to return
993 %> @return value of property
994 % ===================================================================
995 function setP(me, name, value)
996 arguments(Input)
997 me
998 name char
999 value
1000 end
1001 if isprop(me,[name 'Out'])
1002 me.([name 'Out']) = value;
1003 elseif isprop(me, name)
1004 me.(name) = value;
1005 elseif me.verbose
1006 fprintf('!!! Property %s doesn''t exist...\n',name);
1007 end
1008 end
1009
1010 % ===================================================================
1011 %> @fn updateXY
1012 %> @brief Update only position info, faster and doesn't reset image etc.
1013 %>
1014 %> @param x X position
1015 %> @param y Y position
1016 %> @param useDegrees where the input is in degrees (true) or pixels (false)
1017 % ===================================================================
1018 function updateXY(me,x,y,useDegrees)
1019 arguments(Input)
1020 me
1021 x double = NaN
1022 y double = NaN
1023 useDegrees logical = false
1024 end
1025 if ~me.isSetup; warning("updateXY: Need to setup stimulus first!"); return; end
1026 if isnan(x) || isnan(y); return; end
1027 if ~isempty(x) && ~isscalar(x); x = x(1); end; if ~isempty(y) && ~isscalar(y); y = y(1); end
1028 if useDegrees
1029 if ~isempty(x); me.xFinal = me.sM.toPixels(x, 'x'); me.xFinalD = x; me.xPositionOut = x; end
1030 if ~isempty(y); me.yFinal = me.sM.toPixels(y, 'y'); me.yFinalD = y; me.yPositionOut = y; end
1031 else
1032 if ~isempty(x); me.xFinal = x; me.xFinalD = me.sM.toDegrees(x, 'x'); me.xPositionOut = me.xFinalD; end
1033 if ~isempty(y); me.yFinal = y; me.yFinalD = me.sM.toDegrees(y, 'y'); me.yPositionOut = me.yFinalD; end
1034 end
1035 if me.isRect && length(me.mvRect) == 4
1036 me.mvRect=CenterRectOnPointd(me.mvRect, me.xFinal, me.yFinal);
1037 end
1038 end
1039
1040 end %---END PUBLIC METHODS---%
1041
1042 %=======================================================================
1043 methods ( Static ) %----------STATIC METHODS
1044 %=======================================================================
1045
1046 % ===================================================================
1047 %> @brief linear interpolation between two colour arrays based on a contrast
1048 %>
1049 % ===================================================================
1050 function out = mixColour(c1, c2, contrast)
1051 arguments(Input)
1052 c1 (1,3) double
1053 c2 (1,3) double
1054 contrast (1,1) double {mustBeNonnegative, mustBeLessThanOrEqual(contrast,1)}
1055 end
1056 out = c1(1:3) * (1 - contrast) + c2(1:3) * contrast;
1057 end
1058
1059 % ===================================================================
1060 %> @brief degrees2radians
1061 %>
1062 % ===================================================================
1063 function r = d2r(degrees)
1064 % d2r(degrees)
1065 r=degrees*(pi/180);
1066 end
1067
1068 % ===================================================================
1069 %> @brief radians2degrees
1070 %>
1071 % ===================================================================
1072 function degrees = r2d(r)
1073 % r2d(radians)
1074 degrees=r*(180/pi);
1075 end
1076
1077 % ===================================================================
1078 %> @brief findDistance in X and Y coordinates
1079 %>
1080 % ===================================================================
1081 function distance = findDistance(x1, y1, x2, y2)
1082 % findDistance(x1, y1, x2, y2)
1083 distance=sqrt((x2 - x1)^2 + (y2 - y1)^2);
1084 end
1085
1086 % ===================================================================
1087 %> @brief updatePosition returns dX and dY given an angle and delta
1088 %>
1089 % ===================================================================
1090 function [dX, dY] = updatePosition(delta, angle)
1092 dX = delta .* cos(baseStimulus.d2r(angle));
1093 if length(dX)== 1 && abs(dX) < 1e-3; dX = 0; end
1094 dY = delta .* sin(baseStimulus.d2r(angle));
1095 if length(dY)==1 && abs(dY) < 1e-3; dY = 0; end
1096 end
1097
1098 end%---END STATIC METHODS---%
1099
1100 %=======================================================================
1101 methods ( Access = protected ) %-------PRIVATE (protected) METHODS-----%
1102 %=======================================================================
1103
1104 % ===================================================================
1106 %> @brief These are transient properties that specify actions during runtime
1107 % ===================================================================
1108 function addRuntimeProperties(me)
1109 if isempty(me.findprop('doFlash')); me.addprop('doFlash');end
1110 if isempty(me.findprop('doDots')); me.addprop('doDots');end
1111 if isempty(me.findprop('doMotion')); me.addprop('doMotion');end
1112 if isempty(me.findprop('doDrift')); me.addprop('doDrift');end
1113 if isempty(me.findprop('doAnimator')); me.addprop('doAnimator');end
1115 end
1116
1117 % ===================================================================
1119 %> @brief Update transient properties that specify actions during runtime
1120 % ===================================================================
1121 function updateRuntimeProperties(me)
1122 me.doDots = false;
1123 me.doMotion = false;
1124 me.doDrift = false;
1125 me.doFlash = false;
1126 me.doAnimator = false;
1127
1128 if isprop(me, 'tf')
1129 [v,n] = getP(me,'tf');
1130 if ~isempty(n) && v > 0; me.doDrift = true; end
1131 end
1132
1133 if isprop(me, 'speed')
1134 [v,n] = getP(me,'speed');
1135 if ~isempty(n) && v > 0; me.doMotion = true; end
1136 end
1137
1138 if strcmpi(me.family,'dots'); me.doDots = true; end
1139
1140 if strcmpi(me.type,'flash'); me.doFlash = true; end
1141
1142 if ~isempty(me.animator) && isa(me.animator,'animationManager')
1143 me.doAnimator = true;
1144 end
1145 end
1146
1147 % ===================================================================
1148 %> @brief compute xFinal and yFinal (in pixels) taking startPosition,
1149 %> xPosition, yPosition and direction/angle into account
1150 %>
1151 % ===================================================================
1152 function computePosition(me)
1153 if me.mouseOverride && me.mouseValid
1154 me.xFinal = me.mouseX; me.yFinal = me.mouseY;
1155 else
1156 sP = getP(me, 'startPosition');
1157 if isprop(me,'direction')
1158 [dx, dy]=pol2cart(me.d2r(getP(me,'direction')), sP);
1159 else
1160 [dx, dy]=pol2cart(me.d2r(getP(me,'angle')), sP);
1161 end
1162 me.xFinal = me.xPositionOut + (dx * me.ppd) + me.sM.xCenter;
1163 me.yFinal = me.yPositionOut + (dy * me.ppd) + me.sM.yCenter;
1164 me.xFinalD = me.sM.toDegrees(me.xFinal,'x');
1165 me.yFinalD = me.sM.toDegrees(me.yFinal,'y');
1166 if me.verbose; fprintf('---> computePosition: %s X = %gpx | %gpx | %gdeg <> Y = %gpx | %gpx | %gdeg\n',me.fullName, me.xFinal, me.xPositionOut, dx, me.yFinal, me.yPositionOut, dy); end
1167 end
1169 end
1170
1171 % ===================================================================
1172 %> @fn setAnimationDelta
1173 %> setAnimationDelta for performance better not to use get methods for dX dY and
1174 %> delta during animation, so we have to cache these properties to private copies so that
1175 %> when we call the animate method, it uses the cached versions not the
1176 %> public versions. This method simply copies the properties to their cached
1177 %> equivalents.
1178 % ===================================================================
1179 function setAnimationDelta(me)
1180 me.delta_ = me.delta;
1181 me.dX_ = me.dX;
1182 me.dY_ = me.dY;
1183 end
1184
1185 % ===================================================================
1186 %> @fn setRect
1187 %> setRect makes the PsychRect based on the texture and screen
1188 %> values, you should call computePosition() first to get xFinal and
1189 %> yFinal.
1190 % ===================================================================
1191 function setRect(me)
1192 if isempty(me.texture); me.mvRect = [0 0 100 100]; return; end
1193 if isprop(me,'scale')
1194 me.dstRect = ScaleRect(Screen('Rect',me.texture(1)), me.scale, me.scale);
1195 else
1196 me.dstRect=Screen('Rect',me.texture(1));
1197 end
1198 if me.mouseOverride && me.mouseValid
1199 me.dstRect = CenterRectOnPointd(me.dstRect, me.mouseX, me.mouseY);
1200 else
1201 me.dstRect=CenterRectOnPointd(me.dstRect, me.xFinal, me.yFinal);
1202 end
1203 me.mvRect=me.dstRect;
1204 if me.isRect; me.szPx=RectWidth(me.mvRect); end
1205 if me.verbose
1206 fprintf('---> %s setRect = [%.2f %.2f %.2f %.2f] width = %.2f height = %.2f\n',...
1207 me.fullName, me.dstRect(1), me.dstRect(2),me.dstRect(3),me.dstRect(4),...
1208 RectWidth(me.dstRect),RectHeight(me.dstRect));
1209 end
1210 end
1211
1212 % ===================================================================
1213 %> @fn toStructure
1214 %> @brief Converts properties to a structure
1215 %>
1216 %> @param me this instance object
1217 %> @param tmp is whether to use the temporary or permanent properties
1218 %> @return out the structure
1219 % ===================================================================
1220 function out=toStructure(me,tmp)
1221 if ~exist('tmp','var')
1222 tmp = 0; %copy real properties, not temporary ones
1223 end
1224 fn = fieldnames(me);
1225 for j=1:length(fn)
1226 if tmp == 0
1227 out.(fn{j}) = me.(fn{j});
1228 else
1229 out.(fn{j}) = me.([fn{j} 'Out']);
1230 end
1231 end
1232 end
1233
1234 % ===================================================================
1235 %> @fn removeTmpProperties
1236 %> @brief Finds and removes dynamic properties
1237 %>
1238 %> @param me
1239 %> @return
1240 % ===================================================================
1241 function removeTmpProperties(me)
1242 allprops = properties(me);
1243 for i=1:numel(allprops)
1244 m = findprop(me, allprops{i});
1245 if isa(m,'meta.DynamicProperty')
1246 delete(m)
1247 end
1248 end
1249 me.xFinal = [];
1250 me.xFinalD = [];
1251 me.yFinal = [];
1252 me.yFinalD = [];
1253 me.szPx = [];
1254 me.szD = [];
1255 me.isSetup = false;
1256 end
1257
1258 % ===================================================================
1259 %> @fn Delete method
1260 %>
1261 %> @param me
1262 %> @return
1263 % ===================================================================
1264 function delete(me)
1265 if ~isempty(me.texture)
1266 for i = 1:length(me.texture)
1267 if Screen(me.texture, 'WindowKind')~=0 ;try Screen('Close',me.texture); end; end %#ok<*TRYNC>
1268 end
1269 end
1270 if isprop(me,'buffertex') && ~isempty(me.buffertex)
1271 if Screen(me.buffertex, 'WindowKind')~=0 ; try Screen('Close',me.buffertex); end; end
1272 end
1273
1274 if me.verbose; fprintf('--->>> Delete: %s\n',me.fullName); end
1275 end
1276
1277 end%---END PRIVATE METHODS---%
1278end
ANIMATIONMANAGER Provides per frame paths for stimuli We integrate dyn4j java physics engine for rigi...
Definition animationManager.m:38
baseStimulus is the superclass for all stimulus objects
Definition baseStimulus.m:3
Property delayTime
Definition baseStimulus.m:71
Property speed
Definition baseStimulus.m:63
function computePosition(in me)
compute xFinal and yFinal (in pixels) taking startPosition, xPosition, yPosition and direction/angle ...
Property mouseValid
is mouse position within screen co-ordinates?
Definition baseStimulus.m:187
Property yFinalD
Definition baseStimulus.m:121
Property delta_
delta cache
Definition baseStimulus.m:205
function getMousePosition(in me)
get mouse position we make sure this is only called once per animation tick to improve performance an...
Property sM
our screen manager
Definition baseStimulus.m:165
Property xFinalD
X and Y position in °
Definition baseStimulus.m:119
Property szPx
computed size in pixels
Definition baseStimulus.m:113
function setAnimationDelta(in me)
Property ppd
pixels per degree (normally inhereted from screenManager)
Definition baseStimulus.m:139
Property xFinal
Definition baseStimulus.m:94
function setDelayTime(in me, in time)
set delayTime
static function d2r(in degrees)
degrees2radians
function setRect(in me)
Property ignorePropertiesUIBase
Which properties to not draw in the UI panel.
Definition baseStimulus.m:229
Property isRect
Definition baseStimulus.m:130
function delete(in me)
@ readPanelUpdate
triggered when reading from a UI panel,
Definition baseStimulus.m:245
function resetTicks(in me)
reset the various tick counters for our stimulus
Property colour
Definition baseStimulus.m:50
virtual setup(in runObject)
ALL Children must implement these 5 methods!
function getP(in me, in name, in range)
gets a property copy or original property
function updateRuntimeProperties(in me)
Update transient properties that specify actions during runtime.
Property alpha
Alpha (opacity) [0-1], this gets combined with the RGB colour.
Definition baseStimulus.m:53
virtual animate(in runObject)
animate the stimulus, normally called after a draw
static function findDistance(in x1, in y1, in x2, in y2)
findDistance in X and Y coordinates
virtual draw(in runObject)
draw to the screen buffer, ready for flip()
Property handles
handles for the GUI
Definition baseStimulus.m:162
Property texture
Our texture pointer for texture-based stimuli.
Definition baseStimulus.m:159
function setOffTime(in me, in time)
set offTime
Property verbose
Do we log extra details to the command-line?
Definition baseStimulus.m:86
function makePanel(in me, in parent)
make a GUI properties panel for this object
Property isVisible
true or false, whether to draw() this object
Definition baseStimulus.m:77
Property yFinal
Definition baseStimulus.m:98
Property type
stimulus type
Definition baseStimulus.m:23
static function r2d(in r)
radians2degrees
Property animator
animation manager
Definition baseStimulus.m:168
Property startPosition
Definition baseStimulus.m:59
virtual update(in runObject)
function closePanel(in me, in varargin)
close GUI panel for this object
static function mixColour(in c1, in c2, in contrast)
linear interpolation between two colour arrays based on a contrast
Property yPosition
Definition baseStimulus.m:42
Property screenVals
screen settings generated by sM on setup
Definition baseStimulus.m:171
Property isSetup
Definition baseStimulus.m:174
Property offTime
time to turn stimulus off, relative to stimulus onset
Definition baseStimulus.m:74
Property mvRect
Definition baseStimulus.m:102
Property angle
angle in degrees (0 - 360)
Definition baseStimulus.m:66
Property dY
X update which is computed from our speed and angle.
Definition baseStimulus.m:152
Property szD
computed size in °
Definition baseStimulus.m:116
static function updatePosition(in delta, in angle)
updatePosition returns dX and dY given an angle and delta
function removeTmpProperties(in me)
Finds and removes dynamic properties.
function run(in me, in benchmark, in runtime, in s, in forceScreen, in showVBL)
Run stimulus in a window to preview it.
function updateXY(in me, in x, in y, in useDegrees)
Update only position info, faster and doesn't reset image etc.
Property dX_
dX cache
Definition baseStimulus.m:208
virtual reset(in runObject)
Property mouseX
mouse X position
Definition baseStimulus.m:190
Property delta
What our per-frame motion delta is.
Definition baseStimulus.m:146
function hide(in me)
Method to set isVisible=false.
Property dstRect
initial screen rectangle position [LEFT TOP RIGHT BOTTOM]
Definition baseStimulus.m:184
Property family
the stimulus family (grating, dots etc.)
Definition baseStimulus.m:30
function addRuntimeProperties(in me)
These are transient properties that specify actions during runtime.
Property mouseY
mouse Y position
Definition baseStimulus.m:193
function show(in me)
Method to set isVisible=true.
Property dX
X update which is computed from our speed and angle.
Definition baseStimulus.m:149
Property tick
tick updates +1 on each call of draw (even if delay or off is true and no stimulus is drawn,...
Definition baseStimulus.m:133
Property mouseOverride
override X and Y position with mouse input? Useful for RF mapping
Definition baseStimulus.m:80
Property xPosition
Definition baseStimulus.m:38
Property dY_
dY cache
Definition baseStimulus.m:211
function toStructure(in me, in tmp)
Converts properties to a structure.
Show images or directories full of images.
Definition imageStimulus.m:12
function getFonts(in me)
set paths for object
Property fullName
The fullName is the object name combined with its uuid and class name.
Definition optickaCore.m:73
function setPaths(in me)
Sets properties from a structure or normal arguments pairs, ignores invalid or non-allowed properties...
Property paths
storage of various paths
Definition optickaCore.m:66
Property sansFont
sans font
Definition optickaCore.m:83
Property name
object name
Definition optickaCore.m:33
screenManager — manage opening and configuring the PTB screen
Definition screenManager.m:3

Constructor & Destructor Documentation

◆ baseStimulus()

function baseStimulus::baseStimulus ( in  varargin)

Class constructor.

Parameters
vararginare passed as a structure / cell of properties which is parsed.
Returns
instance of class.

Member Function Documentation

◆ addRuntimeProperties()

baseStimulus::addRuntimeProperties ( in  me)
protected

These are transient properties that specify actions during runtime.

◆ alpha()

function set baseStimulus::alpha ( in  me,
in  value 
)

alpha set method

◆ animate()

◆ cleanHandles()

baseStimulus::cleanHandles ( in  me,
in  ignoredArg 
)

clean any handles

Parameters

return

◆ closePanel()

function baseStimulus::closePanel ( in  me,
in  varargin 
)

close GUI panel for this object

◆ colour()

function set baseStimulus::colour ( in  me,
in  value 
)

colour set method Allow 1 (R=G=B) 3 (RGB) or 4 (RGBA) value colour

◆ computePosition()

function baseStimulus::computePosition ( in  me)
protected

compute xFinal and yFinal (in pixels) taking startPosition, xPosition, yPosition and direction/angle into account

◆ d2r()

static function baseStimulus::d2r ( in  degrees)
static

degrees2radians

◆ delete()

function baseStimulus::delete ( in  me)
protected

◆ delta()

function get baseStimulus::delta ( in  me)

delta Get method delta is the normalised number of pixels per frame to move a stimulus

◆ draw()

◆ dX()

function get baseStimulus::dX ( in  me)

dX Get method X position increment for a given delta and angle

◆ dY()

function get baseStimulus::dY ( in  me)

dY Get method Y position increment for a given delta and angle

◆ findDistance()

static function baseStimulus::findDistance ( in  x1,
in  y1,
in  x2,
in  y2 
)
static

findDistance in X and Y coordinates

◆ getMousePosition()

function baseStimulus::getMousePosition ( in  me)

get mouse position we make sure this is only called once per animation tick to improve performance and ensure all stimuli that are following mouse position have consistent X and Y per frame update This sets mouseX and mouseY and mouseValid if mouse is within PTB screen (useful for mouse override positioning for stimuli)

◆ getP()

baseStimulus::getP ( in  me,
in  name,
in  range 
)

gets a property copy or original property

When stimuli are run, their properties are copied, so e.g. angle is copied to angleOut and this is used during the task. This method checks if the copy is available and returns that, otherwise return the original.

Parameters
nameof property
rangeof property to return
Returns
value of property

◆ hide()

function baseStimulus::hide ( in  me)

Method to set isVisible=false.

◆ hidePanel()

function baseStimulus::hidePanel ( in  me)

hide GUI properties panel for this object

◆ makePanel()

function baseStimulus::makePanel ( in  me,
in  parent 
)

make a GUI properties panel for this object

◆ mixColour()

static function baseStimulus::mixColour ( in  c1,
in  c2,
in  contrast 
)
static

linear interpolation between two colour arrays based on a contrast

◆ r2d()

static function baseStimulus::r2d ( in  r)
static

radians2degrees

◆ readPanel()

function baseStimulus::readPanel ( in  me,
in  varargin 
)

read values from a GUI properties panel for this object

◆ removeTmpProperties()

baseStimulus::removeTmpProperties ( in  me)
protected

Finds and removes dynamic properties.

Parameters
me
Returns

◆ reset()

◆ resetTicks()

function baseStimulus::resetTicks ( in  me)

reset the various tick counters for our stimulus

◆ run()

baseStimulus::run ( in  me,
in  benchmark,
in  runtime,
in  s,
in  forceScreen,
in  showVBL 
)

Run stimulus in a window to preview it.

Parameters
benchmarktrue|false [optional, default = false]
runtimetime to show stimulus [optional, default = 2]
screenManagerto use [optional]
forceScreenfor a particulr monitor/screen to use
showVBLshow a plot of the VBL times

◆ selectFilePanel()

function baseStimulus::selectFilePanel ( in  me,
in  varargin 
)

read values from a GUI properties panel for this object

◆ setAnimationDelta()

baseStimulus::setAnimationDelta ( in  me)
protected

setAnimationDelta for performance better not to use get methods for dX dY and delta during animation, so we have to cache these properties to private copies so that when we call the animate method, it uses the cached versions not the public versions. This method simply copies the properties to their cached equivalents.

◆ setDelayTime()

function baseStimulus::setDelayTime ( in  me,
in  time 
)

set delayTime

◆ setOffTime()

function baseStimulus::setOffTime ( in  me,
in  time 
)

set offTime

◆ setP()

baseStimulus::setP ( in  me,
in  name,
in  value 
)

sets a property copy or original property

When stimuli are run, their properties are copied, so e.g. angle is copied to angleOut and this is used during the task. This method checks if the copy is available and returns that, otherwise return the original.

Parameters
nameof property
rangeof property to return
Returns
value of property

◆ setRect()

baseStimulus::setRect ( in  me)
protected

setRect makes the PsychRect based on the texture and screen values, you should call computePosition() first to get xFinal and yFinal.

◆ setup()

virtual baseStimulus::setup ( in  runObject)
virtual

ALL Children must implement these 5 methods!

initialise the stimulus with the PTB screenManager

Reimplemented in plaidStimulus.

◆ show()

function baseStimulus::show ( in  me)

Method to set isVisible=true.

◆ showPanel()

function baseStimulus::showPanel ( in  me)

show GUI properties panel for this object

◆ toStructure()

baseStimulus::toStructure ( in  me,
in  tmp 
)
protected

Converts properties to a structure.

Parameters
methis instance object
tmpis whether to use the temporary or permanent properties
Returns
out the structure

◆ update()

◆ updatePosition()

static function baseStimulus::updatePosition ( in  delta,
in  angle 
)
static

updatePosition returns dX and dY given an angle and delta

◆ updateRuntimeProperties()

baseStimulus::updateRuntimeProperties ( in  me)
protected

Update transient properties that specify actions during runtime.

◆ updateXY()

baseStimulus::updateXY ( in  me,
in  x,
in  y,
in  useDegrees 
)

Update only position info, faster and doesn't reset image etc.

Parameters
xX position
yY position
useDegreeswhere the input is in degrees (true) or pixels (false)

Member Data Documentation

◆ alpha

Property baseStimulus::alpha

Alpha (opacity) [0-1], this gets combined with the RGB colour.

◆ angle

Property baseStimulus::angle

angle in degrees (0 - 360)

◆ animator

Property baseStimulus::animator
protected

animation manager

◆ colour

Property baseStimulus::colour

Colour as a 0-1 range RGB or RGBA vector (if you pass A it also modifies alpha and visa-versa

◆ delayTicks

Property baseStimulus::delayTicks
protected

delay ticks to wait until display

◆ delayTime

Property baseStimulus::delayTime

delay time to display relative to stimulus onset, can set upper and lower range for random interval. This allows for a group of stimuli some to be delayed relative to others for a global stimulus onset time.

◆ delta

Property baseStimulus::delta

What our per-frame motion delta is.

◆ delta_

Property baseStimulus::delta_
protected

delta cache

◆ drawTick

Property baseStimulus::drawTick

draw tick only updates when a draw is actually performed, resets on each update

◆ dstRect

Property baseStimulus::dstRect
protected

initial screen rectangle position [LEFT TOP RIGHT BOTTOM]

◆ dX

Property baseStimulus::dX

X update which is computed from our speed and angle.

◆ dX_

Property baseStimulus::dX_
protected

dX cache

◆ dY

Property baseStimulus::dY

X update which is computed from our speed and angle.

◆ dY_

Property baseStimulus::dY_
protected

dY cache

◆ family

Property baseStimulus::family
protected

the stimulus family (grating, dots etc.)

◆ handles

Property baseStimulus::handles
protected

handles for the GUI

◆ ignorePropertiesBase

Property baseStimulus::ignorePropertiesBase
protected

Which properties to ignore cloning when making transient copies in setup.

◆ ignorePropertiesUIBase

Property baseStimulus::ignorePropertiesUIBase
protected

Which properties to not draw in the UI panel.

◆ inSetup

Property baseStimulus::inSetup
protected

are we setting up?

◆ isGUI

Property baseStimulus::isGUI
protected

is panel constructed?

◆ isInSetColour

Property baseStimulus::isInSetColour
protected

deal with interaction of colour and alpha

◆ isRect

Property baseStimulus::isRect

is PTB stimulus type position defined via rect [true] or point [false] mvRect is the property used for rect based stimuli xFinal and yFinal are used for point-based positioninng

◆ isSetup

Property baseStimulus::isSetup
protected

◆ isVisible

Property baseStimulus::isVisible

true or false, whether to draw() this object

◆ mouseOverride

Property baseStimulus::mouseOverride

override X and Y position with mouse input? Useful for RF mapping

◆ mouseValid

Property baseStimulus::mouseValid
protected

is mouse position within screen co-ordinates?

◆ mouseX

Property baseStimulus::mouseX
protected

mouse X position

◆ mouseY

Property baseStimulus::mouseY
protected

mouse Y position

◆ mvRect

Property baseStimulus::mvRect

current screen rectangle position [LEFT TOP RIGHT BOTTOM] in pixels

◆ offTicks

Property baseStimulus::offTicks
protected

ticks before stimulus turns off

◆ offTime

Property baseStimulus::offTime

time to turn stimulus off, relative to stimulus onset

◆ ppd

Property baseStimulus::ppd

pixels per degree (normally inhereted from screenManager)

◆ screenVals

Property baseStimulus::screenVals
protected

screen settings generated by sM on setup

◆ setLoop

Property baseStimulus::setLoop
protected

◆ showOnTracker

Property baseStimulus::showOnTracker

show the position on the Eyetracker display?

◆ size

Property baseStimulus::size

Size in visual degrees (°), can also be used to scale images/movies or define width+height of a bar etc.

◆ sM

Property baseStimulus::sM
protected

our screen manager

◆ speed

Property baseStimulus::speed

speed in °/s - this mostly afffects linear motion, but with an animationManager is also used to define initial motion value

◆ startPosition

Property baseStimulus::startPosition

For moving stimuli do we start "before" our initial XY position? This allows you to center a stimulus at a screen location, but then drift it across that location, so if xyPosition is 0,0 and startPosition is -2 then the stimulus will start at -2 drifing towards 0.

◆ szD

Property baseStimulus::szD

computed size in °

◆ szIsPx

Property baseStimulus::szIsPx

whether sizeOut is degrees or pixels, see .szPx .szD for copies in pixels and degrees respectively

◆ szPx

Property baseStimulus::szPx

computed size in pixels

◆ texture

Property baseStimulus::texture
protected

Our texture pointer for texture-based stimuli.

◆ tick

Property baseStimulus::tick

tick updates +1 on each call of draw (even if delay or off is true and no stimulus is drawn, resets on each update

◆ type

Property baseStimulus::type

stimulus type

◆ verbose

Property baseStimulus::verbose

Do we log extra details to the command-line?

◆ xFinal

Property baseStimulus::xFinal

computed X position in pixel coordinates PTB uses: 0,0 top-left see computePosition(); a copy in degrees is made in xFinalD

◆ xFinalD

Property baseStimulus::xFinalD

X and Y position in °

◆ xPosition

Property baseStimulus::xPosition

initial X Position ± visual degrees (°) relative to screen center (0,0) in opticka +X is from left-to-right

◆ yFinal

Property baseStimulus::yFinal

computed Y position in pixel coordinates PTB uses: 0,0 top-left see computePosition(); ; a copy in degrees is made in yFinalD

◆ yFinalD

Property baseStimulus::yFinalD

◆ yPosition

Property baseStimulus::yPosition

initial Y Position ± visual degrees (°) relative to screen center (0,0) in opticka +Y is from top-to-bottom


The documentation for this class was generated from the following file: