function [collisionXYZ,rValue,normal] = intersect(boundaryObject,varargin)
%INTERSECT Overloaded intersect command for boundary objects.
% IMPACTXYZ = INTERSECT(OBJ,SEGMENT) computes the 1-by-3 point of
% intersection IMPACTXYZ between boundary object OBJ and a line segment
% SEGMENT. OBJ should be a scalar and SEGMENT should be a 2-by-3 matrix
% where each row is the [x y z] coordinate of an end point of the line
% segment. IMPACTXYZ = [] when either no intersection is found or in the
% degenerate case when the line segment lies exactly in the plane of a
% triangle. The latter case should be addressed in a future update.
%
% IMPACTXYZ = INTERSECT(OBJ,POSITION,VECTOR) represents the line segment
% by a 1-by-3 starting position POSITION and a 1-by-3 vector VECTOR. With
% respect to the above syntax, SEGMENT = [POSITION; POSITION+VECTOR].
%
% IMPACTXYZ = INTERSECT(...,OPTION) is used to specify the behavior when
% more than one intersection with the surface is found for a line
% segment. OPTION can be '-first' (the default), '-last', or '-all', and
% will return, respectively, either the first intersection (closest to
% SEGMENT(1,:) or POSITION), the last intersection (closest to
% SEGMENT(2,:) or POSITION+VECTOR), or all intersections (in which case
% IMPACTXYZ will be an M-by-3 matrix, where M is the number of detected
% intersections).
%
% IMPACTXYZ = INTERSECT(OBJ,POSITION,VECTOR,...), where POSITION and
% VECTOR are both N-by-3 matrices, will return an N-by-1 cell array for
% IMPACTXYZ where each cell contains the M-by-3 intersection point(s) (or
% []) for the line defined by each row pairing of POSITION and VECTOR. If
% one of either POSITION or VECTOR is 1-by-3, it will be replicated to
% match the size of the other.
%
% [IMPACTXYZ,RVALUE,NORMAL] = INTERSECT(...) returns the intersection
% point along with a parameter RVALUE and the normal of the rendered
% surface at the point of intersection NORMAL. RVALUE is a parameter
% between 0 and 1 inclusive and represents the distance along the line
% segment where the intersection occurred. With respect to the above
% syntax, IMPACTXYZ = POSITION + RVALUE.*VECTOR. If OPTION is chosen to
% return multiple intersections then IMPACTXYZ and NORMAL will be M-by-3
% matrices and RVALUE will be an M-by-1 vector. For the POSITION/VECTOR
% syntax, if one or both of POSITION and VECTOR are N-by-3 then each
% output will be an N-by-1 cell array.
%
% Example: Plot line segment, intersection point, and surface normal.
%
% [X,Y] = meshgrid(1:49);
% Z = peaks;
% hSurface = boundary(gca,'XData',X,'YData',Y,'ZData',Z);
% hold on;
% axis equal;
% position = [10.*rand(1,2)+20 -10];
% vector = [0 0 20];
% [pointXYZ,rValue,normal] = intersect(hSurface,position,vector);
% segment = [position; position+vector];
% line(segment(:,1),segment(:,2),segment(:,3),'Color','r');
% plot3(pointXYZ(1),pointXYZ(2),pointXYZ(3),'r*');
% segment = [pointXYZ; pointXYZ+5.*normal];
% line(segment(:,1),segment(:,2),segment(:,3),'Color','b');
%
% See also boundary
% Author: Ken Eaton
% Last modified: 11/13/08
%--------------------------------------------------------------------------
% Initializations:
parameters = struct('position',[],'vector',[],'option','-first');
inputList = varargin;
% Parse input argument list:
try
switch nargin,
%====================================================================
case {0 1}, % Not enough input arguments
error('boundary:intersect:notEnoughInputs',...
'Not enough input arguments.');
%====================================================================
case 2, % Input should be in line segment format
check_line_segment_input;
%====================================================================
case 3, % Input should be in line segment format with an option, or
% position/vector format
option = inputList{2};
if ischar(option),
check_line_segment_input;
check_option_input;
else
check_position_vector_input;
end
%====================================================================
case 4, % Input should be in position/vector format with an option
option = inputList{3};
check_position_vector_input;
check_option_input;
%====================================================================
otherwise, % Too many input arguments
error('boundary:intersect:tooManyInputs',...
'Too many input arguments.');
end
catch
rethrow(mask_last_error('boundary.intersect'));
end
% Check boundary object input:
if (numel(boundaryObject) ~= 1),
error('boundary:intersect:badArgumentSize',...
'Boundary object argument should be a non-empty scalar.');
end
if (~ishandle(boundaryObject)),
error('boundary:intersect:invalidObject','Invalid boundary object.');
end
% Find point of collision between boundary object and line segment(s):
try
[collisionXYZ,rValue,normal] = ...
boundaryObject.handler('intersect',parameters);
catch
rethrow(mask_last_error('boundary.intersect'));
end
%~~~Begin nested functions~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%------------------------------------------------------------------------
function check_line_segment_input
%
% Function for checking if the line segment input is valid.
%
%------------------------------------------------------------------------
position = inputList{1};
if (~isnumeric(position)),
error('boundary:intersect:badArgumentType',...
'Line segment argument should be a numeric matrix.');
end
if (~isequal(size(position),[2 3])),
error('boundary:intersect:badArgumentSize',...
'Line segment argument should be a 2-by-3 matrix.');
end
parameters.position = position(1,:);
parameters.vector = diff(position);
end
%------------------------------------------------------------------------
function check_position_vector_input
%
% Function for checking if the position/vector input is valid.
%
%------------------------------------------------------------------------
position = inputList{1};
if (~isnumeric(position)),
error('boundary:intersect:badArgumentType',...
'Position argument should be a numeric matrix.');
end
[nPositionRows,nCols] = size(position);
if ((nPositionRows < 1) || (nCols ~= 3)),
error('boundary:intersect:badArgumentSize',...
'Position argument should be a non-empty N-by-3 matrix.');
end
vector = inputList{2};
if (~isnumeric(vector)),
error('boundary:intersect:badArgumentType',...
'Vector argument should be a numeric matrix.');
end
[nVectorRows,nCols] = size(vector);
if ((nVectorRows < 1) || (nCols ~= 3)),
error('boundary:intersect:badArgumentSize',...
'Vector argument should be a non-empty N-by-3 matrix.');
end
if (nPositionRows ~= nVectorRows),
if ((nPositionRows > 1) && (nVectorRows > 1)),
error('boundary:intersect:badArgumentSize',...
['One argument must be a 1-by-3 matrix when ' ...
'position\vector arguments are not the same size.']);
elseif (nPositionRows > nVectorRows),
vector = ones(nPositionRows,1)*vector;
elseif (nPositionRows < nVectorRows),
position = ones(nVectorRows,1)*position;
end
end
parameters.position = position;
parameters.vector = vector;
end
%------------------------------------------------------------------------
function check_option_input
%
% Function for checking if the option input is valid.
%
%------------------------------------------------------------------------
if (~ischar(option)),
error('boundary:intersect:badArgumentType',...
'Option argument should be a character string.');
end
if (~ismember(option,{'-first' '-last' '-all'})),
error('boundary:intersect:invalidOption',...
['Invalid option ''',option,'''.']);
end
parameters.option = option;
end
%~~~End nested functions~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
end