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