No BSD License  

Highlights from
SpringLab

image thumbnail
from SpringLab by Ofek Shilon
Interactive RT simulation of elastic bodies

select3d(obj)
function [pout, vout, viout, facevout, faceiout]  = select3d(obj)
    %SELECT3D(H) Determines the selected point in 3-D data space.
    %  P = SELECT3D determines the point, P, in data space corresponding
    %  to the current selection position. P is a point on the first
    %  patch or surface face intersected along the selection ray. If no
    %  face is encountered along the selection ray, P returns empty.
    %
    %  P = SELECT3D(H) constrains selection to graphics handle H and,
    %  if applicable, any of its children. H can be a figure, axes,
    %  patch, or surface object.
    %
    %  [P V] = SELECT3D(...), V is the closest face or line vertex
    %  selected based on the figure's current object.
    %
    %  [P V VI] = SELECT3D(...), VI is the index into the object's
    %  x,y,zdata properties corresponding to V, the closest face vertex
    %  selected.
    %
    %  [P V VI FACEV] = SELECT3D(...), FACE is an array of vertices
    %  corresponding to the face polygon containing P and V.
    %
    %  [P V VI FACEV FACEI] = SELECT3D(...), FACEI is the row index into
    %  the object's face array corresponding to FACE. For patch
    %  objects, the face array can be obtained by doing
    %  get(mypatch,'faces'). For surface objects, the face array
    %  can be obtained from the output of SURF2PATCH (see
    %  SURF2PATCH for more information).
    %
    %  RESTRICTIONS:
    %  SELECT3D supports surface, patch, or line object primitives. For surface
    %  and patches, the algorithm assumes non-self-intersecting planar faces.
    %  For line objects, the algorithm always returns P as empty, and V will
    %  be the closest vertex relative to the selection point.
    %
    %  Example:
    %
    %  h = surf(peaks);
    %  zoom(10);
    %  disp('Click anywhere on the surface, then hit return')
    %  pause
    %  [p v vi face facei] = select3d;
    %  marker1 = line('xdata',p(1),'ydata',p(2),'zdata',p(3),'marker','o',...
    %                 'erasemode','xor','markerfacecolor','k');
    %  marker2 = line('xdata',v(1),'ydata',v(2),'zdata',v(3),'marker','o',...
    %                 'erasemode','xor','markerfacecolor','k');
    %  marker2 = line('erasemode','xor','xdata',face(1,:),'ydata',face(2,:),...
    %                 'zdata',face(3,:),'linewidth',10);
    %  disp(sprintf('\nYou clicked at\nX: %.2f\nY: %.2f\nZ: %.2f',p(1),p(2),p(3)'))
    %  disp(sprintf('\nThe nearest vertex is\nX: %.2f\nY: %.2f\nZ: %.2f',v(1),v(2),v(3)'))
    %
    %  Version 1.3 11-11-04
    %  Copyright Joe Conti 2004
    %  Send comments to jconti@mathworks.com
    %
    %  See also GINPUT, GCO.

    % Output variables
    pout = [];
    vout = [];
    viout = [];
    facevout = [];
    faceiout = [];

    % other variables
    ERRMSG = 'Input argument must be a valid graphics handle';
    isline = logical(0);
    isperspective = logical(0);

    % Parse input arguments
    if nargin<1
	  obj = gco;
    end

    if isempty(obj) | ~ishandle(obj) | length(obj)~=1
	  error(ERRMSG);
    end

    % if obj is a figure
    if strcmp(get(obj,'type'),'figure')
	  fig = obj;
	  ax = get(fig,'currentobject');
	  currobj = get(fig,'currentobject');

	  % bail out if not a child of the axes
	  if ~strcmp(get(get(currobj,'parent'),'type'),'axes')
		return;
	  end

	  % if obj is an axes
    elseif strcmp(get(obj,'type'),'axes')
	  ax = obj;
	  fig = get(ax,'parent');
	  currobj = get(fig,'currentobject');
	  currax = get(currobj,'parent');

	  % Bail out if current object is under an unspecified axes
	  if ~isequal(ax,currax)
		return;
	  end

	  % if obj is child of axes
    elseif strcmp(get(get(obj,'parent'),'type'),'axes')
	  currobj = obj;
	  ax = get(obj,'parent');
	  fig = get(ax,'parent');

	  % Bail out
    else
	  return
    end

    axchild = currobj;
    obj_type = get(axchild,'type');

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% Get vertex, face, and current point data %%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    cp = get(ax,'currentpoint')';

    % If surface object
    if strcmp(obj_type,'surface')
	  % Get surface face and vertices
	  fv = surf2patch(axchild);
	  vert = fv.vertices;
	  faces = fv.faces;

	  % If patch object
    elseif strcmp(obj_type,'patch')
	  vert = get(axchild,'vertices');
	  faces = get(axchild,'faces');

	  % If line object
    elseif strcmp(obj_type,'line')
	  xdata = get(axchild,'xdata');
	  ydata = get(axchild,'ydata');
	  zdata = get(axchild,'zdata');
	  vert = [xdata', ydata',zdata'];
	  faces = [];
	  isline = logical(1);

	  % Ignore all other objects
    else
	  return;
    end

    % Add z if empty
    if size(vert,2)==2
	  vert(:,3) = zeros(size(vert(:,2)));
	  if isline
		zdata = vert(:,3);
	  end
    end

    % NaN and Inf check
    nan_inf_test1 = isnan(faces) | isinf(faces);
    nan_inf_test2 = isnan(vert) | isinf(vert);
    if any(nan_inf_test1(:)) | any(nan_inf_test2(:))
	  warning(sprintf('%s does not support NaNs or Infs in face/vertex data.',mfilename));
    end

    % For debugging
    % if 0
    %     ax1 = getappdata(ax,'testselect3d');
    %     if isempty(ax1) | ~ishandle(ax1)
    %         fig = figure;
    %         ax1 = axes;
    %         axis(ax1,'equal');
    %         setappdata(ax,'testselect3d',ax1);
    %     end
    %     cla(ax1);
    %     patch('parent',ax1,'faces',faces,'vertices',xvert','facecolor','none','edgecolor','k');
    %     line('parent',ax1,'xdata',xcp(1,2),'ydata',xcp(2,2),'zdata',0,'marker','o','markerfacecolor','r','erasemode','xor');
    % end

    % Transform vertices from data space to pixel space
    xvert = local_Data2PixelTransform(ax,vert)';
    xcp = local_Data2PixelTransform(ax,cp')';

    % Translate vertices so that the selection point is at the origin.
    xvert(1,:) = xvert(1,:) - xcp(1,2);
    xvert(2,:) = xvert(2,:) - xcp(2,2);

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% simple algorithm (almost naive algorithm!) for line objects %%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    if isline

	  % Ignoring line width and marker attributes, find closest
	  % vertex in 2-D view space.
	  d = xvert(1,:).*xvert(1,:) + xvert(2,:).*xvert(2,:);
	  [val i] = min(d);
	  i = i(1); % enforce only one output

	  % Assign output
	  vout = [ xdata(i) ydata(i) zdata(i)];
	  viout = i;

	  return % Bail out early
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% Perform 2-D crossing test (Jordan Curve Theorem) %%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    % Find all vertices that have y components less than zero
    vert_with_negative_y = zeros(size(faces));
    face_y_vert = xvert(2,faces);
    ind_vert_with_negative_y = find(face_y_vert<0);
    vert_with_negative_y(ind_vert_with_negative_y) = logical(1);

    % Find all the line segments that span the x axis
    is_line_segment_spanning_x = abs(diff([vert_with_negative_y, vert_with_negative_y(:,1)],1,2));

    % Find all the faces that have line segments that span the x axis
    ind_is_face_spanning_x = find(any(is_line_segment_spanning_x,2));

    % Ignore data that doesn't span the x axis
    candidate_faces = faces(ind_is_face_spanning_x,:);
    vert_with_negative_y = vert_with_negative_y(ind_is_face_spanning_x,:);
    is_line_segment_spanning_x = is_line_segment_spanning_x(ind_is_face_spanning_x,:);

    % Create line segment arrays
    pt1 = candidate_faces;
    pt2 = [candidate_faces(:,2:end), candidate_faces(:,1)];

    % Point 1
    x1 = reshape(xvert(1,pt1),size(pt1));
    y1 = reshape(xvert(2,pt1),size(pt1));

    % Point 2
    x2 = reshape(xvert(1,pt2),size(pt2));
    y2 = reshape(xvert(2,pt2),size(pt2));

    % Cross product of vector to origin with line segment
    cross_product_test = -x1.*(y2-y1) > -y1.*(x2-x1);

    % Find all line segments that cross the positive x axis
    crossing_test = (cross_product_test==vert_with_negative_y) & is_line_segment_spanning_x;

    % If the number of line segments is odd, then we intersected the polygon
    s = sum(crossing_test,2);
    s = mod(s,2);
    ind_intersection_test = find(s~=0);

    % Bail out early if no faces were hit
    if isempty(ind_intersection_test)
	  return;
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% Plane/ray intersection test %%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Perform plane/ray intersection with the faces that passed
    % the polygon intersection tests. Grab the only the first
    % three vertices since that is all we need to define a plane).
    % assuming planar polygons.
    candidate_faces = candidate_faces(ind_intersection_test,1:3);
    candidate_faces = reshape(candidate_faces',1,prod(size(candidate_faces)));
    vert = vert';
    candidate_facev = vert(:,candidate_faces);
    candidate_facev = reshape(candidate_facev,3,3,length(ind_intersection_test));

    % Get three contiguous vertices along polygon
    v1 = squeeze(candidate_facev(:,1,:));
    v2 = squeeze(candidate_facev(:,2,:));
    v3 = squeeze(candidate_facev(:,3,:));

    % Get normal to face plane
    vec1 = [v2-v1];
    vec2 = [v3-v2];
    crs = cross(vec1,vec2);
    mag = sqrt(sum(crs.*crs));
    nplane(1,:) = crs(1,:)./mag;
    nplane(2,:) = crs(2,:)./mag;
    nplane(3,:) = crs(3,:)./mag;

    % Compute intersection between plane and ray
    cp1 = cp(:,1);
    cp2 = cp(:,2);
    d = cp2-cp1;
    dp = dot(-nplane,v1);

    %A = dot(nplane,d);
    A(1,:) = nplane(1,:).*d(1);
    A(2,:) = nplane(2,:).*d(2);
    A(3,:) = nplane(3,:).*d(3);
    A = sum(A,1);

    %B = dot(nplane,pt1)
    B(1,:) = nplane(1,:).*cp1(1);
    B(2,:) = nplane(2,:).*cp1(2);
    B(3,:) = nplane(3,:).*cp1(3);
    B = sum(B,1);

    % Distance to intersection point
    t = (-dp-B)./A;

    % Find "best" distance (smallest)
    [tbest ind_best] = min(t);

    % Determine intersection point
    pout = cp1 + tbest .* d;

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %% Assign additional output variables %%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    if nargout>1

	  % Get face index and vertices
	  faceiout = ind_is_face_spanning_x(ind_intersection_test(ind_best));
	  facevout = vert(:,faces(faceiout,:));

	  % Determine index of closest face vertex intersected
	  facexv = xvert(:,faces(faceiout,:));
	  dist = sqrt(facexv(1,:).*facexv(1,:) +  facexv(2,:).*facexv(2,:));
	  min_dist = min(dist);
	  min_index = find(dist==min_dist);

	  % Get closest vertex index and vertex
	  viout = faces(faceiout,min_index);
	  vout = vert(:,viout);
    end

    %--------------------------------------------------------%
function [p] = local_Data2PixelTransform(ax,vert)
    % Transform vertices from data space to pixel space.

    % Get needed transforms
    xform = get(ax,'x_RenderTransform');
    offset = get(ax,'x_RenderOffset');
    scale = get(ax,'x_RenderScale');

    % Equivalent: nvert = vert/scale - offset;
    nvert(:,1) = vert(:,1)./scale(1) - offset(1);
    nvert(:,2) = vert(:,2)./scale(2) - offset(2);
    nvert(:,3) = vert(:,3)./scale(3) - offset(3);

    % Equivalent xvert = xform*xvert;
    w = xform(4,1) * nvert(:,1) + xform(4,2) * nvert(:,2) + xform(4,3) * nvert(:,3) + xform(4,4);
    xvert(:,1) = xform(1,1) * nvert(:,1) + xform(1,2) * nvert(:,2) + xform(1,3) * nvert(:,3) + xform(1,4);
    xvert(:,2) = xform(2,1) * nvert(:,1) + xform(2,2) * nvert(:,2) + xform(2,3) * nvert(:,3) + xform(2,4);

    % w may be 0 for perspective plots
    ind = find(w==0);
    w(ind) = 1; % avoid divide by zero warning
    xvert(ind,:) = 0; % set pixel to 0

    p(:,1) = xvert(:,1) ./ w;
    p(:,2) = xvert(:,2) ./ w;

Contact us at files@mathworks.com