0001 function mdl2 = place_elec_on_surf(mdl,elec_pos, elec_spec,ng_opt_file, maxh)
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049 citeme(mfilename);
0050
0051 if ischar(mdl) && strcmp(mdl, 'UNIT_TEST') do_unit_test; return; end;
0052 if nargin < 4
0053 ng_opt_file = '';
0054 end
0055 if nargin < 5
0056 maxh = [];
0057 end
0058
0059 opt.fstr = 'place_elec_on_surf';
0060 mdl2 = eidors_cache(@do_place_elec_on_surf,{mdl,elec_pos, elec_spec,ng_opt_file, maxh},opt);
0061
0062
0063
0064 function mdl2 = do_place_elec_on_surf(mdl,elec_pos, elec_spec,ng_opt_file, maxh)
0065
0066
0067
0068 if do_debug; fnstem = 'tmp1';
0069 else; fnstem = tempname;
0070 end
0071
0072 stlfn = [fnstem,'.stl'];
0073 meshfn= [fnstem,'.vol'];
0074
0075 if do_debug; fnstem = 'tmp2';
0076 else; fnstem = tempname;
0077 end
0078
0079 stlfn2 = [fnstem,'.stl'];
0080
0081
0082 mdl = prepare_surf_model(mdl);
0083 if isempty(maxh)
0084 maxh = max(mdl.edge_len);
0085 end
0086
0087 elecs = parse_elecs(mdl,elec_pos,elec_spec);
0088 if isempty(elecs)
0089 error('EIDORS:WrongInput', 'Failed to parse electrode positions. Exiting');
0090 end
0091
0092
0093
0094 for i = 1:length(elecs)
0095 try
0096 [N, fc] = grow_neighbourhood(mdl,elecs(i));
0097 [mdl E1{i} E2{i} V{i}] = add_electrodes(mdl,N,fc, elecs(i));
0098 catch e
0099 eidors_msg('Failed to add electrode #%d',i,1);
0100 rethrow(e);
0101 end
0102 end
0103
0104
0105 write_to_stl(mdl,stlfn);
0106 write_ng_opt_file(ng_opt_file, maxh)
0107 call_netgen(stlfn,meshfn);
0108 delete('ng.opt');
0109
0110
0111 fmdl=ng_mk_fwd_model(meshfn,[],[],[],[]);
0112 mdl = fix_model(fmdl);
0113 mdl = orient_boundary(mdl);
0114 mdl.elems = mdl.boundary;
0115
0116
0117 for i = 1:length(elecs)
0118 try
0119 mdl = flatten_electrode(mdl,E1{i},E2{i}, V{i});
0120 catch e
0121 eidors_msg('Failed to flatten electrode #%d',i,1);
0122 rethrow(e);
0123 end
0124 end
0125
0126
0127 write_to_stl(mdl,stlfn2);
0128 mdl2 = gmsh_stl2tet(stlfn2, maxh);
0129 mdl2.electrode = mdl.electrode;
0130
0131
0132 for i = 1:length(elecs)
0133 enodes = mdl.nodes(mdl.electrode(i).nodes,:);
0134 mdl2.electrode(i).nodes = find_matching_nodes(mdl2,enodes,1e-5);
0135 end
0136
0137 function debugging = do_debug;
0138 debugging = eidors_debug('query','place_elec_on_surf');
0139
0140 function write_to_stl(mdl,stlfn)
0141 STL.vertices = mdl.nodes;
0142 STL.faces = mdl.elems;
0143 stl_write(STL,stlfn);
0144
0145 function write_ng_opt_file(ng_opt_file, maxh)
0146
0147
0148 if ~isempty(ng_opt_file)
0149 ng_write_opt(ng_opt_file);
0150 else
0151 opt.meshoptions.fineness = 6;
0152 opt.options.curvaturesafety = 0.2;
0153
0154
0155 opt.stloptions.yangle = 30;
0156
0157 opt.stloptions.edgecornerangle = 0;
0158
0159 opt.stloptions.outerchartangle = 120;
0160 opt.stloptions.resthchartdistenable = 1;
0161 opt.stloptions.resthchartdistfac = 2.0;
0162 if ~isempty(maxh)
0163 opt.options.meshsize = maxh;
0164 end
0165 opt.meshoptions.laststep = 'mv';
0166 opt.options.optsteps2d = 5;
0167 opt.options.badellimit = 120;
0168 ng_write_opt(opt);
0169 end
0170
0171
0172
0173 function mdl = prepare_surf_model(mdl)
0174 try mdl = rmfield(mdl,'boundary'); end
0175 mdl = fix_model(mdl);
0176 mdl = orient_boundary(mdl);
0177 mdl.elems = mdl.boundary;
0178 mdl.faces = mdl.boundary;
0179 mdl.face_centre = mdl.face_centre(mdl.boundary_face,:);
0180 mdl.normals = mdl.normals(mdl.boundary_face,:);
0181 mdl = rmfield(mdl, 'inner_normal');
0182 mdl = rmfield(mdl, 'boundary_face');
0183 idx = nchoosek(1:3, 2);
0184 elem_sorted = sort(mdl.elems,2);
0185 [mdl.edges ib ia] = unique(reshape(elem_sorted(:,idx),[],2),'rows');
0186 D = mdl.nodes(mdl.edges(:,1),:) - mdl.nodes(mdl.edges(:,2),:);
0187 mdl.edge_len = sqrt(sum(D.^2,2));
0188
0189 function mdl = orient_boundary(mdl)
0190
0191 flip = mdl.elem2face(logical(mdl.boundary_face(mdl.elem2face).*mdl.inner_normal));
0192 mdl.faces(flip,:) = mdl.faces(flip,[1 3 2]);
0193 mdl.normals(flip,:) = -mdl.normals(flip,:);
0194 mdl.boundary = mdl.faces(mdl.boundary_face,:);
0195
0196
0197 function mdl = flatten_electrode(mdl,inner,outer, V)
0198 n1 = find_matching_nodes(mdl,inner, 1e-2);
0199 n2 = find_matching_nodes(mdl,outer, 1e-5);
0200
0201 N1 = false(length(mdl.nodes),1);
0202 N1(n1) = true;
0203 N2 = false(length(mdl.nodes),1);
0204 N2(n2) = true;
0205 rm = sum(N1(mdl.elems),2)>0 & sum(N2(mdl.elems),2)>0;
0206
0207 f = find(sum(N2(mdl.elems),2)>1 & ~rm,1,'first');
0208 B = find(mdl.boundary_face);
0209 p = mdl.face_centre(B(f),:);
0210 r = Inf;
0211 mdl.elems(rm,:) = [];
0212 mdl.boundary = mdl.elems;
0213 mdl.boundary_face(B(rm)) = [];
0214 mdl.face_centre(B(rm),:) = [];
0215 mdl.normals(B(rm),:) = [];
0216 mdl.faces(B(rm),:) = [];
0217 f = f - nnz(rm(1:f));
0218 N = grow_neighbourhood(mdl,f,p,r);
0219
0220
0221
0222
0223 ntm = unique(mdl.elems(N,:));
0224 mdl.nodes(ntm,:) = mdl.nodes(ntm,:) - repmat(V,length(ntm),1);
0225 e_nodes = ntm;
0226
0227
0228 map = 1:length(mdl.nodes);
0229 map(n2) = n1;
0230 mdl.elems = map(mdl.elems);
0231 mdl.faces = map(mdl.faces);
0232 e_nodes = map(ntm);
0233
0234
0235 m = true(length(mdl.nodes),1);
0236 m(n2) = false;
0237 map = zeros(size(m));
0238 map(m) = 1:nnz(m);
0239
0240 mdl.nodes(n2,:) = [];
0241 mdl.elems = map(mdl.elems);
0242 mdl.faces = map(mdl.faces);
0243 e_nodes = map(e_nodes);
0244
0245 mdl.boundary = mdl.elems;
0246 if ~isfield(mdl,'electrode')
0247 mdl.electrode = struct();
0248 l = 1;
0249 else
0250 l = length(mdl.electrode);
0251
0252
0253 for i = 1:l
0254 mdl.electrode(i).nodes = map(mdl.electrode(i).nodes);
0255 end
0256 l = l + 1;
0257 end
0258 mdl.electrode(l).nodes = double(e_nodes);
0259 mdl.electrode(l).z_contact = 0.01;
0260
0261 if do_debug
0262 show_fem(mdl);
0263 end
0264
0265
0266 function match = find_matching_nodes(mdl, nodes,th)
0267 l0 = length(mdl.nodes);
0268 match = 0 * (1:length(nodes));
0269 for n = 1:length(nodes)
0270 D = mdl.nodes - repmat(nodes(n,:),l0,1);
0271 D = sqrt(sum(D.^2,2));
0272 [val p] = min(D);
0273 if val < th
0274 match(n) = p;
0275 end
0276 end
0277
0278
0279
0280 function [joint EL1 EL2 V] = add_electrodes(mdl,N,fc,elecs)
0281
0282
0283
0284
0285
0286
0287 fcs = N;
0288 used_nodes = unique(mdl.faces(fcs,:));
0289 node_map = zeros(1,size(mdl.nodes,1));
0290 node_map(used_nodes) = 1:numel(used_nodes);
0291
0292 jnk.type = 'fwd_model';
0293 jnk.elems = node_map(mdl.boundary(N,:));
0294 jnk.nodes = mdl.nodes(used_nodes,:);
0295 jnk.boundary = jnk.elems;
0296 if do_debug
0297 show_fem(jnk);
0298 hold on
0299 plot3(elecs.points(:,1),elecs.points(:,2),elecs.points(:,3),'ro');
0300
0301 hold off
0302 end
0303
0304 flat = level_model_slice(jnk,struct('centre',mdl.face_centre(fc,:),'normal',mdl.normals(fc,:)));
0305 elec_pts = level_model_slice(elecs.points,struct('centre',mdl.face_centre(fc,:),'normal',mdl.normals(fc,:)));
0306 elec_nodes = level_model_slice(elecs.nodes,struct('centre',mdl.face_centre(fc,:),'normal',mdl.normals(fc,:)));
0307
0308 warning off 'MATLAB:triangulation:PtsNotInTriWarnId'
0309 TR = triangulation(double(flat.elems), flat.nodes(:,1:2));
0310 warning on 'MATLAB:triangulation:PtsNotInTriWarnId'
0311
0312 edges = TR.freeBoundary;
0313
0314
0315 PN = flat.nodes(:,1:2);
0316
0317
0318 neighbour = TR.nearestNeighbor(elec_pts(:,1:2));
0319 D = sqrt(sum((flat.nodes(neighbour,1:2) - elec_pts(:,1:2)).^2,2));
0320 rm = unique(neighbour(D < 2 * elecs.maxh));
0321
0322
0323 b = unique(edges(:));
0324 rm(ismember(rm,b)) = [];
0325
0326
0327 PN(rm,:) = [];
0328 used_nodes(rm) = [];
0329
0330 n = size(flat.nodes,1);
0331 nodelist = 1:n;
0332 nodelist(rm) = [];
0333 map = zeros(n,1);
0334 map(nodelist) = 1:numel(nodelist);
0335 edges = map(edges);
0336
0337 points = [PN; elec_pts(:,1:2)];
0338
0339
0340 f = length(PN) + (1:2);
0341 C = bsxfun(@plus, (0:length(elecs.points)-2)', f);
0342 [wtxt, wid] = lastwarn;
0343 lastwarn('','');
0344 warning off 'MATLAB:DelaunayTri:ConsConsSplitWarnId';
0345 D = DelaunayTri(points,[edges; C]);
0346 els = D.Triangulation(D.inOutStatus,:);
0347 [txt, id] = lastwarn;
0348 if strcmp(id,'MATLAB:DelaunayTri:ConsConsSplitWarnId')
0349 if do_debug
0350 keyboard
0351 else
0352 error(txt);
0353 end
0354 else
0355 lastwarn(wtxt,wid);
0356 end
0357 warning on 'MATLAB:DelaunayTri:ConsConsSplitWarnId';
0358
0359
0360 Ne = mdl.normals(fc,:);
0361 FN = TR.pointLocation(elec_nodes(:,1:2));
0362 FC = fcs(FN);
0363 for j = 1:length(elecs.nodes)
0364 Pe = elecs.nodes(j,:);
0365 Nf = mdl.normals(fcs(FN(j)),:);
0366 Cf = mdl.face_centre(fcs(FN(j)),:);
0367 Proj(j,:) = Pe + Ne * dot(Cf-Pe,Nf) / dot(Ne,Nf) ;
0368 end
0369
0370
0371
0372 EL1 = Proj(1:length(elecs.points),:);
0373
0374
0375 ln = length(used_nodes);
0376
0377
0378
0379 add = elecs.maxh;
0380
0381 nn = mdl.nodes(used_nodes,:);
0382 le = length(elecs.nodes);
0383 ne = Proj + add * repmat(Ne,le,1);
0384
0385
0386 EL2 = ne(1:length(elecs.points),:);
0387 V = add*Ne;
0388
0389
0390
0391 el_c = D.incenters;
0392 el_c(~D.inOutStatus,:) = [];
0393 e_el = inpolygon(el_c(:,1),el_c(:,2),points(ln+1:end,1),points(ln+1:end,2));
0394 els(e_el,:) = [];
0395
0396
0397 E = [];
0398 le = length(elecs.points);
0399 f = ln + [ 1 le+1 le+2; le+2 2 1];
0400 for j = 0:(le-2)
0401 E = [E; j+f];
0402 end
0403 M = ln + [le+1 le 2*le; le le+1 1];
0404 E = [E; M];
0405
0406 jnk.nodes = [nn ; Proj(1:le,:); ne];
0407 jnk.elems = [ els; E; elecs.elems+ln+le];
0408 jnk.boundary = jnk.elems;
0409 if do_debug
0410 show_fem(jnk);
0411 end
0412
0413
0414 big = mdl;
0415 big.boundary(N,:) = [];
0416 big.faces(N,:) = [];
0417 big.normals(N,:) = [];
0418 big.face_centre(N,:) = [];
0419
0420 big.elems = big.boundary;
0421
0422 joint = merge_meshes(big,jnk,0.001);
0423 joint.boundary = joint.elems;
0424 joint.faces = joint.boundary;
0425 opt.normals = true;
0426 opt.face_centre = true;
0427 joint = fix_model(joint,opt);
0428
0429
0430 function PN = project_nodes_on_elec(mdl,elecs,nodes)
0431 fc = find_face_under_elec(mdl,elecs.pos);
0432 Ne = mdl.normals(fc,:);
0433 Pe = elecs.pos;
0434
0435
0436
0437
0438 N = mdl.nodes(nodes,:);
0439
0440 PN = N + bsxfun(@times,sum(bsxfun(@times,bsxfun(@minus,Pe,N), Ne),2), Ne);
0441
0442
0443
0444
0445
0446
0447
0448
0449
0450
0451 function [elecs] = parse_elecs(mdl, elec_pos, elec_shape )
0452 elecs = [];
0453
0454 if size(elec_shape,2) < 3
0455 elec_shape(:,3) = elec_shape(:,1)/10;
0456 end
0457
0458 have_xyz = 0;
0459
0460 if size(elec_pos,1) == 1
0461
0462 n_elecs= elec_pos(1);
0463 offset = elec_pos(2) - floor(elec_pos(2));
0464 switch floor(elec_pos(2))
0465 case 0
0466 th = linspace(0,2*pi, n_elecs+1)'; th(end)=[];
0467 th = th + offset*2*pi;
0468 ind = th >= 2*pi;
0469 th(ind) = th(ind) - 2*pi;
0470 case 1
0471 error('not implemented yet');
0472 end
0473 on_elecs = ones(n_elecs, 1);
0474 el_th = [];
0475 el_z = [];
0476 for i=3:length(elec_pos)
0477 el_th = [el_th; th];
0478 el_z = [el_z ; on_elecs*elec_pos(i)];
0479 end
0480 elseif size(elec_pos,2) == 2
0481
0482 el_th = elec_pos(:,1)*2*pi/360;
0483 el_z = elec_pos(:,2);
0484 elseif size(elec_pos,2) == 3
0485
0486 have_xyz = 1;
0487 el_z = elec_pos(:,3);
0488 end
0489
0490 if ~have_xyz
0491 el_th(el_th>pi) = el_th(el_th>pi) - 2*pi;
0492 el_th(el_th<-pi) = el_th(el_th<-pi) + 2*pi;
0493 end
0494 n_elecs= size(el_z,1);
0495
0496 if size(elec_shape,1) == 1
0497 elec_shape = ones(n_elecs,1) * elec_shape;
0498 end
0499
0500 for i = 1:n_elecs
0501 if ~have_xyz
0502 [fc elecs(i).pos] = find_elec_centre(mdl,el_th(i),el_z(i));
0503 else
0504 elecs(i).pos = elec_pos(i,:);
0505 end
0506
0507 elecs(i).dims = elec_shape(i,1:2);
0508 elecs(i).dims(elecs(i).dims==0) = [];
0509 elecs(i).maxh = elec_shape(i,3);
0510
0511 if elec_shape(i,2) == 0
0512 elecs(i).shape = 'C';
0513 r = elec_shape(i,1);
0514 n = ceil(2*pi*elec_shape(i,1) / elec_shape(i,3));
0515 t = linspace(0,2*pi,n+1); t(end) = [];
0516 x = r*sin(t); y = r*cos(t);
0517 else
0518 elecs(i).shape = 'R';
0519 height = elec_shape(i,1); width = elec_shape(i,2); d_org = elec_shape(i,3);
0520
0521 d = min( [ d_org , height/5, width/5]);
0522 if d < d_org
0523 elecs(i).maxh = d;
0524 eidors_msg('@@@ Decreased maxh of electrode %d from %f to %f',i,d_org, d,2);
0525 end
0526 nh = ceil(height/d)+1; nw = ceil(width/d)+1;
0527 ph = linspace(-height/2,height/2,nh);
0528 pw = linspace(-width/2,width/2,nw);
0529 y = [ph, ph(end)*ones(1,nw-2), fliplr(ph), ph(1)*ones(1,nw-2)];
0530 x = [pw(1)*ones(1,nh-1), pw, pw(end)*ones(1,nh-2), fliplr(pw(2:end))];
0531
0532
0533 n = 2*(nh+nw);
0534
0535
0536
0537
0538
0539
0540
0541
0542
0543
0544
0545
0546 e = tand(0.5)*d;
0547 x = x + e* [0 power(-1,0:nh-3) zeros(1,nw) power(-1,0:nh-3) zeros(1,nw-1)];
0548 y = y + e* [zeros(1,nh) power(-1,0:nw-3) zeros(1,nh) power(-1,0:nw-3)];
0549 end
0550 fc = find_face_under_elec(mdl,elecs(i).pos);
0551 [u v s] = get_face_basis(mdl, fc);
0552
0553 np = length(x);
0554
0555
0556 ng_write_opt('meshoptions.fineness',1,'options.meshsize',1.2*elecs(i).maxh);
0557 emdl = ng_mk_2d_model(flipud([x', y']));
0558 x = emdl.nodes(:,1); y = emdl.nodes(:,2);
0559 elecs(i).nodes = ones(size(x)) * elecs(i).pos + x*s + y*v;
0560 elecs(i).elems = emdl.elems(:,[1 3 2]);
0561 elecs(i).points = elecs(i).nodes(1:np,:);
0562
0563
0564 end
0565 delete('ng.opt');
0566
0567 function [u v s] = get_face_basis(mdl, fc)
0568 u = mdl.normals(fc,:);
0569
0570
0571 I = eye(3);
0572 for i = 1:3
0573 proj(:,i) = I(:,i) - (dot(I(:,i),u')) * u';
0574 end
0575 norm_proj = vecnorm(proj);
0576 min_norm = min(norm_proj);
0577
0578
0579 if norm_proj(3) ~= min_norm
0580 v = [0 0 1] - dot([0 0 1],u) *u;
0581 else
0582
0583
0584
0585 v = [1 0 0] - dot([1 0 0],u)*u;
0586 end
0587 v = v/norm(v);
0588 s = cross(u,v); s= s/norm(s);
0589
0590 function [fc pos] = find_elec_centre(mdl, el_th,el_z)
0591 fc = [];
0592 pos = [];
0593
0594 Ctr = mean(mdl.nodes(mdl.boundary,:));
0595 Ctr(3) = el_z;
0596
0597
0598 n_above = mdl.nodes(:,3) >= el_z;
0599 sum_above = sum(n_above(mdl.edges),2) ;
0600 edg = sum_above == 1;
0601
0602
0603 n = unique(mdl.edges(edg,:));
0604 nn = mdl.nodes(n,1:2);
0605 nn = nn - repmat(Ctr(:,1:2),length(nn),1);
0606 th = cart2pol(nn(:,1),nn(:,2));
0607 th(:,2) = 1:length(th);
0608 th = sortrows(th);
0609 idx = find(th(:,1) > el_th,1,'first');
0610 if isempty(idx) || idx == 1
0611 n1 = n(th(1,2));
0612 n2 = n(th(end,2));
0613
0614
0615 ed = edg & sum( (mdl.edges == n1) + (mdl.edges == n2) ,2) > 0;
0616 else
0617
0618
0619
0620
0621 n1 = n(th(idx-1,2));
0622 n2 = n(th(idx, 2));
0623 ed = edg & sum( (mdl.edges == n1) + (mdl.edges == n2) ,2) > 0;
0624 end
0625
0626 el = false(length(mdl.boundary),1);
0627 for i = find(ed)'
0628 n1 = mdl.edges(i,1);
0629 n2 = mdl.edges(i,2);
0630 el = el | sum( (mdl.boundary == n1) + (mdl.boundary == n2), 2) == 2;
0631 end
0632 el = find(el);
0633
0634
0635
0636 [De(1) De(2) De(3)] = pol2cart(el_th,1, 0);
0637 for i = 1:length(el)
0638 Nf = mdl.normals(el(i),:);
0639 Cf = mdl.face_centre(el(i),:);
0640
0641
0642
0643
0644
0645
0646
0647 t = dot(Cf-Ctr,Nf) / dot(De,Nf);
0648 if t < 0, continue, end
0649 X = Ctr + De * t ;
0650 if point_in_triangle(X, mdl.faces(el(i),:), mdl.nodes)
0651 pos = X;
0652 fc = el(i);
0653 break;
0654 end
0655
0656
0657
0658 end
0659 if isempty(pos)
0660 keyboard
0661 end
0662
0663 function [out, fc] = grow_neighbourhood(mdl, varargin)
0664 use_elec = false;
0665 if length(varargin) == 1
0666 use_elec = true;
0667 elecs = varargin{1};
0668 fc = find_face_under_elec(mdl,elecs.pos);
0669 p = elecs.pos;
0670 switch elecs.shape
0671 case 'R'
0672 r = sqrt(sum(elecs.dims.^2,2));
0673 case 'C'
0674 r = 2 * elecs.dims(1);
0675 end
0676 else
0677 fc = varargin{1};
0678 p = varargin{2};
0679 r = varargin{3};
0680 end
0681
0682 done = false(length(mdl.boundary),1);
0683 todo = false(length(mdl.boundary),1);
0684 todo(fc) = true;
0685 bb = mdl.boundary;
0686 vv = mdl.nodes;
0687
0688
0689 dv = vv - repmat(p,length(vv),1);
0690 nl = mdl.normals;
0691 nl = repmat(nl(fc,:),length(vv),1);
0692 dd = sqrt(sum( (dv - repmat(dot(dv,nl,2),1,3) .* nl).^2,2));
0693 dim = size(bb,2);
0694 first = true;
0695 if use_elec
0696 PN = level_model_slice(mdl.nodes,struct('centre',mdl.face_centre(fc,:),'normal',mdl.normals(fc,:)));
0697 elec_pts = level_model_slice(elecs.points,struct('centre',mdl.face_centre(fc,:),'normal',mdl.normals(fc,:)));
0698 elec_pts = elec_pts(:,1:2);
0699 PN = PN(:,1:2);
0700 emin = min(elec_pts);
0701 emax = max(elec_pts);
0702 rng = emax-emin;
0703 emin = emin - 0.1*rng;
0704 emax = emax + 0.1*rng;
0705 toofar = false(size(mdl.boundary,1),1);
0706
0707 for i = 1:2
0708 nodes = reshape(PN(mdl.boundary,i),[],3);
0709 toofar = toofar | sum(nodes > emax(i),2) == 3 | sum(nodes < emin(i),2) == 3;
0710 end
0711 end
0712 while any(todo)
0713 id = find(todo,1,'first');
0714 done(id) = 1;
0715 nn = find_neighbours(id,bb);
0716 if use_elec
0717 nn = nn & ~toofar;
0718 elseif first
0719
0720 first = false;
0721 else
0722
0723 nn = nn & sum(dd(bb) <= r,2) > 0;
0724 end
0725 todo = todo | nn;
0726 todo(done) = 0;
0727
0728
0729
0730 end
0731 out = find(done);
0732
0733
0734 function nn = find_neighbours(fc, bb);
0735 dim = size(bb,2);
0736 nn = false(length(bb),1);
0737 for i = 1:dim
0738 node = bb(fc,i);
0739 nn = nn | sum(bb == node,2) > 0;
0740 end
0741 nn(fc) = 0;
0742
0743 function [e p] = find_face_under_elec(mdl, elec_pos)
0744 for i = 1:size(elec_pos,1)
0745
0746 ee = repmat(elec_pos(i,:),length(mdl.faces),1);
0747 fc = mdl.face_centre;
0748 n = mdl.normals;
0749 proj1 = ee - repmat(dot(ee-fc, n,2),1,3) .* n;
0750 in1 = point_in_triangle(proj1,mdl.faces,mdl.nodes, 'match');
0751 dis1 = sqrt(sum((ee-proj1).^2,2));
0752
0753 edg = [mdl.faces(:,1:2);mdl.faces(:,2:3);mdl.faces(:,[3 1])];
0754 edg = sort(edg,2);
0755 [edg jnk e2f] = unique(edg,'rows');
0756 ee = repmat(elec_pos(i,:),length(edg),1);
0757 s = mdl.nodes(edg(:,2),:) - mdl.nodes(edg(:,1),:);
0758 t = dot(ee-mdl.nodes(edg(:,1),:),s,2)./dot(s,s,2);
0759 in2 = t>=0 & t <=1;
0760 in2 = any(reshape(in2(e2f),[],3),2);
0761 proj2 = mdl.nodes(edg(:,1),:) + repmat(t,1,3).*s;
0762 dis = sqrt(sum((ee - proj2).^2,2));
0763 dis = repmat(dis,2,1);
0764 dis(t<0 | t > 1) = Inf;
0765 dis = reshape(dis(e2f),[],3);
0766 [jnk, pos] = min(dis,[],2);
0767 idx = sparse(1:length(pos),pos,1);
0768 dis = dis';
0769 dis2 = dis(logical(idx'));
0770
0771 in = in1 | in2;
0772 if nnz(in) == 1
0773 e(i) = find(in1);
0774 p(i,:) = proj1(in1,:);
0775 else
0776
0777 cand = find(in);
0778 dd(in1(cand)) = dis1(in1);
0779 dd(in2(cand)) = dis2(in2);
0780 [jnk pos] = min(dd);
0781 e(i) = cand(pos);
0782 p(i,:) = proj1(e(i),:);
0783 end
0784
0785 end
0786
0787
0788 function do_unit_test
0789 xy= [ -0.89 -0.74 -0.21 0.31 0.79 0.96 0.67 0.05 -0.36 -0.97;
0790 0.14 0.51 0.35 0.50 0.27 -0.23 -0.86 -0.69 -0.85 -0.46]';
0791 [fmdl] = ng_mk_extruded_model({2,xy,[4,80],},[],[]);
0792 elec_pos = [-0.5, -0.8, 1];
0793
0794
0795 mdl = place_elec_on_surf(fmdl, [16 0 1], [0.15 0.1 0.01]);
0796
0797 subplot(121)
0798 show_fem(mdl);
0799
0800 mdl = place_elec_on_surf(fmdl, [16 0 1], [0.15 0.1 0.01],[],0.1);
0801
0802 subplot(122)
0803 show_fem(mdl);