function test_suite = test_external_surfing()
% regression tests for external "surfing" toolbox
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
test_functions = localfunctions();
catch % no problem; early Matlab versions can use initTestSuite fine
end
initTestSuite;
function test_surfing_subsample_surface()
if cosmo_skip_test_if_no_external('surfing')
return
end
opt = struct();
opt.progress = false;
vertices = [0 -1 -2 -1 1 2 1 3 4 3
0 -2 0 2 2 0 -2 2 0 -2
0 0 0 0 0 0 0 0 0 0]';
faces = [1 1 1 1 1 1 5 8 6 6
2 3 4 5 6 7 8 9 9 10
3 4 5 6 7 2 6 6 10 7]';
[v_sub, f_sub] = surfing_subsample_surface(vertices, faces, 1, .2, false);
assertEqual(v_sub', [-1 -2 -1 1 2 1 3 4 3
-2 0 2 2 0 -2 2 0 -2
0 0 0 0 0 0 0 0 0]);
assertEqual(f_sub', [1 1 1 1 4 5 7 5
2 3 4 5 7 9 8 8
3 4 5 6 5 6 5 9]);
[v_sub2, f_sub2] = surfing_subsample_surface(v_sub, f_sub, 1, .2, false);
assertEqual(v_sub2', [-1 -2 -1 1 1 3 4 3
-2 0 2 2 -2 2 0 -2
0 0 0 0 0 0 0 0]);
assertEqual(f_sub2', [1 1 1 4 6 4
2 3 4 8 7 7
3 4 5 5 4 8]);
[v_sub2_alt, f_sub2_alt] = surfing_subsample_surface(vertices, faces, ...
2, .2, false);
assertEqual(v_sub2_alt, v_sub2);
assertEqual(f_sub2_alt, f_sub2);
function test_surfing_generate_planar_surface()
if cosmo_skip_test_if_no_external('surfing')
return
end
if isempty(which('surfing_generate_planar_surface'))
% older versions of surfing
cosmo_notify_test_skipped(['surfing_generate_planar_surface '...
'is not available']);
return
end
nx = round(rand() * 5) + 5;
ny = round(rand() * 5) + 5;
origin = rand(3, 1) * 10;
x1 = rand(3, 1) * 10;
y1 = rand(3, 1) * 10;
[v1, f1] = generate_planar_surface(nx, ny, origin, x1, y1);
[v2, f2] = surfing_generate_planar_surface(nx, ny, origin, x1, y1);
assertElementsAlmostEqual(v1, v2);
assertElementsAlmostEqual(f1, f2);
% test default options
[v1, f1] = generate_planar_surface(nx, ny, [0, 0, 0], [1, 0, 0], [0, 1, 0]);
[v2, f2] = surfing_generate_planar_surface(nx, ny);
assertElementsAlmostEqual(v1, v2);
assertElementsAlmostEqual(f1, f2);
function test_surfing_voxel_selection_dijkstra()
helper_test_surfing_voxel_selection('dijkstra');
function test_surfing_voxel_selection_euclidean()
helper_test_surfing_voxel_selection('euclidean');
function helper_test_surfing_voxel_selection(metric)
if cosmo_skip_test_if_no_external('surfing')
return
end
warning_state = warning();
warning_resetter = onCleanup(@()warning(warning_state));
warning('off', 'all');
nx = round(rand() * 5) + 5;
ny = round(rand() * 5) + 5;
% surface with some nodes outside the volume
[v, f] = generate_planar_surface(nx, ny, [-4, -4, 0], [1, 0, 0], [0, 1, 0]);
thickness = rand() * 2 + 2;
pial = bsxfun(@plus, v, [0, 0, -thickness])';
white = bsxfun(@plus, v, [0, 0, thickness])';
ds = cosmo_synthetic_dataset('size', 'big');
ds_xyz = cosmo_vol_coordinates(ds);
voldef = ds.a.vol;
half_vox_size = voldef.mat(1);
thickness_margin = thickness + half_vox_size;
radius = rand() * 4 + 2;
args = {pial, white, f', radius, voldef, [], [], metric, 0};
n2v = surfing_voxelselection(args{:});
switch metric
case 'dijkstra'
extra_distance = 0;
case 'euclidean'
extra_distance = 0;
otherwise
error('illegal distance ''%s''', metric);
end
nv = size(v, 1);
rp = randperm(nv);
for k = 1:nv / 2
node_id = rp(k);
node_xyz = v(node_id, :);
is_inside = ~isequal(cosmo_vol_coordinates(ds, node_xyz'), 0);
voxel_ids = n2v{node_id};
has_voxels = ~isempty(voxel_ids);
assertEqual(is_inside, has_voxels);
if is_inside
voxels_xyz = ds_xyz(:, n2v{node_id})';
voxels_z = voxels_xyz(:, 3);
% check z coordinates
assert(all(-thickness_margin <= voxels_z & ...
voxels_z <= thickness_margin));
distances = euclidean_distance(ds_xyz', node_xyz, 1:3);
node_distances = distances(n2v{node_id});
assert(all(node_distances <= radius + thickness_margin));
% check voxels not selected
nfeatures = size(ds_xyz, 2);
candidates = true(nfeatures, 1);
candidates(ds_xyz(1, :) <= min(v(:, 1))) = false;
candidates(ds_xyz(2, :) <= min(v(:, 2))) = false;
candidates(ds_xyz(3, :) <= -(thickness + half_vox_size)) = false;
candidates(ds_xyz(1, :) >= max(v(:, 1))) = false;
candidates(ds_xyz(2, :) >= max(v(:, 2))) = false;
candidates(ds_xyz(3, :) >= thickness + half_vox_size) = false;
candidates(n2v{node_id}) = false;
if any(candidates)
min_outside = min(distances(candidates));
assert(min_outside > radius - half_vox_size);
end
end
% check individual call for this node id
args_single_node = args;
args_single_node{6} = node_id;
n2v_single_node = surfing_voxelselection(args_single_node{:});
assertEqual(n2v_single_node, n2v(node_id));
end
function d = euclidean_distance(p, q, dim)
d = sqrt(sum(bsxfun(@minus, p(:, dim), q(:, dim)).^2, 2));
function [vertices, faces] = generate_planar_surface(nx, ny, origin, x1, y1)
vertices = zeros(nx * ny, 3);
faces = zeros(2 * (nx - 1) * (ny - 1), 3);
for i = 1:nx
for j = 1:ny
vpos = (i - 1) * ny + j;
vertices(vpos, :) = origin + (i - 1) * x1 + (j - 1) * y1;
if i < nx && j < ny
p = vpos;
q = vpos + 1;
r = vpos + ny;
s = vpos + ny + 1;
fpos = ((i - 1) * (ny - 1) + j) * 2 - 1;
faces(fpos, :) = [p, q, r];
faces(fpos + 1, :) = [s, r, q];
end
end
end