diff --git a/+exp/inferParameters.m b/+exp/inferParameters.m index 6175fd3f..9bb61a08 100644 --- a/+exp/inferParameters.m +++ b/+exp/inferParameters.m @@ -5,11 +5,20 @@ % create some signals just to pass to the definition function and track % which parameter names are used +% if ischar(expdef) && file.exists(expdef) +% expdeffun = fileFunction(expdef); +% else +% expdeffun = expdef; +% expdef = which(func2str(expdef)); +% end if ischar(expdef) && file.exists(expdef) expdeffun = fileFunction(expdef); + [funcDir, mfile] = fileparts(expdef); addpath(funcDir); + funArgs = nargin(str2func(mfile)); else expdeffun = expdef; expdef = which(func2str(expdef)); + funArgs = nargin(expdeffun); end net = sig.Net; @@ -24,7 +33,15 @@ e.outputs = net.subscriptableOrigin('outputs'); try - expdeffun(e.t, e.events, e.pars, e.visual, e.inputs , e.outputs, e.audio); + + rig = 0; + if funArgs == 7 + expdeffun(e.t, e.events, e.pars, e.visual, e.inputs, e.outputs, e.audio); + else + expdeffun(e.t, e.events, e.pars, e.visual, e.inputs, e.outputs, e.audio, rig); + end + +% expdeffun(e.t, e.events, e.pars, e.visual, e.inputs , e.outputs, e.audio); % paramNames will be the strings corresponding to the fields of e.pars % that the user tried to reference in her expdeffun. paramNames = e.pars.Subscripts.keys'; diff --git a/+srv/StimulusControl.m b/+srv/StimulusControl.m index 7e1fde06..9e6f8114 100644 --- a/+srv/StimulusControl.m +++ b/+srv/StimulusControl.m @@ -198,8 +198,8 @@ function onWSReceived(obj, ~, eventArgs) notify(obj, 'ExpStarting', srv.ExpEvent('starting', ref)); case 'completed' %experiment stopped without any exceptions - ref = data{2}; aborted = data{3}; - notify(obj, 'ExpStopped', srv.ExpEvent('completed', ref, aborted)); + ref = data{2}; + notify(obj, 'ExpStopped', srv.ExpEvent('completed', ref)); case 'expException' %experiment stopped with an exception ref = data{2}; err = data{3}; diff --git a/alyx-matlab b/alyx-matlab index b66e4c6f..46a26d60 160000 --- a/alyx-matlab +++ b/alyx-matlab @@ -1 +1 @@ -Subproject commit b66e4c6f9918a0332489010f049c9e0aa8a4e48a +Subproject commit 46a26d603df85dcde04404ede31ea9f51110cc33 diff --git a/cortexlab/+vis/checker6.m b/cortexlab/+vis/checker6.m index 4901b88c..1733d8f7 100644 --- a/cortexlab/+vis/checker6.m +++ b/cortexlab/+vis/checker6.m @@ -1,4 +1,4 @@ -function elem = checker3(t) +function elem = checker6(t) %vis.checker A grid of rectangles % Detailed explanation goes here diff --git a/cortexlab/+vis/checkerLeft.m b/cortexlab/+vis/checkerLeft.m new file mode 100644 index 00000000..cf1122d7 --- /dev/null +++ b/cortexlab/+vis/checkerLeft.m @@ -0,0 +1,126 @@ +function elem = checkerLeft(t) +%vis.checker A grid of rectangles +% Detailed explanation goes here + +elem = t.Node.Net.subscriptableOrigin('checker'); + +%% make initial layers to be used as templates +maskTemplate = vis.emptyLayer(); +maskTemplate.isPeriodic = false; +maskTemplate.interpolation = 'nearest'; +maskTemplate.show = true; +maskTemplate.colourMask = [false false false true]; + +maskTemplate.textureId = 'checkerMaskPixel'; +[maskTemplate.rgba, maskTemplate.rgbaSize] = vis.rgba(0, 0); +maskTemplate.blending = '1-source'; % allows us to lay down our zero alpha value + +stencilTemplate = maskTemplate; +stencilTemplate.textureId = 'checkerStencilPixel'; +[stencilTemplate.rgba, stencilTemplate.rgbaSize] = vis.rgba(1, 1); +stencilTemplate.blending = 'none'; + +% pattern layer uses the alpha values laid down by mask layers +patternLayer = vis.emptyLayer(); +patternLayer.textureId = sprintf('~checker%i', randi(2^32)); +patternLayer.isPeriodic = false; +patternLayer.interpolation = 'nearest'; +patternLayer.blending = 'destination'; % use the alpha mask gets laid down before this + +%% construct signals used to assemble layers +% N rows by cols signal is derived from the size of the pattern array but +% we skip repeats so that pattern changes don't update the mask layers +% unless the size has acutally changed +nRowsByCols = elem.pattern.flatten().map(@size).skipRepeats(); +aziRange = elem.azimuthRange.flatten(); +altRange = elem.altitudeRange.flatten(); +sizeFrac = elem.rectSizeFrac.flatten(); +% signal containing the masking layers +gridMaskLayers = mapn(nRowsByCols, aziRange, altRange, sizeFrac, ... + maskTemplate, stencilTemplate, @gridMask); +% signal contain the checker layer +checkerLayer = scan(elem.pattern.flatten(), @updatePattern,... + elem.colour.flatten(), @updateColour,... + elem.azimuthRange.flatten(), @updateAzi,... + elem.altitudeRange.flatten(), @updateAlt,... + elem.show.flatten(), @updateShow,... + patternLayer); % initial value +%% set default attribute values +elem.layers = [gridMaskLayers checkerLayer]; +elem.azimuthRange = [-135 0]; +elem.altitudeRange = [-37.5 37.5]; +elem.rectSizeFrac = [1 1]; % horizontal and vertical size of each rectangle +elem.pattern = [ + 1 -1 1 -1 + -1 0 0 0 + 1 0 0 0 + -1 1 -1 1]; + elem.show = true; +end + +%% helper functions +function layer = updatePattern(layer, pattern) +% map pattern from -1 -> 1 range to 0->255, cast to 8 bit integers, then +% convert to RGBA texture format. +[layer.rgba, layer.rgbaSize] = vis.rgbaFromUint8(uint8(127.5*(1 + pattern)), 1); +end + +function layer = updateColour(layer, colour) +layer.maxColour = [colour 1]; +end + +function layer = updateAzi(layer, aziRange) +layer.size(1) = abs(diff(aziRange)); +layer.texOffset(1) = mean(aziRange); +end + +function layer = updateAlt(layer, altRange) +layer.size(2) = abs(diff(altRange)); +layer.texOffset(2) = mean(altRange); +end + +function layer = updateShow(layer, show) +layer.show = show; +end + +function layers = gridMask(nRowsByCols, aziRange, altRange, sizeFrac, mask, stencil) +gridDims = [abs(diff(aziRange)) abs(diff(altRange))]; +cellSize = gridDims./flip(nRowsByCols); +nCols = nRowsByCols(2) + 1; +nRows = nRowsByCols(1) + 1; +midAzi = mean(aziRange); +midAlt = mean(altRange); +%% base layer to imprint area the checker can draw on (by applying an alpha mask) +stencil.texOffset = [midAzi midAlt]; +stencil.size = gridDims; +if any(sizeFrac < 1) + %% layers for lines making up mask grid - masks out margins around each square + % make layers for vertical lines + if nCols > 1 + azi = linspace(aziRange(1), aziRange(2), nCols); + else + azi = midAzi; + end + collayers = repmat(mask, 1, nCols); + for vi = 1:nCols + collayers(vi).texOffset = [azi(vi) midAlt]; + end + [collayers.size] = deal([(1 - sizeFrac(1))*cellSize(1) gridDims(2)]); + % make layers for horizontal lines + if nRows > 1 + alt = linspace(altRange(1), altRange(2), nRows); + else + alt = midAlt; + end + rowlayers = repmat(mask, 1, nRows); + for hi = 1:nRows + rowlayers(hi).texOffset = [midAzi alt(hi)]; + end + [rowlayers.size] = deal([gridDims(1) (1 - sizeFrac(2))*cellSize(2)]); + %% combine the layers and return + layers = [stencil collayers rowlayers]; +else % no mask grid needed as each cell is full size + layers = stencil; +end + +end \ No newline at end of file diff --git a/cortexlab/+vis/checkerRight.m b/cortexlab/+vis/checkerRight.m new file mode 100644 index 00000000..78b03545 --- /dev/null +++ b/cortexlab/+vis/checkerRight.m @@ -0,0 +1,126 @@ +function elem = checkerRight(t) +%vis.checker A grid of rectangles +% Detailed explanation goes here + +elem = t.Node.Net.subscriptableOrigin('checker'); + +%% make initial layers to be used as templates +maskTemplate = vis.emptyLayer(); +maskTemplate.isPeriodic = false; +maskTemplate.interpolation = 'nearest'; +maskTemplate.show = true; +maskTemplate.colourMask = [false false false true]; + +maskTemplate.textureId = 'checkerMaskPixel'; +[maskTemplate.rgba, maskTemplate.rgbaSize] = vis.rgba(0, 0); +maskTemplate.blending = '1-source'; % allows us to lay down our zero alpha value + +stencilTemplate = maskTemplate; +stencilTemplate.textureId = 'checkerStencilPixel'; +[stencilTemplate.rgba, stencilTemplate.rgbaSize] = vis.rgba(1, 1); +stencilTemplate.blending = 'none'; + +% pattern layer uses the alpha values laid down by mask layers +patternLayer = vis.emptyLayer(); +patternLayer.textureId = sprintf('~checker%i', randi(2^32)); +patternLayer.isPeriodic = false; +patternLayer.interpolation = 'nearest'; +patternLayer.blending = 'destination'; % use the alpha mask gets laid down before this + +%% construct signals used to assemble layers +% N rows by cols signal is derived from the size of the pattern array but +% we skip repeats so that pattern changes don't update the mask layers +% unless the size has acutally changed +nRowsByCols = elem.pattern.flatten().map(@size).skipRepeats(); +aziRange = elem.azimuthRange.flatten(); +altRange = elem.altitudeRange.flatten(); +sizeFrac = elem.rectSizeFrac.flatten(); +% signal containing the masking layers +gridMaskLayers = mapn(nRowsByCols, aziRange, altRange, sizeFrac, ... + maskTemplate, stencilTemplate, @gridMask); +% signal contain the checker layer +checkerLayer = scan(elem.pattern.flatten(), @updatePattern,... + elem.colour.flatten(), @updateColour,... + elem.azimuthRange.flatten(), @updateAzi,... + elem.altitudeRange.flatten(), @updateAlt,... + elem.show.flatten(), @updateShow,... + patternLayer); % initial value +%% set default attribute values +elem.layers = [gridMaskLayers checkerLayer]; +elem.azimuthRange = [0 135]; +elem.altitudeRange = [-37.5 37.5]; +elem.rectSizeFrac = [1 1]; % horizontal and vertical size of each rectangle +elem.pattern = [ + 1 -1 1 -1 + -1 0 0 0 + 1 0 0 0 + -1 1 -1 1]; + elem.show = true; +end + +%% helper functions +function layer = updatePattern(layer, pattern) +% map pattern from -1 -> 1 range to 0->255, cast to 8 bit integers, then +% convert to RGBA texture format. +[layer.rgba, layer.rgbaSize] = vis.rgbaFromUint8(uint8(127.5*(1 + pattern)), 1); +end + +function layer = updateColour(layer, colour) +layer.maxColour = [colour 1]; +end + +function layer = updateAzi(layer, aziRange) +layer.size(1) = abs(diff(aziRange)); +layer.texOffset(1) = mean(aziRange); +end + +function layer = updateAlt(layer, altRange) +layer.size(2) = abs(diff(altRange)); +layer.texOffset(2) = mean(altRange); +end + +function layer = updateShow(layer, show) +layer.show = show; +end + +function layers = gridMask(nRowsByCols, aziRange, altRange, sizeFrac, mask, stencil) +gridDims = [abs(diff(aziRange)) abs(diff(altRange))]; +cellSize = gridDims./flip(nRowsByCols); +nCols = nRowsByCols(2) + 1; +nRows = nRowsByCols(1) + 1; +midAzi = mean(aziRange); +midAlt = mean(altRange); +%% base layer to imprint area the checker can draw on (by applying an alpha mask) +stencil.texOffset = [midAzi midAlt]; +stencil.size = gridDims; +if any(sizeFrac < 1) + %% layers for lines making up mask grid - masks out margins around each square + % make layers for vertical lines + if nCols > 1 + azi = linspace(aziRange(1), aziRange(2), nCols); + else + azi = midAzi; + end + collayers = repmat(mask, 1, nCols); + for vi = 1:nCols + collayers(vi).texOffset = [azi(vi) midAlt]; + end + [collayers.size] = deal([(1 - sizeFrac(1))*cellSize(1) gridDims(2)]); + % make layers for horizontal lines + if nRows > 1 + alt = linspace(altRange(1), altRange(2), nRows); + else + alt = midAlt; + end + rowlayers = repmat(mask, 1, nRows); + for hi = 1:nRows + rowlayers(hi).texOffset = [midAzi alt(hi)]; + end + [rowlayers.size] = deal([gridDims(1) (1 - sizeFrac(2))*cellSize(2)]); + %% combine the layers and return + layers = [stencil collayers rowlayers]; +else % no mask grid needed as each cell is full size + layers = stencil; +end + +end \ No newline at end of file