-
Notifications
You must be signed in to change notification settings - Fork 0
/
arclength.m
232 lines (208 loc) · 6.05 KB
/
arclength.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
function [arclen,seglen] = arclength(px,py,varargin)
% arclength: compute arc length of a space curve, or any curve represented as a sequence of points
% usage: [arclen,seglen] = arclength(px,py) % a 2-d curve
% usage: [arclen,seglen] = arclength(px,py,pz) % a 3-d space curve
% usage: [arclen,seglen] = arclength(px,py,method) % specifies the method used
%
% Computes the arc length of a function or any
% general 2-d, 3-d or higher dimensional space
% curve using various methods.
%
% arguments: (input)
% px, py, pz, ... - vectors of length n, defining points
% along the curve. n must be at least 2. Replicate
% points should not be present in the curve.
%
% method - (OPTIONAL) string flag - denotes the method
% used to compute the arc length of the curve.
%
% method may be any of 'linear', 'spline', or 'pchip',
% or any simple contraction thereof, such as 'lin',
% 'sp', or even 'p'.
%
% method == 'linear' --> Uses a linear chordal
% approximation to compute the arc length.
% This method is the most efficient.
%
% method == 'pchip' --> Fits a parametric pchip
% approximation, then integrates the
% segments numerically.
%
% method == 'spline' --> Uses a parametric spline
% approximation to fit the curves, then
% integrates the segments numerically.
% Generally for a smooth curve, this
% method may be most accurate.
%
% DEFAULT: 'linear'
%
%
% arguments: (output)
% arclen - scalar total arclength of all curve segments
%
% seglen - arclength of each independent curve segment
% there will be n-1 segments for which the
% arc length will be computed.
%
%
% Example:
% % Compute the length of the perimeter of a unit circle
% theta = linspace(0,2*pi,10);
% x = cos(theta);
% y = sin(theta);
%
% % The exact value is
% 2*pi
% % ans =
% % 6.28318530717959
%
% % linear chord lengths
% arclen = arclength(x,y,'l')
% % arclen =
% % 6.1564
%
% % Integrated pchip curve fit
% arclen = arclength(x,y,'p')
% % arclen =
% % 6.2782
%
% % Integrated spline fit
% arclen = arclength(x,y,'s')
% % arclen =
% % 6.2856
%
% Example:
% % A (linear) space curve in 5 dimensions
% x = 0:.25:1;
% y = x;
% z = x;
% u = x;
% v = x;
%
% % The length of this curve is simply sqrt(5)
% % since the "curve" is merely the diagonal of a
% % unit 5 dimensional hyper-cube.
% [arclen,seglen] = arclength(x,y,z,u,v,'l')
% % arclen =
% % 2.23606797749979
% % seglen =
% % 0.559016994374947
% % 0.559016994374947
% % 0.559016994374947
% % 0.559016994374947
%
%
% See also: interparc, spline, pchip, interp1
%
% Author: John D'Errico
% e-mail: [email protected]
% Release: 1.0
% Release date: 3/10/2010
% unpack the arguments and check for errors
if nargin < 2
error('ARCLENGTH:insufficientarguments', ...
'at least px and py must be supplied')
end
n = length(px);
% are px and py both vectors of the same length?
if ~isvector(px) || ~isvector(py) || (length(py) ~= n)
error('ARCLENGTH:improperpxorpy', ...
'px and py must be vectors of the same length')
elseif n < 2
error('ARCLENGTH:improperpxorpy', ...
'px and py must be vectors of length at least 2')
end
% compile the curve into one array
data = [px(:),py(:)];
% defaults for method and tol
method = 'linear';
% which other arguments are included in varargin?
if numel(varargin) > 0
% at least one other argument was supplied
for i = 1:numel(varargin)
arg = varargin{i};
if ischar(arg)
% it must be the method
validmethods = {'linear' 'pchip' 'spline'};
ind = strmatch(lower(arg),validmethods);
if isempty(ind) || (length(ind) > 1)
error('ARCLENGTH:invalidmethod', ...
'Invalid method indicated. Only ''linear'',''pchip'',''spline'' allowed.')
end
method = validmethods{ind};
else
% it must be pz, defining a space curve in higher dimensions
if numel(arg) ~= n
error('ARCLENGTH:inconsistentpz', ...
'pz was supplied, but is inconsistent in size with px and py')
end
% expand the data array to be a 3-d space curve
data = [data,arg(:)]; %#ok
end
end
end
% what dimension do we live in?
nd = size(data,2);
% compute the chordal linear arclengths
seglen = sqrt(sum(diff(data,[],1).^2,2));
arclen = sum(seglen);
% we can quit if the method was 'linear'.
if strcmpi(method,'linear')
% we are now done. just exit
return
end
% 'spline' or 'pchip' must have been indicated,
% so we will be doing an integration. Save the
% linear chord lengths for later use.
chordlen = seglen;
% compute the splines
spl = cell(1,nd);
spld = spl;
diffarray = [3 0 0;0 2 0;0 0 1;0 0 0];
for i = 1:nd
switch method
case 'pchip'
spl{i} = pchip([0;cumsum(chordlen)],data(:,i));
case 'spline'
spl{i} = spline([0;cumsum(chordlen)],data(:,i));
nc = numel(spl{i}.coefs);
if nc < 4
% just pretend it has cubic segments
spl{i}.coefs = [zeros(1,4-nc),spl{i}.coefs];
spl{i}.order = 4;
end
end
% and now differentiate them
xp = spl{i};
xp.coefs = xp.coefs*diffarray;
xp.order = 3;
spld{i} = xp;
end
% numerical integration along the curve
polyarray = zeros(nd,3);
for i = 1:spl{1}.pieces
% extract polynomials for the derivatives
for j = 1:nd
polyarray(j,:) = spld{j}.coefs(i,:);
end
% integrate the arclength for the i'th segment
% using quadgk for the integral. I could have
% done this part with an ode solver too.
seglen(i) = quadgk(@(t) segkernel(t),0,chordlen(i));
end
% and sum the segments
arclen = sum(seglen);
% ==========================
% end main function
% ==========================
% begin nested functions
% ==========================
function val = segkernel(t)
% sqrt((dx/dt)^2 + (dy/dt)^2)
val = zeros(size(t));
for k = 1:nd
val = val + polyval(polyarray(k,:),t).^2;
end
val = sqrt(val);
end % function segkernel
end % function arclength