function classvisual3d(test_outputs, true_outputs, ...
    train_outputs, train_targets, classnames);
% A 3D plot of the output space in a classification task (with 3 classes).
%
% Training and verification sets are shown, and misclassifications
% are marked with crosses.
%
% (Old function; probably will be replaced with something more
% universally useful at some point.)
%
% A good thing about examples with three target classes is that the raw 
% output of the classification can be shown in a 3D plot that you can
% rotate and zoom. For this plot, we do not care about the input vectors.
%
% TODO: Come to think of it, 4D classification results could be projected
%       into a 3D tetrahedron for visualization?
%
% Inputs:
%
%   test_outputs - classification output of the MLP being verified
%
%   true_outputs    - known true output that the subject_output is to be
%                     compared with. Each of this should be
%                     exactly one of the class codings (for example,
%                     the three different permutations of [1,-1,-1]).
%
%   train_outputs  - classification output of train_input with the subject MLP.
%
%   train_targets  - the target vectors, corresponding to train_input, used
%                    in training the network. Each of this should be
%                    exactly one of the class codings (for example,
%                    the three different permutations of [1,-1,-1]).
%
%   classnames - names of the three classes.
%
figure; 
hold on; 
axis square;

legstr = {};   % legend strings.

% find subsets of interest. The indices shall point to tx for
% training and subx for verification datas:

% train_inclass{ic}: indices of training pairs that correspond to class ic
for ic = 1:3
    train_inclass{ic} = find(train_targets(ic,:)==1);
end

% true_inclass{ic}: indices of test data that SHOULD have gone to class ic
for ic = 1:3
    true_inclass{ic} = find(true_outputs(ic,:)==1);
end

% test_inclass{ic}: indices of test data that ACTUALLY WENT to class ic
strictclasses = classcompete(test_outputs,1,-1);
for ic = 1:3
    test_inclass{ic} = find(strictclasses(ic,:) == 1);
end

% Find out where the test classifications differ from truth:
for ic = 1:3
    test_failed{ic} = find(strictclasses(ic,:) ~= true_outputs(ic,:));
end

% Plot the outputs for both train and test data:
mstr={'g.','r.','b.'};
for ic = 1:3
  plot3([train_outputs(1,train_inclass{ic}), test_outputs(1,true_inclass{ic})], ...
       [train_outputs(2,train_inclass{ic}), test_outputs(2,true_inclass{ic})], ...
       [train_outputs(3,train_inclass{ic}), test_outputs(3,true_inclass{ic})], ...
       mstr{ic}, 'markersize', 6);
  legstr{length(legstr)+1} = classnames{ic};
end

% Re-plot differently the outputs for test data:
mstr={'g^','r^','b^'};
for ic = 1:3
  plot3(test_outputs(1,test_inclass{ic}), ...
        test_outputs(2,test_inclass{ic}), ...
        test_outputs(3,test_inclass{ic}), ...
        mstr{ic}, 'markersize', 6);
  if length(test_inclass{ic})>0
      legstr{length(legstr)+1} = ['classified as ' classnames{ic}];
  end
end

% Hilight wrong classifications of the test data:
conglom = [test_failed{1} test_failed{2} test_failed{3}];
plot3(test_outputs(1,conglom), ...
      test_outputs(2,conglom), ...
      test_outputs(3,conglom), ...
      'kx', 'markersize', 14);
if length(conglom)>0
    legstr{length(legstr)+1} = 'Wrong classification';
end

% Plot and annotate the "pure" class centers:
plot3([1,-1,-1], [-1,1,-1], [-1,-1,1], ...
        'ko', 'markersize', 12)
legstr{length(legstr)+1} = 'In-class codings';

% A default camera position:
set(gca, 'Projection', 'perspective');
set(gca, 'CameraPosition', [25,25,15]);
set(gca, 'CameraTarget', [0,0,-0.25]);
set(gca, 'CameraUpVector', [-1,-1,1]);

% Grid visibility and labels:
set(gca, 'XGrid', 'on', 'YGrid', 'on', 'ZGrid', 'on');
set(get(gca, 'XLabel'), 'String', ['X (' classnames{1} ')']);
set(get(gca, 'YLabel'), 'String', ['Y (' classnames{2} ')']);
set(get(gca, 'ZLabel'), 'String', ['Z (' classnames{3} ')']);

% Legend box as collected above:
legend(legstr);
