Code covered by the BSD License  

Highlights from
"boundary" class v2.1: a wrapper for surface objects

image thumbnail
from "boundary" class v2.1: a wrapper for surface objects by Kenneth Eaton
Adds functionality for fast detection of intersections between line segments and rendered surfaces

intersect(boundaryObject,varargin)
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

Contact us at files@mathworks.com