你已经派生过 geomesh
镜像自地址
https://gitee.com/sduem/geomesh.git
已同步 2025-08-03 18:26:51 +08:00
420 行
17 KiB
Matlab
420 行
17 KiB
Matlab
function varargout = interpclosed(x,y,varargin)
|
|
% INTERPCLOSED Arc-length interpolation, perimeter and area of 2D closed curves defined by points
|
|
%
|
|
% xyq = INTERPCLOSED(x,y,tq) Interpolates new data points xyq at given
|
|
% positions defined by an arc-length parametrization and the query points
|
|
% tq, along the closed curve defined by the points specified by x and y.
|
|
% The default method 'spline' is used. tq is a n-by-1 array with its
|
|
% elements constrained within [0,1], with 0 being the first point of the
|
|
% curve and 1 the last one.
|
|
%
|
|
% [len,area] = INTERPCLOSED(x,y) If tq is not specified and two output
|
|
% variables are requested, then only the perimeter and area of the
|
|
% interpolated curve are returned. Both outputs are obviously scalars.
|
|
%
|
|
% [len,area,c] = INTERPCLOSED(x,y) If tq is not specified and three output
|
|
% variables are requested, then the perimeter, area and centroid of the
|
|
% interpolated curve are returned. The centroid is a vector with the
|
|
% position as (x,y).
|
|
%
|
|
% [len,area,c,I] = INTERPCLOSED(x,y) If tq is not specified and four output
|
|
% variables are requested, then the perimeter, area, centroid, and second
|
|
% moments of area of the interpolated curve are returned. The second moment
|
|
% of area is a vector with three elements: (Ixx, Iyy, Ixy).
|
|
%
|
|
% pp = INTERPCLOSED(x,y,'pp') If only one output argument is defined and
|
|
% the string 'pp' is given as input, the returned variable is the piesewise
|
|
% polynomial pp, regardless of the definition of tq.
|
|
%
|
|
% [xyq,len,area] = INTERPCLOSED(x,y,tq) If tq is provided and there are
|
|
% three output variables, the perimeter and area are additionally returned.
|
|
%
|
|
% [xyq,len,area,c,I] = INTERPCLOSED(x,y,tq) If tq is provided and there are
|
|
% five output variables, the perimeter, the area, the centroid, and the
|
|
% second moments of area are additionally returned.
|
|
%
|
|
% [___] = INTERPCLOSED(___,method) By specifying the string method it
|
|
% is possible to change how the interpolated points are obtained.
|
|
% 'linear': Linear interpolation. The interpolated value at a query point
|
|
% is based on linear interpolation of the values at neighboring
|
|
% points in each respective dimension. This is the fastest
|
|
% method.
|
|
% 'spline': Spline interpolation using periodic end conditions. The
|
|
% interpolated value at a query point is based on a cubic
|
|
% interpolation of the values at neighboring points in each
|
|
% respective dimension. This is the default method.
|
|
% 'pchip': Shape-preserving piecewise cubic interpolation. The
|
|
% interpolated value at a query point is based on a shape-
|
|
% preserving piecewise cubic periodic interpolation of the
|
|
% values at neighboring points.
|
|
%
|
|
% [___] = INTERPCLOSED(___,print) By setting the boolean variable print to
|
|
% true, more details about the interpolation can be obtained.
|
|
%
|
|
% Examples:
|
|
% 1) Use the spline method to interpolate at 64 query points within the
|
|
% 8 points used to sample the original circle:
|
|
%
|
|
% t = linspace(0,2*pi,9);
|
|
% x = sin(t) + 0.2; y = cos(t) - 0.2;
|
|
% [len,area,c,I] = interpclosed(x,y);
|
|
% fprintf(['Perimeter: %4.5f, Area: %4.5f,\n',...
|
|
% 'Centroid: [%4.5f %4.5f], Iz: %4.5f\n',...
|
|
% 'To be compared to\n',...
|
|
% '2*pi: %4.5f, pi: %4.5f,\n',...
|
|
% 'Centroid: [%4.5f %4.5f], pi/2: %4.5f\n'],...
|
|
% len,area,c,(I(1)-area*c(2)^2+I(2)-area*c(1)^2),...
|
|
% 2*pi,pi,[0.2,-0.2],pi/2)
|
|
%
|
|
% 2) Get the piecewise polyonomial of a linear interpolation of a given
|
|
% set of points and use the polynomial to make a plot:
|
|
%
|
|
% x = [0 .82 .92 0 -.92 -.82]; y = [.66 .9 0 -.83 0 .9];
|
|
% pp = interpclosed(x,y,'pp','linear');
|
|
% tq = min(pp.breaks):0.001:max(pp.breaks);
|
|
% xyq = ppval(pp,tq);
|
|
% figure, plot(xyq(1,:),xyq(2,:))
|
|
%
|
|
% Find more examples in the File Exchange website.
|
|
%
|
|
% See also CSCVN, PCHIP, MKPP, PPVAL, INTERPARC, ARCLENGTH, INTERP1.
|
|
%
|
|
% Author: Santiago M. Benito
|
|
% Ruhr-Universität Bochum
|
|
% -------------------------------------------------------------------------
|
|
% Contact: santiago.benito@rub.de
|
|
% -------------------------------------------------------------------------
|
|
% Current version: 3.0
|
|
% -------------------------------------------------------------------------
|
|
% Last updated: 17.05.2021
|
|
% Changes:
|
|
% - It is now possible to compute the second moments of area of the
|
|
% fit with this function.
|
|
|
|
%% Manage input, output and catch eventual problems.
|
|
% Check for errors in the given inputs.
|
|
if nargin < 2
|
|
error('INTERPCLOSED:insufficientarguments', ...
|
|
'At least x and y must be supplied.')
|
|
end
|
|
|
|
if ~isvector(x) || ~isvector(y) || (length(x) ~= length(y))
|
|
error('INTERPCLOSED:baddimension', ...
|
|
'x and y must be vectors of the same length.')
|
|
end
|
|
|
|
% Set defaults.
|
|
method = 'spline';
|
|
print = false;
|
|
geomcalc = false; tqgiven = false;
|
|
pp = false;
|
|
|
|
% Initialize output variables.
|
|
len = 0;
|
|
area = 0;
|
|
c = zeros(1,2);
|
|
Ixx = 0; Iyy = 0; Ixy = 0;
|
|
|
|
% Check for other input arguments.
|
|
if numel(varargin) > 0
|
|
% At least one other argument was supplied.
|
|
for ii = 1:numel(varargin)
|
|
arg = varargin{ii};
|
|
if ischar(arg)
|
|
% It can be the method or the 'pp'-flag.
|
|
validstrings = {'pp','linear' 'pchip' 'spline'};
|
|
ind = strncmp(arg,validstrings,2);
|
|
if isempty(ind) || (sum(ind) == 0) || (sum(ind) > 1)
|
|
error('INTERPCLOSED:invalidmethod', ...
|
|
['Invalid method indicated. Only ''linear'',',...
|
|
'''pchip'',''spline'' allowed.'])
|
|
end
|
|
if ind(1) == 1
|
|
pp = true;
|
|
else
|
|
method = validstrings{ind>0};
|
|
end
|
|
elseif islogical(arg)
|
|
% It must be the print variable, set the print sampling distance.
|
|
if ~tqgiven, tq = 0:1/32:1; end
|
|
print = arg;
|
|
else
|
|
% It must be tq, defining the parametric arc-length query
|
|
% points
|
|
if ~isnumeric(arg)
|
|
error('INTERPCLOSED:badtq', ...
|
|
'tq must be numeric.')
|
|
else
|
|
if max(arg) > 1 || min(arg) < 0
|
|
error('INTERPCLOSED:badtq', ...
|
|
'tq elements must be bigger than 0 and smaller than 1.')
|
|
end
|
|
tqgiven = true;
|
|
tq = arg;
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
% If only one output variable is requested and the 'pp' flag was given, no
|
|
% need to compute the interpolations, regardless of the definition of tq.
|
|
% If three are given, geometry computations will be needed. If two or three
|
|
% are given, but tq was not provided, also compute the geometry computations.
|
|
if nargout == 1 && pp && ~print
|
|
tqgiven = false;
|
|
elseif (nargout == 2 || nargout == 3 || nargout == 4) && ~tqgiven
|
|
geomcalc = true;
|
|
elseif (nargout == 3 || nargout == 4 || nargout == 5)
|
|
geomcalc = true;
|
|
if ~tqgiven
|
|
error('INTERPCLOSED:badtq', ...
|
|
'tq was not defined and is needed for interpolation.')
|
|
end
|
|
end
|
|
|
|
% Be sure everything is formatted correclty and group it.
|
|
x = x(:)'; y = y(:)';
|
|
points = [x;y];
|
|
|
|
% Round to the 15th decimal position to avoid rounding errors. This is
|
|
% necessary for the function to recongnize start and ending points
|
|
% properly.
|
|
points = round(points,15);
|
|
|
|
% If the set of points does not describe a closed loop, close it.
|
|
if sum(points(:,1) ~= points(:,end)) > 0
|
|
points = [points,points(:,1)];
|
|
end
|
|
|
|
% If less than three distinct points are given, no closed curve can be
|
|
% formed.
|
|
d = sum((diff(points.').^2).');
|
|
if numel(x) - sum(d==0)-1 < 2
|
|
error('INTERPCLOSED:baddimension', ...
|
|
'x and y must be vectors describing at least three distinct points.')
|
|
end
|
|
|
|
%% Actual program start
|
|
% Compute the coefficients of the fit-polynomials according to the user's
|
|
% choice.
|
|
if strcmpi(method,'linear')
|
|
% Remove segments with length equal to zero, the linear interpolation
|
|
% has no continuous derivatives anyway.
|
|
points(:,d==0) = [];
|
|
|
|
% Compute the linear coefficients of the parametric versions of the
|
|
% lines. First compute the lengths of each segment, then the cumulative
|
|
% length and finally use the slope in each direction to get the coefs.
|
|
seglen = sqrt(sum(diff(points,[],2).^2,1));
|
|
cumarc = [0,cumsum(seglen)];
|
|
coefX = [diff(points(1,:))./diff(cumarc);points(1,1:(end-1))];
|
|
coefY = [diff(points(2,:))./diff(cumarc);points(2,1:(end-1))];
|
|
|
|
% Create a piecewise polynomial with the given coefficients.
|
|
coefs = zeros(size([coefX,coefY]));
|
|
coefs(:,1:2:end) = coefX;
|
|
coefs(:,2:2:end) = coefY;
|
|
curve = mkpp(cumarc,coefs',2);
|
|
|
|
% Provide the differentiation array for later use.
|
|
diffarray = [0 0 1;0 0 0];
|
|
|
|
% Since we already have the lenghts of the individual segments, just
|
|
% sum everything up and save some time.
|
|
len = sum(seglen);
|
|
|
|
elseif strcmpi(method,'spline')
|
|
% MATLAB(R) already has a very useful function that makes all the work
|
|
% for us.
|
|
curve = cscvn(points);
|
|
|
|
% Provide the differentiation array for later use.
|
|
diffarray = [3 0 0;0 2 0;0 0 1;0 0 0];
|
|
|
|
elseif strcmpi(method,'pchip')
|
|
% Like in the function CSCVN, if the user specified a point where the
|
|
% 2nd derivative is equal to zero, we have to be able to handle the
|
|
% situation.
|
|
d = sum((diff(points.').^2).');
|
|
|
|
if all(d > 0)
|
|
% The fit is periodic. To have the start and end slopes equal to
|
|
% each other, some tricks must be done. Extra points will be added
|
|
% right before the start and right after the end of the data set.
|
|
% The fit will be performed with these points, and then the extra
|
|
% pieces will be removed from the general fit.
|
|
%pointsNew = [x(end-2:end-1),x,x(2:3);y(end-2:end-1),y,y(2:3)];
|
|
pointsNew = [points(:,end-2:end-1),points,points(:,2:3)];
|
|
|
|
% We need the arc length of the modified dataset, therefore we will
|
|
% compute it here.
|
|
seglen = sqrt(sum(diff(pointsNew,[],2).^2,1));
|
|
cumarc = [0,cumsum(seglen)];
|
|
|
|
% Fit coefficients are obtained from the MATLAB(R) original pchip
|
|
% function.
|
|
temp = pchip(cumarc,pointsNew(1,:)); coefX = temp.coefs;
|
|
temp = pchip(cumarc,pointsNew(2,:)); coefY = temp.coefs;
|
|
|
|
% Here we remove the unnecesary pieces by removing the extra
|
|
% coefficients.
|
|
coefs = zeros(size([coefX;coefY])-[8,0]);
|
|
coefs(1:2:end,:) = coefX(3:end-2,:);
|
|
coefs(2:2:end,:) = coefY(3:end-2,:);
|
|
|
|
% Compute the actual arc length
|
|
seglen = sqrt(sum(diff(points,[],2).^2,1));
|
|
cumarc = [0,cumsum(seglen)];
|
|
|
|
else
|
|
% The 1st derivatives at the end points and at the specified points
|
|
% are not equal, while analysed from both sides. Firstly compute
|
|
% the arclength of the point distribution.
|
|
seglen = sqrt(sum(diff(points,[],2).^2,1));
|
|
cumarc = [0,cumsum(seglen)];
|
|
|
|
% Fit coefficients are obtained from the MATLAB(R) original pchip
|
|
% function, according to the desired derivative contiguity.
|
|
dp = find(d>0);
|
|
dpbig = find(diff(dp)>1);
|
|
dpbig = [dpbig,length(dp)];
|
|
idx = dp(1):(dp(dpbig(1))+1);
|
|
temp = pchip(cumarc(idx),points(1,idx)); coefX = temp.coefs;
|
|
temp = pchip(cumarc(idx),points(2,idx)); coefY = temp.coefs;
|
|
for j=2:length(dpbig)
|
|
idx = dp(dpbig(j-1)+1):(dp(dpbig(j))+1);
|
|
temp = pchip(cumarc(idx),points(1,idx));
|
|
coefX = [coefX;temp.coefs];
|
|
temp = pchip(cumarc(idx),points(2,idx));
|
|
coefY = [coefY;temp.coefs];
|
|
end
|
|
|
|
% Compiling the coefficients in a simple array.
|
|
coefs = zeros(size([coefX;coefY]));
|
|
coefs(1:2:end,:) = coefX(1:end,:);
|
|
coefs(2:2:end,:) = coefY(1:end,:);
|
|
|
|
% Update the cumulative arclength
|
|
cumarc(:,d==0) = [];
|
|
end
|
|
|
|
% Finally compute the piecewise polynomial.
|
|
curve = mkpp(cumarc,coefs,2);
|
|
|
|
% Provide the differentiation array for later use.
|
|
diffarray = [3 0 0;0 2 0;0 0 1;0 0 0];
|
|
end
|
|
|
|
% If tq is given (or a print is required), compute the interpolation using
|
|
% the piecewise evaluation function provided in MATLAB(R) and then convert
|
|
% the parametrization into an arc-lenght one.
|
|
if tqgiven || print
|
|
step = (max(curve.breaks)-min(curve.breaks))/numel(tq)/30;
|
|
auxtq = min(curve.breaks):step:max(curve.breaks);
|
|
xyq = ppval(curve,auxtq);
|
|
tqp = pdearcl(auxtq,xyq,tq,0,1);
|
|
xyq = ppval(curve,tqp);
|
|
end
|
|
|
|
% If the geometric parameters (perimeter and area) are required, compute
|
|
% them using some calculus.
|
|
if geomcalc
|
|
for ii = 1:curve.pieces
|
|
% Get the coefficients of the piecewise polynomial expresions of
|
|
% the parametric form.
|
|
coefX = curve.coefs(2*ii-1,:);
|
|
coefY = curve.coefs(2*ii,:);
|
|
|
|
% Obtain the derivatives of the polynomials.
|
|
difX = coefX*diffarray;
|
|
difY = coefY*diffarray;
|
|
|
|
% The length in the linear case is already computed, skip this bit.
|
|
if ~strcmpi(method,'linear')
|
|
% Define the function employed in the arc length and integrate
|
|
% it.
|
|
flen = @(t) sqrt(polyval(difX,t-curve.breaks(ii)).^2 ...
|
|
+ polyval(difY,t-curve.breaks(ii)).^2);
|
|
len = len + integral(flen,curve.breaks(ii),curve.breaks(ii+1));
|
|
end
|
|
% The area integral is computed here.
|
|
farea = @(t) polyval(conv(coefY,difX),...
|
|
t-curve.breaks(ii));
|
|
area = area + integral(farea,curve.breaks(ii),...
|
|
curve.breaks(ii+1));
|
|
|
|
% The centroid is computed here
|
|
fcx = @(t) polyval(conv(coefX,conv(coefY,difX)),...
|
|
t-curve.breaks(ii));
|
|
c(1) = c(1) + integral(fcx,curve.breaks(ii),...
|
|
curve.breaks(ii+1));
|
|
fcy = @(t) polyval(conv(coefY,conv(coefX,difY)),...
|
|
t-curve.breaks(ii));
|
|
c(2) = c(2) - integral(fcy,curve.breaks(ii),...
|
|
curve.breaks(ii+1));
|
|
|
|
% The area moments of inertia
|
|
fIxx = @(t) polyval(conv(coefY,conv(coefY,conv(coefY,difX))),...
|
|
t-curve.breaks(ii));
|
|
Ixx = Ixx + integral(fIxx,curve.breaks(ii),...
|
|
curve.breaks(ii+1));
|
|
fIyy = @(t) polyval(conv(coefX,conv(coefX,conv(coefX,difY))),...
|
|
t-curve.breaks(ii));
|
|
Iyy = Iyy - integral(fIyy,curve.breaks(ii),...
|
|
curve.breaks(ii+1));
|
|
fIxy = @(t) polyval(conv(coefY,conv(coefY,conv(coefX,difX))),...
|
|
t-curve.breaks(ii));
|
|
Ixy = Ixy + integral(fIxy,curve.breaks(ii),...
|
|
curve.breaks(ii+1));
|
|
end
|
|
c = c / area;
|
|
I = [1/3*Ixx,1/3*Iyy,1/2*Ixy]*sign(area);
|
|
area = abs(area);
|
|
|
|
|
|
end
|
|
|
|
%% If required, print some figures to show what the algorithm did.
|
|
if print
|
|
figure
|
|
subplot(1,2,1)
|
|
plot(xyq(1,:),xyq(2,:),'*')
|
|
hold on
|
|
plot(points(1,:),points(2,:),'o')
|
|
plot(c(1),c(2),'x')
|
|
xlabel('x'), ylabel('y'), hold off, axis equal
|
|
title('Cartesian representation'), legend('Interpolation','Points',...
|
|
'Centroid')
|
|
subplot(1,2,2)
|
|
plot(tq,xyq), hold on
|
|
for ii = (curve.breaks)/max(curve.breaks)
|
|
line([ii ii],ylim,'LineStyle','--','Color','k')
|
|
line([ii ii],ylim,'LineStyle','--','Color','k')
|
|
end
|
|
hold off, xlim([min(tq) max(tq)]), title('Parametric representation')
|
|
xlabel('t'), ylabel('x(t), y(t)'), legend('x(t)','y(t)')
|
|
end
|
|
|
|
%% Process adequately the variables to be returned.
|
|
if nargout == 2 && ~tqgiven
|
|
varargout{1} = len; varargout{2} = area;
|
|
elseif nargout == 3 && ~tqgiven
|
|
varargout{1} = len; varargout{2} = area; varargout{3} = c;
|
|
elseif nargout == 3
|
|
varargout{1} = xyq;
|
|
varargout{2} = len; varargout{3} = area;
|
|
elseif nargout == 4 && tqgiven
|
|
varargout{1} = xyq;
|
|
varargout{2} = len; varargout{3} = area; varargout{4} = c;
|
|
elseif nargout == 4 && ~tqgiven
|
|
varargout{1} = len; varargout{2} = area; varargout{3} = c;
|
|
varargout{4} = I;
|
|
elseif nargout == 5
|
|
varargout{1} = xyq;
|
|
varargout{2} = len; varargout{3} = area; varargout{4} = c;
|
|
varargout{5} = I;
|
|
elseif nargout == 1 && pp
|
|
varargout{1} = curve;
|
|
else
|
|
varargout{1} = xyq;
|
|
end |