function theResult = tsearchsafe(x, y, tri, xi, yi)

% tsearchsafe -- TSEARCH for arbitrary triangles.
%  tsearchsafe('demo') demonstrates itself by
%   invoking "tsearch(10)".
%  tsearchsafe(NTRI, NPTS) demonstrates itself with
%   NTRI triangles and NPTS test-points (defaults:
%   NTRI=10, NPTS=10*NTRI).
%  tsearchsafe(x, y, tri, xi, yi) mimics TSEARCH for
%   an arbitrary group of non-overlapping triangles,
%   not necessarily produced by DELAUNAY.  The result
%   is an array of triangle indices for those points
%   that lie on or within at least one triangle.  All
%   other points are represented by NaN.  For points
%   lying on or within more than one triangle, only
%   one such boundary is identified, as in TSEARCH.
%
% Note: This routine exists because TSEARCH itself
%  is not reliable for non-Delaunay sets of triangles.
 
% Copyright (C) 2000 Dr. Charles R. Denham, ZYDECO.
%  All Rights Reserved.
%   Disclosure without explicit written consent from the
%    copyright owner does not constitute publication.
 
% Version of 04-Apr-2000 15:24:36.
% Updated    07-Apr-2000 14:28:36.

if nargout > 0, theResult = []; end

if nargin < 1, x = 'demo'; end

if isequal(x, 'demo'), help(mfilename), x = 10; end
if ischar(x), x= eval(x); end

if length(x) == 1
	nTriangles = max(x, 5);
	if nargin < 2, y = 10*x; end
	if ischar(y), y = eval(y); end
	nPoints = max(y, 10);
	x = 0.75*rand(2*nTriangles, 1) + 1/8;
	y = 0.75*rand(2*nTriangles, 1) + 1/8;
	tri = delaunay(x, y);
	[r, indices] = sort(rand(size(tri, 1)), 1);
	tri = tri(indices(1:nTriangles), :);
	xi = rand(nPoints, 1);
	yi = rand(nPoints, 1);
	tic;
	result = feval(mfilename, x, y, tri, xi, yi);
	disp([' ## Elapsed time: ' num2str(toc) ' s'])
	if nargout > 0
		theResult = result;
	else
		theDefaultMarkerSize = get(0, 'DefaultLineMarkerSize');
		theDefaultLineWidth = get(0, 'DefaultLineLineWidth');
		theMarkerSize = theDefaultMarkerSize/sqrt(nPoints/1000);
		theMarkerSize = min(theDefaultMarkerSize, ...
			max(theDefaultLineWidth, theMarkerSize));
		f = find(~isnan(result));
		g = find(isnan(result));
		hold off
		h = plot(xi(f), yi(f), 'g+', ...
					xi(g), yi(g), 'r+', ...
						'MarkerSize', theMarkerSize);
		hold on
		trimesh(tri, x, y, zeros(size(x)), ...
			'EdgeColor', [0 0 1], ...
			'FaceColor', 'none', ...
			'LineWidth', 0.5)
		hold off
		view(2)
		grid off
		try, zoomsafe, catch, zoom on, end
		figure(gcf)
		set(gcf, 'Name', [mfilename ' ' int2str(nTriangles) ' ' int2str(nPoints)])
		if nargout > 0, theResult = result; end
	end
	return
end

result = zeros(size(xi)) + NaN;

% Compute bounding rectangles for triangles.

t = tri.';
xmin = min(x(tri.')).';
xmax = max(x(tri.')).';
ymin = min(y(tri.')).';
ymax = max(y(tri.')).';

% Eliminate out-of-bounds points from
%  further consideration.

g = find(xi >= min(xmin) & xi <= max(xmax) & ...
			yi >= min(ymin) & yi <= max(ymax));

if (0)
	eliminated = length(xi) - length(g)
end

% For each point, find all the triangles
%  for which each point lies within their
%  bounding rectangles, then perform TSEARCH
%  on each one.  We depend on the assumption
%  that TSEARCH is reliable for a single
%  triangle and many points.

% NOTE: By pre-sorting the triangles and the (xi, yi)
%  points, we could reduce the calculation time, by
%  (say) working from right-to-left.
%  Also, whereas we presently loop over the (xi, yi),
%  we could also do the opposite, depending on the
%  relative numbers of triangles and (xi, yi) points.

for j = 1:length(g)
	k = g(j);
	xtemp = xi(k);
	ytemp = yi(k);
	f = find( ...   % Points strictly inside the bounds.
		(xtemp > xmin) & ...
		(xtemp < xmax) & ...
		(ytemp > ymin) & ...
		(ytemp < ymax));
	if any(f)
		for i = 1:length(f)
			index = tsearch(x, y, tri(f(i), :), xtemp, ytemp);
			if ~isnan(index)
				result(k) = f(i);
			end
		end
	end
end

if nargout > 0
	theResult = result;
else
	disp(result)
end
