comparison +grid/Curve.m @ 87:0a29a60e0b21

In Curve: Rearranged for speed. arc_length_fun is now a property of Curve. If it is not supplied, it is computed via the derivative and spline fitting. Switching to the arc length parameterization is much faster now. The new stuff can be tested with testArcLength.m (which should be deleted after that).
author Martin Almquist <martin.almquist@it.uu.se>
date Sun, 29 Nov 2015 22:23:09 +0100
parents 3c39dd714fb6
children b30f3d8845f4
comparison
equal deleted inserted replaced
86:3c39dd714fb6 87:0a29a60e0b21
1 classdef Curve 1 classdef Curve
2 properties 2 properties
3 g 3 g
4 gp 4 gp
5 transformation 5 transformation
6 arc_length_fun
7
8 % arc_length_fun:
9 % function handle. arc_length(s)=distance
10 % between t=0 and t=s.
6 end 11 end
7 methods 12 methods
8 %TODO: 13 %TODO:
9 % Errors or FD if there is no derivative function added. 14 % Errors or FD if there is no derivative function added.
10 % -semi-done 15 % -semi-done
13 % Subsections of curves 18 % Subsections of curves
14 % Stretching of curve paramter - semi-done 19 % Stretching of curve paramter - semi-done
15 % Curve to cell array of linesegments 20 % Curve to cell array of linesegments
16 21
17 % Should supply either derivative or a difference operator D1 22 % Should supply either derivative or a difference operator D1
18 function obj = Curve(g,gp,transformation,D1) 23 function obj = Curve(g,gp,transformation,D1,arc_length_fun)
19 default_arg('gp',[]); 24 default_arg('gp',[]);
20 default_arg('transformation',[]); 25 default_arg('transformation',[]);
21 default_arg('D1',[]); 26 default_arg('D1',[]);
27 default_arg('arc_length_fun',[]);
22 p_test = g(0); 28 p_test = g(0);
23 assert(all(size(p_test) == [2,1]), 'A curve parametrization must return a 2x1 vector.'); 29 assert(all(size(p_test) == [2,1]), 'A curve parametrization must return a 2x1 vector.');
24 30
25 if(isempty(gp) && isempty(D1)); 31 if(isempty(gp) && isempty(D1));
26 % Should be error instead of disp. 32 % Should be error instead of disp.
27 disp(['You really should supply either the exact derivative ',... 33 disp(['You really should supply either the exact derivative ',...
28 'or a suitable difference operator to compute it.']); 34 'or a suitable difference operator to compute an approximation.']);
29 elseif(isempty(gp)) 35 elseif(isempty(gp))
30 gp = grid.Curve.numerical_derivative(g,D1); 36 gp = grid.Curve.numerical_derivative(g,D1);
31 end 37 end
32 38
33 if ~isempty(transformation) 39 if ~isempty(transformation)
34 transformation.base_g = g; 40 transformation.base_g = g;
35 transformation.base_gp = gp; 41 transformation.base_gp = gp;
36 [g,gp] = grid.Curve.transform_g(g,gp,transformation); 42 [g,gp] = grid.Curve.transform_g(g,gp,transformation);
37 end 43 end
38 44
45 if(isempty(arc_length_fun))
46 if(~isempty(D1)); N = length(D1); % Same accuracy as for deriv.
47 else N = 101; % Best way to let user choose?
48 end
49 tvec = linspace(0,1,N);
50 arc_vec = grid.Curve.arc_length(gp,0,tvec);
51 arc_length_fun = grid.Curve.spline(tvec,arc_vec);
52 end
53
39 obj.g = g; 54 obj.g = g;
40 obj.gp = gp; 55 obj.gp = gp;
41 obj.transformation = transformation; 56 obj.transformation = transformation;
42 end 57 obj.arc_length_fun = arc_length_fun;
43 58
44 % Length of arc from parameter t0 to t1 (which may be vectors). 59
45 % Computed using derivative.
46 function L = arc_length(obj,t0,t1)
47 deriv = obj.gp;
48 speed = @(t) sp(deriv(t));
49
50 function s = sp(deriv)
51 s = sqrt(sum(deriv.^2,1));
52 end
53 L = util.integral_vec(speed,t0,t1);
54 end 60 end
55 61
56 % Made up length calculation!! Science this before actual use!! 62 % Made up length calculation!! Science this before actual use!!
57 % Calculates the length of the curve. Makes sure the longet segment used is shorter than h_max. 63 % Calculates the length of the curve. Makes sure the longet segment used is shorter than h_max.
58 function [L,t] = curve_length(C,h_max) 64 function [L,t] = curve_length(C,h_max)
102 normalization = sqrt(sum(deriv.^2,1)); 108 normalization = sqrt(sum(deriv.^2,1));
103 n = [-deriv(2,:)./normalization; deriv(1,:)./normalization]; 109 n = [-deriv(2,:)./normalization; deriv(1,:)./normalization];
104 end 110 end
105 111
106 112
107 % Plots a curve g(t) for 0<t<1, using n points. Retruns a handle h to the plotted curve. 113 % Plots a curve g(t) for 0<t<1, using n points. Returns a handle h to the plotted curve.
108 % h = plot_curve(g,n) 114 % h = plot_curve(g,n)
109 function h = plot(obj,n) 115 function h = plot(obj,n,marker)
110 default_arg('n',100); 116 default_arg('n',100);
117 default_arg('marker','line')
111 118
112 t = linspace(0,1,n); 119 t = linspace(0,1,n);
113 120
114 p = obj.g(t); 121 p = obj.g(t);
115 122
116 h = line(p(1,:),p(2,:)); 123 switch marker
124 case 'line'
125 h = line(p(1,:),p(2,:));
126 otherwise
127 h = plot(p(1,:),p(2,:),marker);
128 end
129 end
130
131 % Plots the derivative gp(t) for 0<t<1, using n points. Returns a handle h to the plotted curve.
132 % h = plot_curve(gp,n)
133 function h = plot_derivative(obj,n,marker)
134 default_arg('n',100);
135 default_arg('marker','line')
136
137 t = linspace(0,1,n);
138
139 p = obj.gp(t);
140
141 switch marker
142 case 'line'
143 h = line(p(1,:),p(2,:));
144 otherwise
145 h = plot(p(1,:),p(2,:),marker);
146 end
117 end 147 end
118 148
119 function h= plot_normals(obj,l,n,m) 149 function h= plot_normals(obj,l,n,m)
120 default_arg('l',0.1); 150 default_arg('l',0.1);
121 default_arg('n',10); 151 default_arg('n',10);
146 obj.plot(); 176 obj.plot();
147 end 177 end
148 % Shows curve with name and arrow for direction. 178 % Shows curve with name and arrow for direction.
149 179
150 180
151 % Should this be in the transformation method?
152 function curve = stretch_parameter(obj,type) 181 function curve = stretch_parameter(obj,type)
153 default_arg('type','arc_length'); 182 default_arg('type','arc_length');
154 switch type 183 switch type
155 % Arc length parameterization. WARNING: slow. 184 % Arc length parameterization.
156 case 'arc_length' 185 case 'arc_length'
157 arcLength = @(t) obj.arc_length(0,t); 186 arcLength = obj.arc_length_fun;
158 arcPar = @(t) util.fzero_vec(@(s)arcLength(s) - t*arcLength(1),0); 187 arcPar = @(t) util.fzero_vec(@(s)arcLength(s) - t*arcLength(1),[0-10*eps,1+10*eps]);
159 g_new = @(t)obj.g(arcPar(t)); 188 g_new = @(t)obj.g(arcPar(t));
160 gp_old = obj.gp; 189 gp_old = obj.gp;
161 gp_new = @(t) normalize(gp_old(arcPar(t))); 190 gp_new = @(t) normalize(gp_old(arcPar(t)));
162 191
163 curve = grid.Curve(g_new,gp_new); 192 arc_len_new = @(t) t;
193 curve = grid.Curve(g_new,gp_new,[],[],arc_len_new);
164 194
165 otherwise 195 otherwise
166 error('That stretching is not implemented.'); 196 error('That stretching is not implemented.');
167 end 197 end
168 198
306 end 336 end
307 end 337 end
308 338
309 methods (Static) 339 methods (Static)
310 340
341 % Length of arc from parameter t0 to t1 (which may be vectors).
342 % Computed using derivative.
343 function L = arc_length(deriv,t0,t1)
344 speed = @(t) sp(deriv(t));
345
346 function s = sp(deriv)
347 s = sqrt(sum(deriv.^2,1));
348 end
349 L = util.integral_vec(speed,t0,t1);
350 end
351
311 function gp_out = numerical_derivative(g,D1) 352 function gp_out = numerical_derivative(g,D1)
312 m = length(D1); L = 1; % Assume curve parameter from 0 to 1. 353 m = length(D1); L = 1; % Assume curve parameter from 0 to 1.
313 dt = L/(m-1);
314 t = linspace(0,L,m); 354 t = linspace(0,L,m);
315 g = g(t)'; 355 g = g(t)';
316 gp = (D1*g)'; 356 gp = (D1*g)';
317 357
318 % Make vectors longer to make sure splines are 358 gp1_fun = grid.Curve.spline(t,gp(1,:));
319 % reasonable at 1+eps. 359 gp2_fun = grid.Curve.spline(t,gp(2,:));
320 t_l = linspace(0,L+dt,m+1);
321 gp_l = [gp,gp(:,end)+gp(:,end)-gp(:,end-1)];
322
323 % 4:th order splines.
324 spline_order = 4;
325 gp1 = spapi( optknt(t_l,spline_order), t_l, gp_l(1,:) );
326 gp2 = spapi( optknt(t_l,spline_order), t_l, gp_l(2,:) );
327 gp1_fun = @(t) fnval(gp1,t); gp2_fun = @(t) fnval(gp2,t);
328 gp_out = @(t) [gp1_fun(t);gp2_fun(t)]; 360 gp_out = @(t) [gp1_fun(t);gp2_fun(t)];
329 361
362 end
363
364 % Returns a function handle to the spline.
365 function f = spline(tval,fval,spline_order)
366 default_arg('spline_order',4);
367 [m,~] = size(tval);
368 assert(m==1,'Need row vectors.');
369
370 % make vectors longer to be safe slightly beyond edges.
371 dt0 = tval(2)-tval(1); dt1 = tval(end)-tval(end-1);
372 df0 = fval(2)-fval(1); df1 = fval(end)-fval(end-1);
373 tval = [tval(1)-dt0, tval, tval(end)+dt1];
374 fval = [fval(1)-df0, fval, fval(end)+df1];
375
376 f_spline = spapi( optknt(tval,spline_order), tval, fval );
377 f = @(t) fnval(f_spline,t);
330 end 378 end
331 379
332 function obj = line(p1, p2) 380 function obj = line(p1, p2)
333 381
334 function v = g_fun(t) 382 function v = g_fun(t)