├── .gitignore
├── ReadMe.md
├── mesh2d
├── LICENSE
├── README.md
├── aabb-tree
│ ├── drawtree.m
│ ├── findball.m
│ ├── findline.m
│ ├── findtria.m
│ ├── lineline.m
│ ├── linenear.m
│ ├── maketree.m
│ ├── maprect.m
│ ├── mapvert.m
│ ├── queryset.m
│ └── scantree.m
├── bfsgeo2.m
├── bfstri2.m
├── cdtbal1.m
├── cdtbal2.m
├── cfmtri2.m
├── compile.m
├── deltri2.m
├── drawscr.m
├── fixgeo2.m
├── getnan2.m
├── idxtri2.m
├── inpoly2.m
├── inpoly2_oct.cpp
├── isfeat2.m
├── lfshfn2.m
├── limgrad.m
├── limhfn2.m
├── mesh-file
│ ├── loadmsh.m
│ └── savemsh.m
├── minlen2.m
├── poly-data
│ ├── airfoil.msh
│ ├── islands.msh
│ ├── lake-1-small.png
│ ├── lake-2-small.png
│ ├── lake.msh
│ ├── river.msh
│ └── wavy-channel.msh
├── refine2.m
├── relhfn2.m
├── setset2.m
├── smooth2.m
├── triang2.m
├── triarea.m
├── tribal2.m
├── tricon2.m
├── trideg2.m
├── tridemo.m
├── tridiv2.m
├── trihfn2.m
├── triread.m
└── triscr2.m
└── src
├── GenerateBasicData.m
├── RateStickEvolve.m
├── Reinitialize2D.m
├── ThreeCirclesEvolve.m
├── UShapedAnalytical.m
├── UShapedEvolve.m
├── buildConnection.m
├── buildMatrixA.m
├── buildOutgoingEdges.m
├── calcGradient.m
├── config.m
├── dcircle.m
├── ddiff.m
├── distLineSeg2Point.m
├── distance2curve.m
├── drawResult.m
├── drectangle.m
├── dunion.m
├── findContourInTriangle.m
├── findZeroOnEdge.m
├── generateTriangleNorms.m
├── rayLineSegmentIntersection.m
├── resultData
└── ReadMe.txt
├── triangleOutNorm.m
└── tricontour.m
/.gitignore:
--------------------------------------------------------------------------------
1 | *.mat
2 |
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 | # Unstructured Level Set Method
2 | Robust 3D Level Set Method for Evolving Fronts on Complex Unstructured Meshes
3 |
4 | # Introduction
5 |
6 | This repository contains demonstration code of our robust 3D level set method on complex unstructured meshes.
7 |
8 | The main advantage of our implementation is its ability to handle discontinuous propagation speed, sharp geometric feature, and the geometries which start from the boundary of mesh.
9 |
10 | # Usage
11 |
12 | 0. Download the code and `cd` to the downloaded directory in `MATLAB`.
13 | 1. In `src/config.m` edit the simulation configurations such as mesh scale.
14 | 2. Run `src/GenerateBasicData.m`, which generates a series of `*.mat` files which contains the generated unstructured mesh.
15 | 3. Run any of the `src/*Evolve.m` files, which perform the actual level set computation for corresponding problems. The results will be stored in `src/resultData` directory.
16 | 4. Run script `drawResult.m`, which draws the result of each example. Note that each example is corresponding to a boolean switch called `draw*` in this file. You may have to set the specific switch to `true` in order to see corresponding results.
17 | 5. `Reinitialize2D.m` contains an independent example, which is not described in our article. You can run the file directly (after running src/GenerateBasicData.m) to see the output figure.
18 | 6. Other files are auxiliary functions, of which the file names are quite self-explanatory.
19 |
20 |
21 | # Acknowledgment
22 |
23 | + We have used [MESH2D](https://github.com/dengwirda/mesh2d) to generate the required mesh.
24 | + The [distance2curve](https://ww2.mathworks.cn/matlabcentral/fileexchange/34869-distance2curve) function is written by [John D'Errico](https://ww2.mathworks.cn/matlabcentral/profile/authors/869215-john-d-errico).
25 | + Functions `dunion`, `ddiff`, `dcircle`, `drectangle` are borrowed from [DistMesh](http://persson.berkeley.edu/distmesh/) toolkit.
26 | + Function [tricontour](https://ww2.mathworks.cn/matlabcentral/fileexchange/10408-contours-for-triangular-grids) is written by [Darren Engwirda](https://ww2.mathworks.cn/matlabcentral/profile/authors/870403-darren-engwirda).
27 |
28 | # Citation
29 |
30 | If any of the provided code does help in your research, please cite the following paper:
31 |
32 | [1] Wei, R., Bao, F., Liu, Y., Hui, W., 2018. Robust Three-Dimensional Level-Set Method for Evolving Fronts on Complex Unstructured Meshes. Mathematical Problems in Engineering 2018, 1–15. https://doi.org/10.1155/2018/2730829
33 |
34 | BibTex item:
35 |
36 | @article{weiRobustThreeDimensionalLevelSet2018,
37 | title = {Robust {{Three}}-{{Dimensional Level}}-{{Set Method}} for {{Evolving Fronts}} on {{Complex Unstructured Meshes}}},
38 | volume = {2018},
39 | copyright = {All rights reserved},
40 | issn = {1024-123X, 1563-5147},
41 | doi = {10.1155/2018/2730829},
42 | abstract = {With a purpose to evolve the surfaces of complex geometries in their normal direction at arbitrarily defined velocities, we have developed a robust level-set approach which runs on three-dimensional unstructured meshes. The approach is built on the basis of an innovative spatial discretization and corresponding gradient-estimating approach. The numerical consistency of the estimating method is mathematically proven. A correction technology is utilized to improve accuracy near sharp geometric features. Validation tests show that the proposed approach is able to accurately handle geometries containing sharp features, computation regions having irregular shapes, discontinuous speed fields, and topological changes. Results of the test problems fit well with the reference results produced by analytical or other numerical methods and converge to reference results as the meshes refine. Compared to level-set method implementations on Cartesian meshes, the proposed approach makes it easier to describe jump boundary conditions and to perform coupling simulations.},
43 | language = {en},
44 | journal = {Mathematical Problems in Engineering},
45 | author = {Wei, Ran and Bao, Futing and Liu, Yang and Hui, Weihua},
46 | month = sep,
47 | year = {2018},
48 | pages = {1-15}
49 | }
50 |
--------------------------------------------------------------------------------
/mesh2d/README.md:
--------------------------------------------------------------------------------
1 | ## `MESH2D: Delaunay-based mesh generation in MATLAB`
2 |
3 | `MESH2D` is a `MATLAB` / `OCTAVE`-based unstructured mesh-generator for two-dimensional polygonal geometries, providing a range of relatively simple, yet effective two-dimensional meshing algorithms. `MESH2D` includes variations on the "classical" Delaunay refinement technique, a new "Frontal"-Delaunay refinement scheme, a non-linear mesh optimisation method, and auxiliary mesh and geometry pre- and post-processing facilities.
4 |
5 |
6 |
       
7 |
8 |
9 |
10 | Algorithms implemented in `MESH2D` are "provably-good" - ensuring convergence, geometrical and topological correctness, and providing guarantees on algorithm termination and worst-case element quality bounds. Support for user-defined "mesh-spacing" functions and "multi-part" geometry definitions is also provided, allowing `MESH2D` to handle a wide range of complex domain types and user-defined constraints. `MESH2D` typically generates very high-quality output, appropriate for a variety of finite-volume/element type applications.
11 |
12 | `MESH2D` is a simplified version of my `JIGSAW` mesh-generation algorithm (a `C++` code). `MESH2D` aims to provide a straightforward `MATLAB` / `OCTAVE` implementation of these Delaunay-based triangulation and mesh optimisation techniques.
13 |
14 | ## `Code Structure`
15 |
16 | `MESH2D` is a pure `MATLAB` / `OCATVE` package, consisting of a core library + associated utilities:
17 |
18 | MESH2D::
19 | ├── MAIN-DIR. -- core MESH2D library functions. See REFINE2, SMOOTH2 and TRIDEMO, etc.
20 | ├── aabb-tree -- support for fast spatial indexing, via tree-based data-structures.
21 | ├── mesh-file -- support for mesh file text serialisation.
22 | └── poly-data -- geometry data for example problems, image cache, etc.
23 |
24 | ## `Starting Out`
25 |
26 | After downloading and unzipping the current repository, navigate to the installation directory within `MATLAB` / `OCTAVE` and run the set of examples contained in `tridemo.m`:
27 | ```
28 | tridemo( 0); % a very simple example to get everything started.
29 | tridemo( 1); % investigate the impact of the "radius-edge" threshold.
30 | tridemo( 2); % Frontal-Delaunay vs. Delaunay-refinement refinement.
31 | tridemo( 3); % explore impact of user-defined mesh-size constraints.
32 | tridemo( 4); % explore impact of "hill-climbing" mesh optimisations.
33 | tridemo( 5); % assemble triangulations for "multi-part" geometries.
34 | tridemo( 6); % assemble triangulations with "internal" constraints.
35 | tridemo( 7); % investigate the use of "quadtree"-type refinement.
36 | tridemo( 8); % explore use of custom, user-defined mesh-size functions.
37 | tridemo( 9); % larger-scale problem, mesh refinement + optimisation.
38 | tridemo(10); % medium-scale problem, mesh refinement + optimisation.
39 | ```
40 |
41 | For `OCTAVE` users, performance can be improved by compiling elements of the `MESH2D` library. Running `compile.m` within the `MESH2D` installation directory will complete the build process (note: requires a `-dev` installation of `OCTAVE`).
42 |
43 | ## `References!`
44 |
45 | If you make use of `MESH2D` please include a reference to the following! `MESH2D` is designed to provide a simple and easy-to-understand implementation of Delaunay-based mesh-generation techniques. For a much more advanced, and fully three-dimensional mesh-generation library, see the `JIGSAW` package. `MESH2D` makes use of the `AABBTREE` and `FINDTRIA` packages to compute efficient spatial queries and intersection tests.
46 |
47 | `[1]` - Darren Engwirda, Locally-optimal Delaunay-refinement and optimisation-based mesh generation, Ph.D. Thesis, School of Mathematics and Statistics, The University of Sydney, September 2014.
48 |
49 | `[2]` - Darren Engwirda, Unstructured mesh methods for the Navier-Stokes equations, Honours Thesis, School of Aerospace, Mechanical and Mechatronic Engineering, The University of Sydney, November 2005.
50 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/drawtree.m:
--------------------------------------------------------------------------------
1 | function drawtree(tr,varargin)
2 | %DRAWTREE draw an aabb-tree generated using MAKETREE.
3 | % DRAWTREE(TR) draws the tree TR for cases in R^2 and R^3.
4 | %
5 | % See also MAKETREE
6 |
7 | % Darren Engwirda : 2014 --
8 | % Email : darren.engwirda@columbia.edu
9 | % Last updated : 18/12/2014
10 |
11 | %---------------------------------------------- basic checks
12 | if (~isa(tr,'struct') || ...
13 | ~isfield(tr,'xx') || ...
14 | ~isfield(tr,'ii') || ...
15 | ~isfield(tr,'ll') )
16 | error('drawtree:incorrectInputClass', ...
17 | 'Incorrect aabb-tree.') ;
18 | end
19 |
20 | %----------------------------------------- find "leaf" nodes
21 | lf = ~cellfun('isempty', tr.ll) ;
22 |
23 | fc = [.95,.95,.55] ;
24 | ec = [.15,.15,.15] ;
25 |
26 | %-------------------------- draw all "leaf" nodes as patches
27 | switch (size(tr.xx,2))
28 | case 4
29 | %----------------------------------------------- tree in R^2
30 | np = numel(find(lf));
31 | %------------------------------------------------- nodes
32 | pp = [tr.xx(lf,1),tr.xx(lf,2)
33 | tr.xx(lf,3),tr.xx(lf,2)
34 | tr.xx(lf,3),tr.xx(lf,4)
35 | tr.xx(lf,1),tr.xx(lf,4)
36 | ] ;
37 | %------------------------------------------------- faces
38 | bb = [(1:np)'+np*0,(1:np)'+np*1,...
39 | (1:np)'+np*2,(1:np)'+np*3
40 | ] ;
41 |
42 | case 6
43 | %----------------------------------------------- tree in R^3
44 | np = numel(find(lf));
45 | %------------------------------------------------- nodes
46 | pp = [tr.xx(lf,1),tr.xx(lf,2),tr.xx(lf,3)
47 | tr.xx(lf,4),tr.xx(lf,2),tr.xx(lf,3)
48 | tr.xx(lf,4),tr.xx(lf,5),tr.xx(lf,3)
49 | tr.xx(lf,1),tr.xx(lf,5),tr.xx(lf,3)
50 | tr.xx(lf,1),tr.xx(lf,2),tr.xx(lf,6)
51 | tr.xx(lf,4),tr.xx(lf,2),tr.xx(lf,6)
52 | tr.xx(lf,4),tr.xx(lf,5),tr.xx(lf,6)
53 | tr.xx(lf,1),tr.xx(lf,5),tr.xx(lf,6)
54 | ] ;
55 | %------------------------------------------------- faces
56 | bb = [(1:np)'+np*0,(1:np)'+np*1,...
57 | (1:np)'+np*2,(1:np)'+np*3
58 | (1:np)'+np*4,(1:np)'+np*5,...
59 | (1:np)'+np*6,(1:np)'+np*7
60 | (1:np)'+np*0,(1:np)'+np*3,...
61 | (1:np)'+np*7,(1:np)'+np*4
62 | (1:np)'+np*3,(1:np)'+np*2,...
63 | (1:np)'+np*6,(1:np)'+np*7
64 | (1:np)'+np*2,(1:np)'+np*1,...
65 | (1:np)'+np*5,(1:np)'+np*6
66 | (1:np)'+np*1,(1:np)'+np*0,...
67 | (1:np)'+np*4,(1:np)'+np*5
68 | ] ;
69 |
70 | otherwise
71 | %--------------------------- what to do with a tree in R^d!?
72 | error('scantree:unsupportedDimension', ...
73 | 'Unsupported tree dimensionality.') ;
74 | end
75 |
76 | patch('faces',bb,'vertices',pp,'facecolor',fc,...
77 | 'edgecolor',ec,'facealpha',+.2);
78 |
79 | end
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/findball.m:
--------------------------------------------------------------------------------
1 | function [bp,bj,tr] = findball(bb,pp,varargin)
2 | %FINDBALL spatial queries for collections of d-balls.
3 | % [BP,BI] = FINDBALL(BB,PI) finds the set of d-dim. balls
4 | % that intersect with a given spatial query. Balls are sp-
5 | % ecified as a set of centres BB(:,1:ND) and (squared)
6 | % radii BB(:,ND+1), where ND is the number of dimensions.
7 | %
8 | % A set of intersecting balls is returned for each query
9 | % point in PI, such that the II-th point is associated
10 | % with the balls BI(BP(II,1):BP(II,2)). Unenclosed points
11 | % have BP(II,1) == 0.
12 | %
13 | % [BP,BI,TR] = FINDBALL(BB,PI) additionally returns the
14 | % supporting aabb-tree used internally to compute the que-
15 | % ry. If the underlying collection BB is static, the tree
16 | % TR may be passed to subsequent calls, via [BP,BI,TR] =
17 | % FINDBALL(BB,PI,TR). This syntax may lead to improved pe-
18 | % rformance, especially when the number of balls is large
19 | % w.r.t. the number of query points. Note that in such ca-
20 | % ses the distribution of underlying balls is NOT permitt-
21 | % ed to change between calls, or erroneous results may be
22 | % returned. Additional parameters used to control the cre-
23 | % ation of the underlying aabb-tree may also be passed via
24 | % [...] = FINDBALL(BB,PI,TR,OP). See MAKETREE for additio-
25 | % nal information.
26 | %
27 | % See also MAKETREE, FINDTRIA
28 |
29 | % Please see the following for additional information:
30 | %
31 | % Darren Engwirda, "Locally-optimal Delaunay-refinement &
32 | % optimisation-based mesh generation". Ph.D. Thesis, Scho-
33 | % ol of Mathematics and Statistics, Univ. of Sydney, 2014:
34 | % http://hdl.handle.net/2123/13148
35 |
36 | % Darren Engwirda : 2017 --
37 | % Email : de2363@columbia.edu
38 | % Last updated : 27/04/2017
39 |
40 | bp = []; bj = []; tr = []; op = [];
41 |
42 | %---------------------------------------------- basic checks
43 | if (nargin < +2 || nargin > +4)
44 | error('findball:incorrectNumInputs', ...
45 | 'Incorrect number of inputs.');
46 | end
47 |
48 | if (nargin >= +3), tr = varargin{1}; end
49 | if (nargin >= +4), op = varargin{2}; end
50 |
51 | %------------------------------ quick return on empty inputs
52 | if (isempty(bb)), return; end
53 |
54 | %---------------------------------------------- basic checks
55 | if (~isnumeric(bb) || ~isnumeric(pp))
56 | error('findball:incorrectInputClass', ...
57 | 'Incorrect input class.') ;
58 | end
59 |
60 | if (ndims(bb) ~= +2 || size(bb,2) < +3 )
61 | error('findball:incorrectDimensions', ...
62 | 'Incorrect input dimensions.');
63 | end
64 | if (ndims(pp) ~= +2 || ...
65 | size(bb,2) ~= size(pp,2)+1)
66 | error('findball:incorrectDimensions', ...
67 | 'Incorrect input dimensions.');
68 | end
69 |
70 | if (~isempty(tr) && ~isstruct(tr) )
71 | error('findball:incorrectInputClass', ...
72 | 'Incorrect input class.') ;
73 | end
74 | if (~isempty(op) && ~isstruct(op) )
75 | error('findball:incorrectInputClass', ...
76 | 'Incorrect input class.') ;
77 | end
78 |
79 | if (isempty(tr))
80 | %------------------------------ compute aabb-tree for d-ball
81 | nd = size(pp,2) ;
82 | rs = sqrt(bb(:,nd+1));
83 | rs = rs(:,ones(1,nd));
84 | ab =[bb(:,1:nd)-rs, ... % compute aabb-tree
85 | bb(:,1:nd)+rs];
86 |
87 | tr = maketree(ab,op) ;
88 | end
89 |
90 | %------------------------------ compute tree-to-vert mapping
91 | tm = mapvert (tr,pp);
92 |
93 | %------------------------------ compute vert-to-ball queries
94 | [bi,ip,bj] = ...
95 | queryset (tr,tm,@ballkern,pp,bb) ;
96 |
97 | %------------------------------ re-index onto full obj. list
98 | bp = zeros(size(pp,1),2) ;
99 | bp( :,2) = -1 ;
100 |
101 | if (isempty(bi)), return ; end
102 |
103 | bp(bi,:) = ip ;
104 |
105 | end
106 |
107 | function [ip,ib] = ballkern(pk,bk,pp,bb)
108 | %BALLKERN d-dim. ball-vert intersection kernel routine.
109 |
110 | mp = length(pk);
111 | mb = length(bk);
112 |
113 | nd = size(pp,2);
114 |
115 | %-------------------------- push ball/vert onto n*m tile
116 | pk = pk.' ;
117 |
118 | pk = pk(ones(mb,1),:);
119 | pk = pk(:);
120 | bk = bk(:,ones(1,mp));
121 | bk = bk(:);
122 |
123 | %-------------------------- compute O(n*m) loc. distance
124 | dd = ...
125 | sum((pp(pk,+1:nd)-bb(bk,+1:nd)).^2,2);
126 |
127 | in = dd<=bb(bk,nd+1) ;
128 |
129 | ip = pk(in) ;
130 | ib = bk(in) ;
131 |
132 | end
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/findline.m:
--------------------------------------------------------------------------------
1 | function [lp,lj,tr] = findline(pa,pb,pp,varargin)
2 | %FINDLINE "point-on-line" queries in d-dimensional space.
3 | % [LP,LI] = FINDLINE(PA,PB,PI) finds the set of d-dimensi-
4 | % onal line-segments that intersect with a given spatial
5 | % query. Lines are specified as a set of endpoints [PA,PB]
6 | % where both PA and PB are NL-by-ND arrays of coordinates,
7 | % where ND is the number of dimensions.
8 | %
9 | % A set of intersecting lines is returned for each query
10 | % point in PI, such that the II-th point is associated
11 | % with the lines LI(LP(II,1):LP(II,2)). Unenclosed points
12 | % have LP(II,1) == 0.
13 | %
14 | % [LP,LI,TR] = FINDLINE(PA,PB,PI) additionally returns the
15 | % supporting aabb-tree used internally to compute the que-
16 | % ry. If the underlying collection [PA,PB] is static, the
17 | % tree TR may be passed to subsequent calls, via
18 | % [LP,LI,TR] = FINDLINE(PA,PB,PI,TR). This syntax may lead
19 | % to improved performance, especially when the number of
20 | % lines is large w.r.t. the number of query points. Note
21 | % that in such cases the distribution of underlying lines
22 | % is NOT permitted to change between calls, or erroneous
23 | % results may be returned. Additional parameters used to
24 | % govern the creation of the underlying aabb-tree may be
25 | % passed via [...] = FINDLINE(...,TR,OP). See MAKETREE for
26 | % additional information.
27 | %
28 | % See also MAKETREE, FINDTRIA, FINDBALL, LINELINE
29 |
30 | % Please see the following for additional information:
31 | %
32 | % Darren Engwirda, "Locally-optimal Delaunay-refinement &
33 | % optimisation-based mesh generation". Ph.D. Thesis, Scho-
34 | % ol of Mathematics and Statistics, Univ. of Sydney, 2014:
35 | % http://hdl.handle.net/2123/13148
36 |
37 | %-----------------------------------------------------------
38 | % Darren Engwirda : 2017 --
39 | % Email : de2363@columbia.edu
40 | % Last updated : 02/08/2017
41 | %-----------------------------------------------------------
42 |
43 | lp = []; lj = []; tr = []; op = [];
44 |
45 | %---------------------------------------------- basic checks
46 | if (nargin < +3 || nargin > +5)
47 | error('findline:incorrectNumInputs', ...
48 | 'Incorrect number of inputs.');
49 | end
50 |
51 | if (nargin >= +4), tr = varargin{1}; end
52 | if (nargin >= +5), op = varargin{2}; end
53 |
54 | %------------------------------ quick return on empty inputs
55 | if (isempty(pa)), return; end
56 | if (isempty(pb)), return; end
57 |
58 | %---------------------------------------------- basic checks
59 | if (~isnumeric(pa) || ~isnumeric(pb) || ...
60 | ~isnumeric(pp) )
61 | error('findline:incorrectInputClass', ...
62 | 'Incorrect input class.') ;
63 | end
64 |
65 | if (ndims(pa) ~= +2 || size(pa,2) < +2 || ...
66 | ndims(pb) ~= +2 || size(pb,2) < +2 )
67 | error('findline:incorrectDimensions', ...
68 | 'Incorrect input dimensions.');
69 | end
70 | if (ndims(pp) ~= +2 || ...
71 | size(pa,1) ~= size(pb,1) || ...
72 | size(pa,2) ~= size(pp,2) || ...
73 | size(pb,2) ~= size(pp,2) )
74 | error('findline:incorrectDimensions', ...
75 | 'Incorrect input dimensions.');
76 | end
77 |
78 | if (~isempty(tr) && ~isstruct(tr) )
79 | error('findline:incorrectInputClass', ...
80 | 'Incorrect input class.') ;
81 | end
82 | if (~isempty(op) && ~isstruct(op) )
83 | error('findline:incorrectInputClass', ...
84 | 'Incorrect input class.') ;
85 | end
86 |
87 | if (isempty(tr))
88 | %------------------------------ compute aabb-tree for d-line
89 | nd = size(pp,2) ;
90 | nl = size(pa,1) ;
91 |
92 | ab = zeros(nl,nd*2); % compute aabb-tree
93 | for ax = +1:nd
94 | ab(:,ax+nd*0) = min(pa(:,ax), ...
95 | pb(:,ax)) ;
96 | ab(:,ax+nd*1) = max(pa(:,ax), ...
97 | pb(:,ax)) ;
98 | end
99 | tr = maketree(ab,op) ;
100 | end
101 |
102 | %------------------------------ compute tree-to-vert mapping
103 | tm = mapvert (tr,pp);
104 |
105 | %------------------------------ compute intersection rel-tol
106 | p0 = min([pa; pb],[],+1) ;
107 | p1 = max([pa; pb],[],+1) ;
108 |
109 | zt = max(p1-p0) * eps^.8 ;
110 |
111 | %------------------------------ compute vert-to-line queries
112 | [li,ip,lj] = queryset( ...
113 | tr,tm,@linekern,pp,pa,pb,zt) ;
114 |
115 | %------------------------------ re-index onto full obj. list
116 | lp = zeros(size(pp,1),2) ;
117 | lp( :,2) = -1 ;
118 |
119 | if (isempty(li)), return ; end
120 |
121 | lp(li,:) = ip ;
122 |
123 | end
124 |
125 | function [ip,il] = linekern(pk,lk,pp,pa,pb,zt)
126 | %LINEKERN d-dim. node//line intersection kernel routine.
127 |
128 | mp = length(pk);
129 | ml = length(lk);
130 |
131 | %-------------------------- push line/vert onto n*m tile
132 | pk = pk.' ;
133 |
134 | pk = pk(ones(ml,1),:);
135 | pk = pk(:);
136 | lk = lk(:,ones(1,mp));
137 | lk = lk(:);
138 |
139 | %-------------------------- compute O(n*m) intersections
140 | mm = (pa(lk,:)+pb(lk,:)) * +.5 ;
141 | DD = (pb(lk,:)-pa(lk,:)) * +.5 ;
142 |
143 | mp = mm-pp(pk,:) ;
144 |
145 | tt =-sum(mp.*DD,2)./ ...
146 | sum(DD.*DD,2) ;
147 | tt = max(min(tt,+1.),-1.);
148 |
149 | nd = size(pp,2);
150 |
151 | qq = mm + repmat(tt,1,nd) .* DD;
152 |
153 | on = ...
154 | sum((pp(pk,:)-qq).^2,2) <= zt^2;
155 |
156 | ip = pk(on) ;
157 | il = lk(on) ;
158 |
159 | end
160 |
161 |
162 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/findtria.m:
--------------------------------------------------------------------------------
1 | function [tp,tj,tr] = findtria(pp,tt,pj,varargin)
2 | %FINDTRIA spatial queries for collections of d-simplexes.
3 | % [TP,TI] = FINDTRIA(PP,TT,PJ) finds the set of simple-
4 | % xes that intersect with a given spatial query. Simplexes
5 | % are specified via the vertex array PP = [X1,X2,...,XN]
6 | % and the indexing array TT = [T1,T2,...,TM], such that
7 | % the vertex positions for the II-th simplex are the poin-
8 | % ts [PP(TT(II,1),:),PP(TT(II,2),:),...,PP(TT(II,M),:)].
9 | %
10 | % Simplexes are NOT required to form a conforming triangu-
11 | % lation. Specifically, non-delaunay, non-convex and even
12 | % overlapping configurations are supported. Multiple matc-
13 | % hes may be returned if the collection is overlapping.
14 | %
15 | % A set of intersecting simplexes is returned for each
16 | % query point in PI, such that the II-th point is associa-
17 | % ted with the simplexes TI(TP(II,1):TP(II,2)). Unenclosed
18 | % points have TP(II,1)==+0.
19 | %
20 | % In general, query points may be matched to multiple sim-
21 | % plexes, but in cases when single matches are guaranteed,
22 | % or if only a single match is desired, the following ret-
23 | % urns a singly-matched indexing array that is consistent
24 | % with MATLAB's existing point-location routines:
25 | %
26 | % [tp,tj] = findtria(pp,tt,pj) ;
27 | % ti = nan(size(tp,1),1);
28 | % in = tp(:,1) > +0;
29 | % ti(in) = tj(tp(in,+1));
30 | %
31 | % [TP,TI,TR] = FINDTRIA(PP,TT,PI) additionally returns the
32 | % supporting aabb-tree used internally to compute the que-
33 | % ry. If the underlying collection [PP,TT] is static, the
34 | % tree TR may be passed to subsequent calls, via [...] =
35 | % FINDTRIA(PP,TT,PJ,TR). This syntax may lead to improved
36 | % performance, especially when the number of simplexes
37 | % is large w.r.t. the number of query points. Note that in
38 | % such cases the underlying simplexes are NOT permitted to
39 | % change between calls, or erroneous results may be retur-
40 | % ned. Additional parameters used to control the creation
41 | % of the underlying aabb-tree may also be passed via [...]
42 | % = FINDTRIA(PP,TT,PI,TR,OP). See MAKETREE for additional
43 | % information.
44 | %
45 | % See also MAKETREE, QUERYSET
46 |
47 | % Please see the following for additional information:
48 | %
49 | % Darren Engwirda, "Locally-optimal Delaunay-refinement &
50 | % optimisation-based mesh generation". Ph.D. Thesis, Scho-
51 | % ol of Mathematics and Statistics, Univ. of Sydney, 2014:
52 | % http://hdl.handle.net/2123/13148
53 |
54 | % Darren Engwirda : 2014 --
55 | % Email : de2363@columbia.edu
56 | % Last updated : 10/03/2018
57 |
58 | tp = []; tj = []; tr = []; op = [];
59 |
60 | %---------------------------------------------- basic checks
61 | if (nargin < +3 || nargin > +6)
62 | error('queryset:incorrectNumInputs', ...
63 | 'Incorrect number of inputs.');
64 | end
65 |
66 | %------------------------------- fast return on empty inputs
67 | if (isempty(pj)), return; end
68 |
69 | %------------------------------- extract user-defined inputs
70 | if (nargin >= +4), tr = varargin{1}; end
71 | if (nargin >= +5), op = varargin{2}; end
72 |
73 | %---------------------------------------------- basic checks
74 | if (~isnumeric(pp) || ~isnumeric(tt) || ...
75 | ~isnumeric(pj) )
76 | error('findtria:incorrectInputClass', ...
77 | 'Incorrect input class.') ;
78 | end
79 |
80 | %---------------------------------------------- basic checks
81 | if (ndims(pp) ~= +2 || size(pp,2) < +2 ...
82 | || size(pp,2) > size(tt,2))
83 | error('findtria:incorrectDimensions', ...
84 | 'Incorrect input dimensions.');
85 | end
86 | if (ndims(tt) ~= +2 || size(tt,2) < +3)
87 | error('findtria:incorrectDimensions', ...
88 | 'Incorrect input dimensions.');
89 | end
90 |
91 | %------------------------------- test query array for VERT's
92 | if (ndims(pj) ~= +2 || ...
93 | size(pj,2) ~= size(pp,2) )
94 | error('findtria:incorrectDimensions', ...
95 | 'Incorrect input dimensions.');
96 | end
97 |
98 | %---------------------------------------------- basic checks
99 | if (~isempty(tr) && ~isstruct(tr) )
100 | error('findtria:incorrectInputClass', ...
101 | 'Incorrect input class.') ;
102 | end
103 | if (~isempty(op) && ~isstruct(op) )
104 | error('findtria:incorrectInputClass', ...
105 | 'Incorrect input class.') ;
106 | end
107 |
108 | if (isempty(tr))
109 | %------------------------------ compute aabb's for triangles
110 | bi = pp(tt(:,1),:); bj = pp(tt(:,1),:);
111 | for ii = 2 : size(tt,2)
112 | bi = min(bi,pp(tt(:,ii),:)) ;
113 | bj = max(bj,pp(tt(:,ii),:)) ;
114 | end
115 | bb = [bi,bj];
116 |
117 | tr = maketree(bb,op); % compute aabb-tree
118 | end
119 |
120 | %------------------------------ compute tree-to-vert mapping
121 | tm = mapvert (tr,pj);
122 |
123 | %------------------------------ compute vert-to-tria queries
124 | x0 = min(pp,[],1);
125 | x1 = max(pp,[],1);
126 | rt = prod(x1 - x0) * eps^.8 ;
127 |
128 | [ti,ip,tj] = ...
129 | queryset(tr,tm,@triakern,pj,pp,tt,rt) ;
130 |
131 | %------------------------------ re-index onto full obj. list
132 | tp = zeros(size(pj,1),2);
133 | tp( :,2) = -1 ;
134 |
135 | if (isempty(ti)), return; end
136 |
137 | tp(ti,:) = ip ;
138 |
139 | end
140 |
141 | function [ip,it] = triakern(pk,tk,pi,pp,tt,rt)
142 | %TESTPTS compute the "point-tria" matches within a tile.
143 |
144 | mp = length(pk); mt = length(tk);
145 |
146 | switch (size(tt,2))
147 | case 3
148 | %------------------------------------ pts in 2-simplexes
149 | pk = pk.' ;
150 | pk = pk(ones(mt,1),:);
151 | pk = pk(:);
152 | tk = tk(:,ones(1,mp));
153 | tk = tk(:);
154 |
155 | in = intria2( ...
156 | pp,tt(tk,:),pi(pk,:),rt);
157 |
158 | ip = pk(in) ;
159 | it = tk(in) ;
160 |
161 | case 4
162 | %------------------------------------ pts in 3-simplexes
163 | pk = pk.' ;
164 | pk = pk(ones(mt,1),:);
165 | pk = pk(:);
166 | tk = tk(:,ones(1,mp));
167 | tk = tk(:);
168 |
169 | in = intria3( ...
170 | pp,tt(tk,:),pi(pk,:),rt);
171 |
172 | ip = pk(in) ;
173 | it = tk(in) ;
174 |
175 | otherwise
176 | %------------------------------------ pts in d-simplexes
177 | [il,jl] = intrian( ...
178 | pp,tt(tk,:),pi(pk,:));
179 |
180 | ip = pk(il(:));
181 | it = tk(jl(:));
182 |
183 | end
184 |
185 | end
186 |
187 | function [in] = intria2(pp,tt,pi,rt)
188 | %INTRIA2 returns TRUE for points enclosed by 2-simplexes.
189 |
190 | t1 = tt(:,1); t2 = tt(:,2) ;
191 | t3 = tt(:,3);
192 |
193 | vi = pp(t1,:) - pi ;
194 | vj = pp(t2,:) - pi ;
195 | vk = pp(t3,:) - pi ;
196 |
197 | %------------------------------- compute sub-volume about PI
198 | aa = zeros(size(tt,1),3) ;
199 | aa(:,1) =(vi(:,1).*vj(:,2) - ...
200 | vj(:,1).*vi(:,2) ) ;
201 | aa(:,2) =(vj(:,1).*vk(:,2) - ...
202 | vk(:,1).*vj(:,2) ) ;
203 | aa(:,3) =(vk(:,1).*vi(:,2) - ...
204 | vi(:,1).*vk(:,2) ) ;
205 |
206 | %------------------------------- PI is internal if same sign
207 | rt = rt ^ 2 ;
208 | in = aa(:,1).*aa(:,2) >= -rt ...
209 | & aa(:,2).*aa(:,3) >= -rt ...
210 | & aa(:,3).*aa(:,1) >= -rt ;
211 |
212 | end
213 |
214 | function [in] = intria3(pp,tt,pi,rt)
215 | %INTRIA3 returns TRUE for points enclosed by 3-simplexes.
216 |
217 | t1 = tt(:,1); t2 = tt(:,2) ;
218 | t3 = tt(:,3); t4 = tt(:,4) ;
219 |
220 | v1 = pi - pp(t1,:) ;
221 | v2 = pi - pp(t2,:) ;
222 | v3 = pi - pp(t3,:) ;
223 | v4 = pi - pp(t4,:) ;
224 |
225 | %------------------------------- compute sub-volume about PI
226 | aa = zeros(size(tt,1),4) ;
227 | aa(:,1) = ...
228 | +v1(:,1).*(v2(:,2).*v3(:,3) - ...
229 | v2(:,3).*v3(:,2) ) ...
230 | -v1(:,2).*(v2(:,1).*v3(:,3) - ...
231 | v2(:,3).*v3(:,1) ) ...
232 | +v1(:,3).*(v2(:,1).*v3(:,2) - ...
233 | v2(:,2).*v3(:,1) ) ;
234 | aa(:,2) = ...
235 | +v1(:,1).*(v4(:,2).*v2(:,3) - ...
236 | v4(:,3).*v2(:,2) ) ...
237 | -v1(:,2).*(v4(:,1).*v2(:,3) - ...
238 | v4(:,3).*v2(:,1) ) ...
239 | +v1(:,3).*(v4(:,1).*v2(:,2) - ...
240 | v4(:,2).*v2(:,1) ) ;
241 | aa(:,3) = ...
242 | +v2(:,1).*(v4(:,2).*v3(:,3) - ...
243 | v4(:,3).*v3(:,2) ) ...
244 | -v2(:,2).*(v4(:,1).*v3(:,3) - ...
245 | v4(:,3).*v3(:,1) ) ...
246 | +v2(:,3).*(v4(:,1).*v3(:,2) - ...
247 | v4(:,2).*v3(:,1) ) ;
248 | aa(:,4) = ...
249 | +v3(:,1).*(v4(:,2).*v1(:,3) - ...
250 | v4(:,3).*v1(:,2) ) ...
251 | -v3(:,2).*(v4(:,1).*v1(:,3) - ...
252 | v4(:,3).*v1(:,1) ) ...
253 | +v3(:,3).*(v4(:,1).*v1(:,2) - ...
254 | v4(:,2).*v1(:,1) ) ;
255 |
256 | %------------------------------- PI is internal if same sign
257 | rt = rt ^ 2 ;
258 | in = aa(:,1).*aa(:,2) >= -rt ...
259 | & aa(:,1).*aa(:,3) >= -rt ...
260 | & aa(:,1).*aa(:,4) >= -rt ...
261 | & aa(:,2).*aa(:,3) >= -rt ...
262 | & aa(:,2).*aa(:,4) >= -rt ...
263 | & aa(:,3).*aa(:,4) >= -rt ;
264 |
265 | end
266 |
267 | function [ii,jj] = intrian(pp,tt,pi)
268 | %INTRIAN return a list of points and enclosing "n"-simplexes.
269 |
270 | [np,pd] = size(pi);
271 | [nt,td] = size(tt);
272 |
273 | %---------------- coefficient matrices for barycentric coord.
274 | mm = zeros(pd,pd,nt);
275 | for id = +1 : pd
276 | for jd = +1 : pd
277 | mm(id,jd,:) = pp(tt(:,jd),id) - ...
278 | pp(tt(:,td),id) ;
279 | end
280 | end
281 |
282 | %---------------- solve linear systems for barycentric coord.
283 | xx = zeros(pd,np,nt);
284 | vp = zeros(pd,np,+1);
285 | for ti = +1 : nt
286 | %---------------------------------------- form rhs coeff.
287 | for id = +1 : pd
288 | vp(id,:) = ...
289 | pi(:,id) - pp(tt(ti,td),id) ;
290 | end
291 | %---------------------------- actually faster to call LU-
292 | [ll,uu] = lu(mm(:,:,ti));
293 | %---------------------------- and then forward/back solve
294 | xx(:,:,ti) = uu\(ll\vp);
295 | end
296 |
297 | %-------------------- PI is internal if coord. have same sign
298 | in = all(xx >= +.0-(eps^.8),1) & ...
299 | sum(xx,1) <= +1.+(eps^.8) ;
300 |
301 | %-------------------- find lists of matching points/simplexes
302 | [ii,jj] = ...
303 | find(reshape(in, [np,nt])) ;
304 |
305 | end
306 |
307 |
308 |
309 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/lineline.m:
--------------------------------------------------------------------------------
1 | function [lp,lj,tr] = lineline(pa,pb,pc,pd,varargin)
2 | %LINELINE intersection between lines in d-dimensional space.
3 | % [LP,LI] = LINELINE(PA,PB,PC,PD) finds intersections bet-
4 | % ween line segments in d-dimensions. Lines are specified
5 | % as a set of endpoints [PA,PB] and [PC,PD] where PA, PB,
6 | % PC and PD are NL-by-ND arrays of coordinates, where ND
7 | % is the number of dimensions.
8 | %
9 | % A set of intersecting lines from [PA,PB] is returned for
10 | % each query line in [PC,PB], such that the II-th query
11 | % line is associated with the lines LI(LP(II,1):LP(II,2)).
12 | % Lines without intersections have LP(II,1) == 0.
13 | %
14 | % [LP,LI,TR] = LINELINE(PA,PB,PC,PD) additionally returns
15 | % the supporting aabb-tree used internally to compute the
16 | % query. If the underlying collection [PA,PB] is static,
17 | % the tree TR may be recycled for subsequent calls, using
18 | % [LP,LI,TR] = FINDLINE(PA,PB,PC,PD,TR). This syntax may
19 | % lead to improved performance, especially when the number
20 | % of lines is large w.r.t. the number of query lines. Note
21 | % that in such cases the distribution of underlying lines
22 | % is NOT permitted to change between calls, or erroneous
23 | % results may be returned. Additional parameters used to
24 | % govern the creation of the underlying aabb-tree may be
25 | % passed via [...] = LINELINE(...,TR,OP). See MAKETREE for
26 | % additional information.
27 | %
28 | % See also MAKETREE, FINDTRIA, FINDBALL, FINDLINE
29 |
30 | % Please see the following for additional information:
31 | %
32 | % Darren Engwirda, "Locally-optimal Delaunay-refinement &
33 | % optimisation-based mesh generation". Ph.D. Thesis, Scho-
34 | % ol of Mathematics and Statistics, Univ. of Sydney, 2014:
35 | % http://hdl.handle.net/2123/13148
36 |
37 | %-----------------------------------------------------------
38 | % Darren Engwirda : 2017 --
39 | % Email : de2363@columbia.edu
40 | % Last updated : 10/10/2017
41 | %-----------------------------------------------------------
42 |
43 | lp = []; lj = []; tr = []; op = [];
44 |
45 | %---------------------------------------------- basic checks
46 | if (nargin < +4 || nargin > +6)
47 | error('lineline:incorrectNumInputs', ...
48 | 'Incorrect number of inputs.');
49 | end
50 |
51 | if (nargin >= +5), tr = varargin{1}; end
52 | if (nargin >= +6), op = varargin{2}; end
53 |
54 | %------------------------------ quick return on empty inputs
55 | if (isempty(pa)), return ; end
56 | if (isempty(pb)), return ; end
57 | if (isempty(pc)), return ; end
58 | if (isempty(pd)), return ; end
59 |
60 | %---------------------------------------------- basic checks
61 | if (~isnumeric(pa) || ~isnumeric(pb) || ...
62 | ~isnumeric(pc) || ~isnumeric(pd) )
63 | error('lineline:incorrectInputClass', ...
64 | 'Incorrect input class.') ;
65 | end
66 |
67 | if (ndims(pa) ~= +2 || size(pa,2) < +2 || ...
68 | ndims(pb) ~= +2 || size(pb,2) < +2 || ...
69 | ndims(pc) ~= +2 || size(pc,2) < +2 || ...
70 | ndims(pd) ~= +2 || size(pd,2) < +2 )
71 | error('lineline:incorrectDimensions', ...
72 | 'Incorrect input dimensions.');
73 | end
74 | if (size(pa,1) ~= size(pb,1) || ...
75 | size(pc,1) ~= size(pd,1) )
76 | error('lineline:incorrectDimensions', ...
77 | 'Incorrect input dimensions.');
78 | end
79 |
80 | if (~isempty(tr) && ~isstruct(tr) )
81 | error('lineline:incorrectInputClass', ...
82 | 'Incorrect input class.') ;
83 | end
84 | if (~isempty(op) && ~isstruct(op) )
85 | error('lineline:incorrectInputClass', ...
86 | 'Incorrect input class.') ;
87 | end
88 |
89 | nd = size(pa,2);
90 | nl = size(pa,1);
91 | ml = size(pc,1);
92 |
93 | if (isempty(tr))
94 | %------------------------------ compute aabb-tree for d-line
95 | ab = zeros(nl,nd*+2) ;
96 | for ax = +1:nd % compute aabb's
97 | ab(:,ax+nd*0) = min(pa(:,ax), ...
98 | pb(:,ax)) ;
99 | ab(:,ax+nd*1) = max(pa(:,ax), ...
100 | pb(:,ax)) ;
101 | end
102 | tr = maketree(ab,op) ;
103 |
104 | end
105 |
106 | %------------------------------ compute tree-to-vert mapping
107 | ab = zeros(ml,nd*+2) ;
108 | for ax = +1:nd % compute aabb's
109 | ab(:,ax+nd*0) = min(pc(:,ax), ...
110 | pd(:,ax)) ;
111 | ab(:,ax+nd*1) = max(pc(:,ax), ...
112 | pd(:,ax)) ;
113 | end
114 | tm = maprect (tr,ab) ;
115 |
116 | %------------------------------ compute line-to-line queries
117 | [li,ip,lj] = queryset( ...
118 | tr,tm,@linekern,pc,pd,pa,pb) ;
119 |
120 | %------------------------------ re-index onto full obj. list
121 | lp = zeros(size(pc,1),2) ;
122 | lp( :,2) = -1 ;
123 |
124 | if (isempty(li)), return ; end
125 |
126 | lp(li,:) = ip ;
127 |
128 | end
129 |
130 | function [i1,i2] = linekern(l1,l2,pa,pb,pc,pd)
131 | %LINEKERN d-dim. line//line intersection kernel routine.
132 |
133 | m1 = length(l1) ;
134 | m2 = length(l2) ;
135 |
136 | %-------------------------- push line/vert onto n*m tile
137 | l1 = l1.' ;
138 |
139 | l1 = l1(ones(m2,1),:) ;
140 | l1 = l1(:);
141 | l2 = l2(:,ones(1,m1)) ;
142 | l2 = l2(:);
143 |
144 | %-------------------------- compute O(n*m) intersections
145 | [ok,tp,tq] = linenear( ...
146 | pa(l1,:),pb(l1,:), ...
147 | pc(l2,:),pd(l2,:)) ;
148 |
149 | rt = +1.+eps;
150 |
151 | ix = abs(tp) <= +rt & ...
152 | abs(tq) <= +rt ;
153 |
154 | ix(~ok) = false ;
155 |
156 | i1 = l1(ix) ;
157 | i2 = l2(ix) ;
158 |
159 | end
160 |
161 |
162 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/linenear.m:
--------------------------------------------------------------------------------
1 | function [ok,tp,tq] = linenear(pa,pb,pc,pd)
2 | %LINENEAR calc. the nearest points on line segments embedded
3 | %in d-dimensions. Line paramters are bounded on [-1,+1].
4 |
5 | %-----------------------------------------------------------
6 | % Darren Engwirda : 2017 --
7 | % Email : de2363@columbia.edu
8 | % Last updated : 10/10/2017
9 | %-----------------------------------------------------------
10 |
11 | m1 = (pa+pb) * +.5 ;
12 | D1 = (pb-pa) * +.5 ;
13 |
14 | m2 = (pc+pd) * +.5 ;
15 | D2 = (pd-pc) * +.5 ;
16 |
17 | r1 = sum(m2.*D1,2) ...
18 | - sum(m1.*D1,2) ;
19 | r2 = sum(m1.*D2,2) ...
20 | - sum(m2.*D2,2) ;
21 |
22 | A1 = sum(D1.*D1,2) ;
23 | A2 =-sum(D1.*D2,2) ;
24 | A3 =-sum(D1.*D2,2) ;
25 | A4 = sum(D2.*D2,2) ;
26 |
27 | dd = A1.*A4 - A2.*A3 ;
28 |
29 | tp = A4.*r1 - A2.*r2 ;
30 | tq =-A3.*r1 + A1.*r2 ;
31 |
32 | rt = max(abs([A1,A2,A3,A4]),[],2);
33 | rt = rt * eps ^ .8 ;
34 |
35 | ok = abs(dd) > +rt ;
36 |
37 | tp(~ok) = +0. ;
38 | tq(~ok) = +0. ;
39 |
40 | tp(ok) = tp(ok) ./ dd(ok) ;
41 | tq(ok) = tq(ok) ./ dd(ok) ;
42 |
43 | end
44 |
45 |
46 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/maketree.m:
--------------------------------------------------------------------------------
1 | function [tr] = maketree(rp,varargin)
2 | %MAKETREE assemble an AABB search tree for a collection of
3 | %(hyper-)rectangles.
4 | % [TR] = MAKETREE(RP) returns an axis-aligned bounding-box
5 | % (AABB) tree for a collection of d-rectangles embedded in
6 | % R^d. The rectangles are defined as an NR-by-NDIM*2 array
7 | % of min/max coordinates RP = [PMIN,PMAX], where PMIN =
8 | % [A1,A2,...,ANDIM] and PMAX = [B1,B2,...,BNDIM] are the
9 | % minimum/maximum coordinates associated with each rectan-
10 | % gle.
11 | %
12 | % The resulting tree is returned as a structure containing
13 | % an NT-by-NDIM*2 array of tree-node coordinates TR.XX =
14 | % [PMIN,PMAX], an NT-by-2 array of tree-node indexing
15 | % TR.II = [PI,CI], and an NT-by-1 cell array of node lists
16 | % TR.LL. PI,CI are the parent/child pointers associated
17 | % with each node in the tree, and TR.LL{II} is the list of
18 | % rectangles associated with the II-th node.
19 | %
20 | % MAKETREE forms a binary search tree, such that each
21 | % "leaf" node in the tree encloses a maximum number of re-
22 | % ctangles. The tree is formed by recursively subdividing
23 | % the bounding-box of the collection. At each division, a
24 | % simple heuristic is used to determine a splitting axis
25 | % and to position an axis-aligned splitting (hyper-)plane.
26 | % The associated collection of rectangles is partitioned
27 | % between two new child nodes. The dimensions of each node
28 | % in the tree are selected to provide a minimal enclosure
29 | % of the rectangles in its associated sub-tree. Tree nodes
30 | % may overlap as a result.
31 | %
32 | % [...] = MAKETREE(RP,OP) also passes an additional user-
33 | % defined options structure. OP.NOBJ = {32} is the maximum
34 | % allowable number of rectangles per tree-node. OP.LONG =
35 | % {.75} is a relative length tolerance for "long" rectang-
36 | % les, such that any rectangles with RMAX(IX)-RMIN(IX) >=
37 | % OP.LONG * (NMAX(IX)-NMIN(IX)) remain in the parent node.
38 | % Here RMIN,RMAX are the coordinates of the rectangle,
39 | % NMIN,NMAX are the coordinates of the enclosing node in
40 | % the tree, and IX is the splitting axis. Nodes that beco-
41 | % me "full" of "long" items may exceed their maximum rect-
42 | % angle capacity. OP.VTOL = {.55} is a "volume" splitting
43 | % criteria, designed to continue subdivision while the net
44 | % node volume is reducing. Specifically, a node is split
45 | % if V1+V2 <= OP.VTOL*VP, where VP is the d-dim. "volume"
46 | % of the parent node, and V1,V2 are the volumes associated
47 | % with its children.
48 | %
49 | % See also DRAWTREE, QUERYSET, MAPVERT, MAPRECT
50 |
51 | % Please see the following for additional information:
52 | %
53 | % Darren Engwirda, "Locally-optimal Delaunay-refinement &
54 | % optimisation-based mesh generation". Ph.D. Thesis, Scho-
55 | % ol of Mathematics and Statistics, Univ. of Sydney, 2014:
56 | % http://hdl.handle.net/2123/13148
57 |
58 | % Darren Engwirda : 2014 --
59 | % Email : de2363@columbia.edu
60 | % Last updated : 08/10/2017
61 |
62 | tr.xx = []; tr.ii = []; tr.ll = {}; op = [];
63 |
64 | %------------------------------ quick return on empty inputs
65 | if (isempty(rp)), return; end
66 |
67 | %---------------------------------------------- basic checks
68 | if (~isnumeric(rp))
69 | error('maketree:incorrectInputClass', ...
70 | 'Incorrect input class.');
71 | end
72 |
73 | %---------------------------------------------- basic checks
74 | if (nargin < +1 || nargin > +2)
75 | error('maketree:incorrectNoOfInputs', ...
76 | 'Incorrect number of inputs.');
77 | end
78 | if (ndims(rp) ~= +2 || ...
79 | mod(size(rp,2),2)~= +0)
80 | error('maketree:incorrectDimensions', ...
81 | 'Incorrect input dimensions.');
82 | end
83 |
84 | %------------------------------- extract user-defined inputs
85 | if (nargin >= +2), op = varargin{1}; end
86 |
87 | isoctave = exist( ...
88 | 'OCTAVE_VERSION','builtin') > 0;
89 |
90 | if (isoctave)
91 |
92 | %-- faster for OCTAVE with large tree block size; slower
93 | %-- loop execution...
94 |
95 | NOBJ = +1024 ;
96 |
97 | else
98 |
99 | %-- faster for MATLAB with small tree block size; better
100 | %-- loop execution...
101 |
102 | NOBJ = + 32 ;
103 |
104 | end
105 |
106 | %--------------------------------------- user-defined inputs
107 | if (~isstruct(op))
108 |
109 | op.nobj = NOBJ ;
110 | op.long = .75;
111 | op.vtol = .55;
112 |
113 | else
114 |
115 | %-------------------------------- bound population count
116 | if (isfield(op,'nobj'))
117 | if (op.nobj <= +0 )
118 | error('Invalid options.') ;
119 | end
120 | else
121 | op.nobj = NOBJ ;
122 | end
123 |
124 | %-------------------------------- bound "long" tolerance
125 | if (isfield(op,'long'))
126 | if (op.long < +.0 || op.long > +1.)
127 | error('Invalid options.') ;
128 | end
129 | else
130 | op.long = .75;
131 | end
132 |
133 | %-------------------------------- bound "long" tolerance
134 | if (isfield(op,'vtol'))
135 | if (op.vtol < +.0 || op.vtol > +1.)
136 | error('Invalid options.') ;
137 | end
138 | else
139 | op.vtol = .55;
140 | end
141 |
142 | end
143 |
144 | %---------------------------------- dimensions of rectangles
145 | nd = size(rp,2) / +2 ;
146 | ni = size(rp,1) ;
147 |
148 | %------------------------------------------ alloc. workspace
149 | xl = zeros(ni*1,1*nd);
150 | xr = zeros(ni*1,1*nd);
151 | ii = zeros(ni*1,2);
152 | ll = cell (ni*1,1);
153 | ss = zeros(ni*1,1);
154 |
155 | %------------------------------------ min & max coord. masks
156 | lv = false(size(rp,2),1);
157 | rv = false(size(rp,2),1);
158 | lv((1:nd)+nd*+0) = true ;
159 | rv((1:nd)+nd*+1) = true ;
160 |
161 | %----------------------------------------- inflate rectangle
162 | r0 = min(rp(:,lv),[],1) ;
163 | r1 = max(rp(:,rv),[],1) ;
164 |
165 | rd = repmat(r1-r0,ni,1) ;
166 |
167 | rp(:,lv) = ...
168 | rp(:,lv) - rd * eps^.8;
169 | rp(:,rv) = ...
170 | rp(:,rv) + rd * eps^.8;
171 |
172 | %----------------------------------------- rectangle centres
173 | rc = rp(:,lv)+rp(:,rv);
174 | rc = rc * .5 ;
175 | %----------------------------------------- rectangle lengths
176 | rd = rp(:,rv)-rp(:,lv);
177 |
178 | %------------------------------ root contains all rectangles
179 | ll{1} = (+1:ni)' ;
180 | %------------------------------------ indexing for root node
181 | ii(1,1) = +0 ;
182 | ii(1,2) = +0 ;
183 | %------------------------------ root contains all rectangles
184 | xl(1,:) = min(rp(:,lv),[],1);
185 | xr(1,:) = max(rp(:,rv),[],1);
186 |
187 | %-- main loop : divide nodes until all constraints satisfied
188 | ss(+1) = +1; ns = +1; nn = +1;
189 | while (ns ~= +0)
190 | %----------------------------------- pop node from stack
191 | ni = ss(ns) ;
192 | ns = ns - 1 ;
193 | %----------------------------------- push child indexing
194 | n1 = nn + 1 ;
195 | n2 = nn + 2 ;
196 |
197 | %--------------------------- set of rectangles in parent
198 | li = ll{ni} ;
199 | %--------------------------- split plane on longest axis
200 | dd = xr(ni,:) ...
201 | - xl(ni,:) ;
202 | [dd,ia] = sort(dd);
203 |
204 | for id = nd : -1 : +1
205 | %--------------------------- push rectangles to children
206 | ax = ia (id) ;
207 | mx = dd (id) ;
208 |
209 | il = rd(li,ax) > ...
210 | op.long * mx ;
211 | lp = li( il) ; % "long" rectangles
212 | ls = li(~il) ; % "short" rectangles
213 |
214 | if (length(lp) < ...
215 | 0.5*length(ls)&& ...
216 | length(lp) < ...
217 | 0.5 * op.nobj)
218 | break ;
219 | end
220 | end
221 |
222 | if (isempty(ls) )
223 | %-------------------------------- partition empty, done!
224 | continue ;
225 | end
226 |
227 | % select the split position: take the mean of the set of
228 | % (non-"long") rectangle centres along axis AX
229 | %-------------------------------------------------------
230 | sp = sum(rc(ls,ax))/length(ls);
231 |
232 | %---------------------------- partition based on centres
233 | i2 = rc(ls,ax)>sp ;
234 | l1 = ls(~i2) ; % "left" rectangles
235 | l2 = ls( i2) ; % "right" rectangles
236 |
237 | if (isempty(l1) || ...
238 | isempty(l2) )
239 | %-------------------------------- partition empty, done!
240 | continue ;
241 | end
242 |
243 | %-------------------------------- finalise node position
244 | xl(n1,:) = ...
245 | min(rp(l1,lv),[],1) ;
246 | xr(n1,:) = ...
247 | max(rp(l1,rv),[],1) ;
248 | xl(n2,:) = ...
249 | min(rp(l2,lv),[],1) ;
250 | xr(n2,:) = ...
251 | max(rp(l2,rv),[],1) ;
252 |
253 | %--------------------------- push child nodes onto stack
254 | if (length(li) <= op.nobj )
255 |
256 | vi = prod(xr(ni,:) ... % upper d-dim "vol."
257 | - xl(ni,:) ) ;
258 | v1 = prod(xr(n1,:) ... % lower d-dim "vol."
259 | - xl(n1,:) ) ;
260 | v2 = prod(xr(n2,:) ...
261 | - xl(n2,:) ) ;
262 |
263 | if (v1+v2 < op.vtol*vi)
264 |
265 | %-------------------------------- parent--child indexing
266 | ii(n1,1) = ni ;
267 | ii(n2,1) = ni ;
268 | ii(ni,2) = n1 ;
269 |
270 | %-------------------------------- finalise list indexing
271 | ll{ni,1} = lp ;
272 | ll{n1,1} = l1 ;
273 | ll{n2,1} = l2 ;
274 |
275 | ss(ns+1) = n1 ;
276 | ss(ns+2) = n2 ;
277 | ns = ns+2; nn = nn+2;
278 |
279 | end
280 |
281 | else
282 | %-------------------------------- parent--child indexing
283 | ii(n1,1) = ni ;
284 | ii(n2,1) = ni ;
285 | ii(ni,2) = n1 ;
286 |
287 | %-------------------------------- finalise list indexing
288 | ll{ni,1} = lp ;
289 | ll{n1,1} = l1 ;
290 | ll{n2,1} = l2 ;
291 |
292 | ss(ns+1) = n1 ;
293 | ss(ns+2) = n2 ;
294 | ns = ns+2; nn = nn+2;
295 |
296 | end
297 |
298 | end
299 | %----------------------------------------------- trim alloc.
300 | xl = xl(1:nn,:) ;
301 | xr = xr(1:nn,:) ;
302 | ii = ii(1:nn,:) ;
303 | ll = ll(1:nn,:) ;
304 |
305 | %----------------------------------------------- pack struct
306 | tr.xx =[xl, xr] ;
307 | tr.ii = ii ;
308 | tr.ll = ll ;
309 |
310 | end
311 |
312 |
313 |
314 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/maprect.m:
--------------------------------------------------------------------------------
1 | function [tm,im] = maprect(tr,pr)
2 | %MAPRECT find the tree-to-rectangle mappings.
3 | % [TM,IM] = MAPRECT(TR,PR) returns the tree-to-rectangle
4 | % and rectangle-to-tree mappings for a given aabb-tree TR
5 | % and a collection of query vertices PI.
6 | %
7 | % The tree-to-item mapping TM is a structure representing
8 | % the intersection of the items PI with the tree TR. TM.II
9 | % is an M-by-1 array of tree indices and TM.LL is an
10 | % M-by-1 cell array of item lists. Specifically, items in
11 | % the list TM.LL{JJ} intersect with the node TM.II(JJ).
12 | %
13 | % The item-to-tree mapping IM is a structure representing
14 | % the inverse mapping. IM.II is an N-by-1 array of item
15 | % indices and IM.LL is an N-by-1 cell array of node lists.
16 | % Specifically, nodes in the list IM.LL{JJ} intersect with
17 | % the item IM.II(JJ).
18 | %
19 | % See also QUERYSET, MAPVERT, MAKETREE
20 |
21 | % Darren Engwirda : 2017 --
22 | % Email : de2363@columbia.edu
23 | % Last updated : 09/04/2017
24 |
25 | %----------------------- call SCANTREE to do the actual work
26 | if (nargout == +1)
27 | [tm ] = scantree(tr,pr,@partrect);
28 | else
29 | [tm,im] = scantree(tr,pr,@partrect);
30 | end
31 |
32 | end
33 |
34 | function [j1,j2] = partrect(pr,b1,b2)
35 | %PARTRECT partition points between boxes B1,B2 for SCANTREE.
36 |
37 | j1 = true(size(pr,1),1) ;
38 | j2 = true(size(pr,1),1) ;
39 |
40 | nd = size(b1,2) / +2;
41 |
42 | for ax = +1 : nd
43 | %--------------- remains TRUE if inside bounds along axis AX
44 | j1 = j1 & pr(:,ax+nd*1) ...
45 | >= b1( ax+nd*0) ...
46 | & pr(:,ax+nd*0) ...
47 | <= b1( ax+nd*1) ;
48 |
49 | %--------------- remains TRUE if inside bounds along axis AX
50 | j2 = j2 & pr(:,ax+nd*1) ...
51 | >= b2( ax+nd*0) ...
52 | & pr(:,ax+nd*0) ...
53 | <= b2( ax+nd*1) ;
54 | end
55 |
56 | end
57 |
58 |
59 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/mapvert.m:
--------------------------------------------------------------------------------
1 | function [tm,im] = mapvert(tr,pi)
2 | %MAPVERT find the tree-to-vertex mappings.
3 | % [TM,IM] = MAPVERT(TR,PI) returns the tree-to-vertex and
4 | % vertex-to-tree mappings for a given aabb-tree TR and a
5 | % collection of query vertices PI.
6 | %
7 | % The tree-to-item mapping TM is a structure representing
8 | % the intersection of the items PI with the tree TR. TM.II
9 | % is an M-by-1 array of tree indices and TM.LL is an
10 | % M-by-1 cell array of item lists. Specifically, items in
11 | % the list TM.LL{JJ} intersect with the node TM.II(JJ).
12 | %
13 | % The item-to-tree mapping IM is a structure representing
14 | % the inverse mapping. IM.II is an N-by-1 array of item
15 | % indices and IM.LL is an N-by-1 cell array of node lists.
16 | % Specifically, nodes in the list IM.LL{JJ} intersect with
17 | % the item IM.II(JJ).
18 | %
19 | % See also QUERYSET, MAPRECT, MAKETREE
20 |
21 | % Darren Engwirda : 2014 --
22 | % Email : de2363@columbia.edu
23 | % Last updated : 06/04/2017
24 |
25 | %----------------------- call SCANTREE to do the actual work
26 | if (nargout == +1)
27 | [tm ] = scantree(tr,pi,@partvert);
28 | else
29 | [tm,im] = scantree(tr,pi,@partvert);
30 | end
31 |
32 | end
33 |
34 | function [j1,j2] = partvert(pi,b1,b2)
35 | %PARTVERT partition points between boxes B1,B2 for SCANTREE.
36 |
37 | j1 = true(size(pi,1),1);
38 | j2 = true(size(pi,1),1);
39 |
40 | nd = size(b1,2) / +2;
41 |
42 | for ax = +1 : nd
43 | %--------------- remains TRUE if inside bounds along axis AX
44 | j1 = j1 & pi(:,ax)>=b1(ax+nd*0) ...
45 | & pi(:,ax)<=b1(ax+nd*1) ;
46 |
47 | %--------------- remains TRUE if inside bounds along axis AX
48 | j2 = j2 & pi(:,ax)>=b2(ax+nd*0) ...
49 | & pi(:,ax)<=b2(ax+nd*1) ;
50 | end
51 |
52 | end
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/queryset.m:
--------------------------------------------------------------------------------
1 | function [qi,qp,qj] = queryset(tr,tm,fn,varargin)
2 | %QUERYSET spatial queries for AABB-indexed collections.
3 | % [QI,QP,QJ] = QUERYSET(TR,TM,FN) computes a spatial query
4 | % on an indexed collection. TR is the AABB-tree built to
5 | % index the collection, TM is the query-to-tree mapping
6 | % structure, and FN is the intersection "kernel" function,
7 | % called to compute actual intersections.
8 | %
9 | % A set of intersecting objects is returned for each item,
10 | % such that for each query item QI(II), a list of interse-
11 | % cting objects QJ(QP(II,1):QP(II,2)) is returned.
12 | %
13 | % [PK,CK] = FN(PJ,CJ,A1,...,AN) is an intersection kernel
14 | % function called for each non-empty node in the tree TR.
15 | % PJ,CJ are Nx1 and Mx1 arrays of query- and obj.-indices
16 | % to compare against each other. These lists represent a
17 | % "localised" O(N*M) "tile" of pairwise comparisons. PK,CK
18 | % are lists of matching objects. A1,...,AN are a set of
19 | % optional user-defined arguments that are passed to the
20 | % kernel function FN internally. Optional arguments can be
21 | % passed to FN via calls to QUERYSET(..., A1,...AN).
22 | %
23 | % See also MAPVERT, MAPRECT, MAKETREE
24 |
25 | % Darren Engwirda : 2017 --
26 | % Email : de2363@columbia.edu
27 | % Last updated : 07/10/2017
28 |
29 | qi = []; qp = []; qj = [];
30 |
31 | %---------------------------------------------- basic checks
32 | if (nargin <= +2)
33 | error('queryset:incorrectNumInputs', ...
34 | 'Incorrect number of inputs.');
35 | end
36 |
37 | %---------------------------------------------- empty inputs
38 | if (isempty(tr)), return; end
39 |
40 | %---------------------------------------------- basic checks
41 | if (~isempty(tr) && ~isstruct(tr) )
42 | error('queryset:incorrectInputClass', ...
43 | 'Incorrect input class.') ;
44 | end
45 | if (~isempty(tm) && ~isstruct(tm) )
46 | error('queryset:incorrectInputClass', ...
47 | 'Incorrect input class.') ;
48 | end
49 |
50 | %---------------------------------- check existing aabb-tree
51 | if (~isfield(tm,'ii') || ...
52 | ~isfield(tm,'ll') )
53 | error('queryset:incorrectAABBstruct', ...
54 | 'Invalid aabb-maps obj.') ;
55 | end
56 | %---------------------------------- check existing aabb-tree
57 | if (~isfield(tr,'xx') || ...
58 | ~isfield(tr,'ii') || ...
59 | ~isfield(tr,'ll') )
60 | error('queryset:incorrectAABBstruct', ...
61 | 'Invalid aabb-tree obj.') ;
62 | end
63 |
64 | %------------------------------ spatial query over tree-node
65 | ic = cell(size(tm.ii,1),1) ;
66 | jc = cell(size(tm.ii,1),1) ;
67 |
68 | for ip = 1 : size(tm.ii,1)
69 | %-------------------------- extract items/query per tile
70 | ni = tm.ii(ip,1) ; % node (in tree)
71 |
72 | %-------------------------- do O(n*m) search within tile
73 | [qi,qj] = feval(fn, ...
74 | tm.ll{ip,1}, ... % query in tile
75 | tr.ll{ni,1}, ... % items in tile
76 | varargin {:} ) ;
77 |
78 | %-------------------------- push loc. item-query matches
79 | ic{ip} = qi(:) ;
80 | jc{ip} = qj(:) ;
81 | end
82 |
83 | %-------------------------------- concat matches into arrays
84 | qi = vertcat(ic{:});
85 | qj = vertcat(jc{:});
86 |
87 | if (isempty(qj)),return; end
88 |
89 | %-------------------------------- form sparse-style indexing
90 | [qi,ix] = sort (qi) ;
91 | qj = qj(ix);
92 | ix = find(diff(qi));
93 |
94 | ni = length (qi) ;
95 |
96 | qi = qi([ix;ni]) ;
97 |
98 | nj = length (qj) ;
99 | ni = length (qi) ;
100 |
101 | %------------------------------ each list is IP(I,1):IP(I,2)
102 | qp = zeros(ni,2) ;
103 | qp(:,1) = [+1; ix+1] ;
104 | qp(:,2) = [ix; nj+0] ;
105 |
106 | end
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/mesh2d/aabb-tree/scantree.m:
--------------------------------------------------------------------------------
1 | function [tm,im] = scantree(tr,pi,fn)
2 | %SCANTREE find the tree-to-item mappings.
3 | % [TM,IM] = SCANTREE(TR,PI,FN) is a low-level routine that
4 | % returns the tree-to-item and item-to-tree mappings for a
5 | % given aabb-tree TR and a query collection PI. The funct-
6 | % ion [KI,KJ] = FN(PJ,NI,NJ) is called internally to part-
7 | % ition the sub-collection PJ between the aabb-tree nodes
8 | % NI,NJ, where:
9 | %
10 | % * KI(II) = TRUE if the II-th item intersects NI, and
11 | % * KJ(II) = TRUE if the II-th item intersects NJ.
12 | %
13 | % The tree-to-item mapping TM is a structure representing
14 | % the intersection of the items PI with the tree TR. TM.II
15 | % is an M-by-1 array of tree indices and TM.LL is an
16 | % M-by-1 cell array of item lists. Specifically, items in
17 | % the list TM.LL{JJ} intersect with the node TM.II(JJ).
18 | %
19 | % The item-to-tree mapping IM is a structure representing
20 | % the inverse mapping. IM.II is an N-by-1 array of item
21 | % indices and IM.LL is an N-by-1 cell array of node lists.
22 | % Specifically, nodes in the list IM.LL{JJ} intersect with
23 | % the item IM.II(JJ).
24 | %
25 | % See also QUERYSET, MAPVERT, MAPRECT, MAKETREE
26 |
27 | % Darren Engwirda : 2014 --
28 | % Email : de2363@columbia.edu
29 | % Last updated : 06/04/2017
30 |
31 | tm.ii = [] ; tm.ll = {} ;
32 | im.ii = [] ; im.ll = {} ;
33 |
34 | %------------------------------ quick return on empty inputs
35 | if (isempty(pi)), return; end
36 | if (isempty(tr)), return; end
37 |
38 | %---------------------------------------------- basic checks
39 | if (~isa(tr, 'struct') || ...
40 | ~isa(pi,'numeric') || ...
41 | ~isa(fn,'function_handle') )
42 | error('scantree:incorrectInputClass', ...
43 | 'Invalid input class.') ;
44 | end
45 | %---------------------------------------------- basic checks
46 | if ( ~isfield(tr,'xx') || ...
47 | ~isfield(tr,'ii') || ...
48 | ~isfield(tr,'ll') )
49 | error('scantree:incorrectAABBstruct', ...
50 | 'Incorrect aabb-tree.') ;
51 | end
52 |
53 | %----------------------------------- alloc. output/workspace
54 | tm.ii = zeros(size(tr.ii,1),1);
55 | tm.ll = cell (size(tr.ii,1),1);
56 |
57 | ss = zeros(size(tr.ii,1),1);
58 | sl = cell (size(tr.ii,1),1);
59 | sl{1} = (1:size(pi,1))';
60 |
61 | tf = ~cellfun('isempty',tr.ll);
62 |
63 | %---------- descend tree from root, push items amongst nodes
64 |
65 | ss(1) = +1; ns = +1; no = +1;
66 | while (ns ~= +0)
67 | %---------------------------------- _pop node from stack
68 | ni = ss(ns); ns = ns - 1;
69 |
70 | if (tf(ni))
71 | %-- push onto tree-item mapping -- non-empty node NI
72 | %-- contains items LL
73 | tm.ii(no) = ni ;
74 | tm.ll{no} ...
75 | = sl{ns+1} ;
76 | no = no + 1 ;
77 | end
78 |
79 | if (tr.ii(ni,+2)~=+0)
80 | %--------------------- partition amongst child nodes
81 | c1 = ...
82 | tr.ii(ni,2) + 0 ;
83 | c2 = ...
84 | tr.ii(ni,2) + 1 ;
85 |
86 | %--------------------- user-defined partitions of LL
87 | [j1,j2] = feval( ...
88 | fn,pi(sl{ns+1},:), ...
89 | tr.xx(c1,:),tr.xx(c2,:)) ;
90 |
91 | %--------------------- lists of items per child node
92 | l1 = sl{ns+1}(j1) ;
93 | l2 = sl{ns+1}(j2) ;
94 |
95 | if (~isempty(l1))
96 | %--------------------- push nonempty node onto stack
97 | ns = ns + 1 ;
98 | ss(ns) = c1 ;
99 | sl{ns} = l1 ;
100 | end
101 | if (~isempty(l2))
102 | %--------------------- push nonempty node onto stack
103 | ns = ns + 1 ;
104 | ss(ns) = c2 ;
105 | sl{ns} = l2 ;
106 | end
107 |
108 | end
109 |
110 | end
111 | %----------------------------------------------- trim alloc.
112 | tm.ii(no:end) = [];
113 | tm.ll(no:end) = [];
114 |
115 | %----------------------- compute inverse map only if desired
116 | if (nargout==+1), return; end
117 |
118 | %----------------------- accumulate pair'd tree-item matches
119 | ic = cell(no-1,+1); jc = tm.ll;
120 | for ip = +1 : no-1
121 | ni = tm.ii(ip);
122 | ic{ip} = ...
123 | ni * ones(length(jc{ip}),1);
124 | end
125 | ii = vertcat(ic{:});
126 | ni = size(ii,1) ;
127 | jj = vertcat(jc{:});
128 | nj = size(jj,1) ;
129 |
130 | if (isempty(jj)), return; end
131 |
132 | im.ll = cell (size(pi,1),1) ;
133 |
134 | %---------------------------------- invert ordering via sort
135 | [jj,jx] = sort(jj) ;
136 | ii = ii(jx);
137 | jx = find(diff(jj)~=+0);
138 | im.ii = [jj(jx);jj(nj)];
139 | jx = [+0;jx;ni];
140 |
141 | %----------------------- distribute single item-tree matches
142 | for ip = +1 : size(im.ii,1)
143 | im.ll{ip} = ...
144 | ii(jx(ip+0)+1:jx(ip+1)+0);
145 | end
146 |
147 | end
148 |
149 |
150 |
151 |
--------------------------------------------------------------------------------
/mesh2d/bfsgeo2.m:
--------------------------------------------------------------------------------
1 | function [node,PSLG,part] = bfsgeo2(node,PSLG,seed)
2 | %BFSGEO2 partition geometry about "seeds" via breadth-first
3 | %search.
4 | % [NODE,EDGE,PART] = BFSGEO2(NODE,EDGE,SEED) returns a set
5 | % of 2-manifold geometry partitions by expanding about a
6 | % list of "seed" points. Partitions expand in a breadth-
7 | % first sense until a polygon bounday is encountered. SEED
8 | % is an S-by-2 array of XY-coordinates to expand around,
9 | % NODE is an N-by-2 array of polygon vertices, and EDGE is
10 | % an E-by-2 array of polygon edge indexing. Each row in
11 | % EDGE represents an edge of the polygon, such that
12 | % NODE(EDGE(JJ,1),:) and NODE(EDGE(JJ,2),:) are the XY-co-
13 | % ordinates of the endpoints of the JJ-TH edge. PART is an
14 | % S-by-1 cell array of geometry partitions, where each
15 | % PART{KK} is a list of edge indices into EDGE that define
16 | % the KK-TH partition.
17 | %
18 | % This function may be useful when seeking to partition
19 | % complex, non-manifold geometry into a format that's app-
20 | % ropriate for the triangulation algorithms in REFINE2.
21 | %
22 | % See also REFINE2, FIXGEO2, BFSTRI2
23 |
24 | %-----------------------------------------------------------
25 | % Darren Engwirda : 2017 --
26 | % Email : de2363@columbia.edu
27 | % Last updated : 11/10/2017
28 | %-----------------------------------------------------------
29 |
30 | filename = mfilename('fullpath') ;
31 | filepath = fileparts( filename ) ;
32 |
33 | addpath([filepath,'/aabb-tree']) ;
34 |
35 | %---------------------------------------------- basic checks
36 | if ( ~isnumeric(node) || ...
37 | ~isnumeric(PSLG) || ...
38 | ~isnumeric(seed) )
39 | error('bfsgeo2:incorrectInputClass' , ...
40 | 'Incorrect input class.') ;
41 | end
42 |
43 | %---------------------------------------------- basic checks
44 | if (ndims(node) ~= +2 || ...
45 | ndims(PSLG) ~= +2 || ...
46 | ndims(seed) ~= +2 )
47 | error('bfsgeo2:incorrectDimensions' , ...
48 | 'Incorrect input dimensions.');
49 | end
50 |
51 | if (size(node,2)~= +2 || ...
52 | size(PSLG,2)~= +2 || ...
53 | size(seed,2)~= +2 )
54 | error('bfsgeo2:incorrectDimensions' , ...
55 | 'Incorrect input dimensions.');
56 | end
57 |
58 | nnod = size(node,1) ;
59 | nedg = size(PSLG,1) ;
60 |
61 | %---------------------------------------------- basic checks
62 | if (min([PSLG(:)])<+1 || ...
63 | max([PSLG(:)])>nnod)
64 | error('bfsgeo2:invalidInputs', ...
65 | 'Invalid EDGE input array.') ;
66 | end
67 |
68 | %------------------------------------------ assemble full CDT
69 | [node,PSLG,tria] = deltri2 (node,PSLG) ;
70 |
71 | %------------------------------------------ find seeds in CDT
72 | [sptr,stri] = findtria(node,tria,seed) ;
73 |
74 | okay = sptr(:,2) ...
75 | >= sptr(:,1) ;
76 | itri = stri(sptr(okay,1));
77 |
78 | %------------------------------------------ PART for all seed
79 | part = {} ;
80 |
81 | for ipos = +1 : size (itri,1)
82 |
83 | %-------------- BFS about current tria.
84 | [mark] = ...
85 | bfstri2(PSLG,tria,itri(ipos)) ;
86 |
87 | %-------------- match tria./poly. edges
88 | edge = [
89 | tria(mark,[1,2]) ;
90 | tria(mark,[2,3]) ;
91 | tria(mark,[3,1]) ;
92 | ] ;
93 |
94 | edge = sort(edge,+2) ;
95 | PSLG = sort(PSLG,+2) ;
96 |
97 | [same,epos] = ...
98 | setset2(edge(:,1:2),PSLG) ;
99 |
100 | %-------------- find match multiplicity
101 | epos = epos(epos>+0) ;
102 | epos = sort(epos);
103 |
104 | eidx = ...
105 | find(diff(epos)) ;
106 |
107 | eptr = ...
108 | [1;eidx+1;length(epos)+1] ;
109 | enum = ...
110 | eptr(2:end)-eptr(1:end-1) ;
111 |
112 | %---------- select singly-matched edges
113 | part{ipos} = ...
114 | epos(eptr(enum == 1)) ;
115 |
116 | end
117 |
118 | end
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/mesh2d/bfstri2.m:
--------------------------------------------------------------------------------
1 | function [seen] = bfstri2(PSLG,tria,seed)
2 | %BFSTRI2 expand about a single seed triangle via BFS. The se-
3 | %arch terminates when constraining edges are encountered.
4 | %SEEN(II) is TRUE if the II-TH triangle is found in the curr-
5 | %ent expansion.
6 | %
7 | % See also BFSGEO2, REFINE2, FIXGEO2
8 |
9 | %-----------------------------------------------------------
10 | % Darren Engwirda : 2017 --
11 | % Email : de2363@columbia.edu
12 | % Last updated : 01/10/2017
13 | %-----------------------------------------------------------
14 |
15 | seen = [];
16 |
17 | %---------------------------------------------- basic checks
18 | if ( ~isnumeric(tria) || ...
19 | ~isnumeric(seed) )
20 | error('bfstri2:incorrectInputClass' , ...
21 | 'Incorrect input class.') ;
22 | end
23 |
24 | %---------------------------------------------- basic checks
25 | if (ndims(tria) ~= +2 )
26 | error('bfstri2:incorrectDimensions' , ...
27 | 'Incorrect input dimensions.');
28 | end
29 | if (size(tria,2)~= +3 )
30 | error('bfstri2:incorrectDimensions' , ...
31 | 'Incorrect input dimensions.');
32 | end
33 |
34 | %---------------------------------------------- extra checks
35 | if ( ~isempty (PSLG) )
36 | if ( ~isnumeric(PSLG) )
37 | error('bfstri2:incorrectInputClass' , ...
38 | 'Incorrect input class.') ;
39 | end
40 |
41 | if (ndims(PSLG) ~= +2 )
42 | error('bfstri2:incorrectDimensions' , ...
43 | 'Incorrect input dimensions.');
44 | end
45 | if (size(PSLG,2)~= +2 )
46 | error('bfstri2:incorrectDimensions' , ...
47 | 'Incorrect input dimensions.');
48 | end
49 | end
50 |
51 | %----------------------------------------- form adj. indices
52 | ntri = size (tria,1);
53 |
54 | [edge,tria] = tricon2 (tria,PSLG);
55 |
56 | seed = seed(:) ;
57 |
58 | list = zeros(ntri,1);
59 | nlst = length (seed);
60 | list(1:nlst) = seed ;
61 |
62 | %----------------------------------------- do BFS iterations
63 | seen = false(ntri,1);
64 |
65 | while (nlst >= +1)
66 |
67 | %-------------- pop tria from stack top
68 | next = list(nlst);
69 | nlst = nlst-1 ;
70 | seen(next) = true;
71 |
72 | %-------------- visit 1-ring neighbours
73 | for eadj = +1 : +3
74 |
75 | epos = tria(next,eadj+3);
76 |
77 | %---------- find adjacent triangles
78 | if (edge(epos,5) == 0)
79 |
80 | if (next ~= edge(epos,3))
81 | tadj = edge(epos,3);
82 | else
83 | tadj = edge(epos,4);
84 | end
85 |
86 | if (tadj > +0 && ~seen(tadj))
87 |
88 | %---------- add unvisited neighbour
89 | seen(tadj) = true ;
90 | nlst = nlst+1 ;
91 | list(nlst) = tadj ;
92 |
93 | end
94 |
95 | end
96 |
97 | end
98 |
99 | end
100 |
101 | end
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/mesh2d/cdtbal1.m:
--------------------------------------------------------------------------------
1 | function [bb] = cdtbal1(pp,ee)
2 | %CDTBAL1 compute the circumballs associated with a 1-simplex
3 | %triangulation embedded in R^2.
4 | % [BB] = TRIBAL1(PP,EE) returns the circumscribing balls
5 | % associated with the 1-simplexes in [PP,TT], such that BB
6 | % = [XC,YC,RC.^2].
7 |
8 | % Darren Engwirda : 2017 --
9 | % Email : de2363@columbia.edu
10 | % Last updated : 24/03/2017
11 |
12 | %---------------------------------------------- basic checks
13 | if (~isnumeric(pp) || ...
14 | ~isnumeric(ee) )
15 | error('cdtbal1:incorrectInputClass' , ...
16 | 'Incorrect input class.') ;
17 | end
18 | %---------------------------------------------- basic checks
19 | if (ndims(pp) ~= +2 || ndims(ee) ~= +2)
20 | error('cdtbal1:incorrectDimensions' , ...
21 | 'Incorrect input dimensions.');
22 | end
23 | if (size(pp,2)~= +2 || size(ee,2) < +2)
24 | error('cdtbal1:incorrectDimensions' , ...
25 | 'Incorrect input dimensions.');
26 | end
27 |
28 | bb = zeros(size(ee,1),3);
29 |
30 | bb(:,1:2) = (pp(ee(:,1),:)+pp(ee(:,2),:))*.50 ;
31 | bb(:, 3) = ...
32 | sum((pp(ee(:,1),:)-pp(ee(:,2),:)).^2,2)*.25 ;
33 |
34 | end
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/mesh2d/cdtbal2.m:
--------------------------------------------------------------------------------
1 | function [cc] = cdtbal2(pp,ee,tt)
2 | %CDTBAL2 compute the modified circumballs associated with a
3 | %constrained 2-simplex Delaunay triangulation in R^2.
4 | % [CC] = CDTBAL2(PP,EE,TT) returns the smallest enclosing
5 | % balls associated with the triangles in [PP,TT], such th-
6 | % at CC = [XC,YC,RC.^2]. Such balls never lie outside the
7 | % boundaries of the associated CDT. See TRICON2 for info-
8 | % mation regarding the edge array EE.
9 |
10 | % Darren Engwirda : 2017 --
11 | % Email : de2363@columbia.edu
12 | % Last updated : 01/10/2017
13 |
14 | %---------------------------------------------- basic checks
15 | if (~isnumeric(pp) || ~isnumeric(ee) || ...
16 | ~isnumeric(tt) )
17 | error('cdtbal2:incorrectInputClass' , ...
18 | 'Incorrect input class.') ;
19 | end
20 |
21 | %---------------------------------------------- basic checks
22 | if (ndims(pp) ~= +2 || ndims(ee) ~= +2 || ...
23 | ndims(tt) ~= +2 )
24 | error('cdtbal2:incorrectDimensions' , ...
25 | 'Incorrect input dimensions.');
26 | end
27 | if (size(pp,2)~= +2 || size(ee,2) < +5 || ...
28 | size(tt,2) < +6 )
29 | error('cdtbal2:incorrectDimensions' , ...
30 | 'Incorrect input dimensions.');
31 | end
32 |
33 | %----------------------------------------- calc. circumballs
34 | cc = tribal2(pp,tt);
35 |
36 | %------------------------ replace with face-balls if smaller
37 | cc = minfac2(cc,pp,ee,tt,1,2,3) ;
38 | cc = minfac2(cc,pp,ee,tt,2,3,1) ;
39 | cc = minfac2(cc,pp,ee,tt,3,1,2) ;
40 |
41 | end
42 |
43 | function [cc] = minfac2(cc,pp,ee,tt,ni,nj,nk)
44 | %MINFAC2 modify the set of circumballs to constrain centres
45 | %to the boundaries of the CDT.
46 | % [CM] = MINFAC2(CC,PP,EE,TT,NI,NJ,NK) returns the set of
47 | % modified circmballs CM, where any ball CC lying outside
48 | % the boundaries of the CDT [PP,EE,TT] is replaced by the
49 | % edge-centred diametric ball. [NI,NJ] are the local inde-
50 | % xes associated with an edge to test. NK is the local in-
51 | % dex of the opposite vertex.
52 |
53 | %------------------------------------------------ outer edge
54 | EF = ee(tt(:,ni+3),5) > +0 ;
55 |
56 | %------------------------------------------------ edge balls
57 | bc = (pp(tt(EF,ni),:)+pp(tt(EF,nj),:))*.50;
58 |
59 | %------------------------------------------------ edge radii
60 | br = sum((bc(:,1:2)-pp(tt(EF,ni),:)).^2,2)...
61 | + sum((bc(:,1:2)-pp(tt(EF,nj),:)).^2,2);
62 | br = br * +0.5 ;
63 |
64 | %------------------------------------------- enclosing radii
65 | ll = sum((bc(:,1:2)-pp(tt(EF,nk),:)).^2,2);
66 |
67 | %------------------------------------------- replace if min.
68 | bi = br >= ll ...
69 | & br <= cc(EF,3) ;
70 | ei = find(EF) ;
71 | ti = ei (bi) ;
72 |
73 | %------------------------------------------- replace is min.
74 | cc(ti,1:2) = bc(bi,:) ;
75 | cc(ti, 3) = br(bi,:) ;
76 |
77 | end
78 |
79 |
80 |
--------------------------------------------------------------------------------
/mesh2d/cfmtri2.m:
--------------------------------------------------------------------------------
1 | function [vert,econ,tria] = cfmtri2(vert,econ)
2 | %CFMTRI2 compute a conforming 2-simplex Delaunay triangulat-
3 | %ion in the two-dimensional plane.
4 | % [VERT,CONN,TRIA]=CFMTRI2(VERT,CONN) computes the confor-
5 | % ming Delaunay trianguation, given the points VERT, and
6 | % edge constraints CONN. New points are inserted to bisect
7 | % edge constraints until all are recovered. VERT is a
8 | % V-by-2 array of XY coordinates to be triangulated, TRIA
9 | % is a T-by-3 array of vertex indexing, with each row
10 | % defining a triangle, such that VERT(TRIA(II,1),:),
11 | % VERT(TRIA(II,2),:) and VERT(TRIA(II,3),:) are the coord-
12 | % inates of the II-TH triangle. CONN is a C-by-2 array of
13 | % constraining edges, where each row defines an edge, as
14 | % per the TRIA array.
15 | %
16 | % See also DELTRI2, DELAUNAYN
17 |
18 | % Darren Engwirda : 2017 --
19 | % Email : de2363@columbia.edu
20 | % Last updated : 07/07/2017
21 |
22 | %---------------------------------------------- basic checks
23 | if ( ~isnumeric(vert) || ...
24 | ~isnumeric(econ) )
25 | error('cfmtri2:incorrectInputClass' , ...
26 | 'Incorrect input class.') ;
27 | end
28 |
29 | %---------------------------------------------- basic checks
30 | if (ndims(vert) ~= +2 || ndims(econ) ~= +2)
31 | error('cfmtri2:incorrectDimensions' , ...
32 | 'Incorrect input dimensions.');
33 | end
34 |
35 | if (size(vert,2)~= +2 || size(econ,2)~= +2)
36 | error('cfmtri2:incorrectDimensions' , ...
37 | 'Incorrect input dimensions.');
38 | end
39 |
40 | %-- the DELAUNAYN routine is *not* well-behaved numerically,
41 | %-- so explicitly re-scale the problem about [-1,-1; +1,+1].
42 | vmax = max(vert,[],1) ;
43 | vmin = min(vert,[],1) ;
44 |
45 | vdel = vmax - vmin;
46 | vdel = mean(vdel) ;
47 | vdel = vdel * +.5 ;
48 |
49 | vmid = vmax + vmin;
50 | vmid = vmid * +.5 ;
51 |
52 | vert = vert - vmid;
53 | vert = vert / vdel;
54 |
55 | %-- keep bisecting edge constraints until they are all reco-
56 | %-- vered!
57 | while (true)
58 |
59 | %----------------- un-constrained delaunay triangulation
60 | tria = delaunay2(vert) ;
61 |
62 | nv = size(vert,+1);
63 | nt = size(tria,+1);
64 |
65 | %----------------------------- build non-unique edge-set
66 | ee = zeros(nt*3,2);
67 | ee((1:nt)+nt*0,:) = tria(:,[1,2]);
68 | ee((1:nt)+nt*1,:) = tria(:,[2,3]);
69 | ee((1:nt)+nt*2,:) = tria(:,[3,1]);
70 |
71 | %----------------- find constraints within tria-edge set
72 | [in] = setset2(econ,ee) ;
73 |
74 | %----------------------------- done when have contraints
75 | if (all(in)), break; end
76 |
77 | %----------------------------- un-recovered edge centres
78 | vm = vert(econ(~in,1),:) ...
79 | + vert(econ(~in,2),:) ;
80 | vm = vm * +.5 ;
81 |
82 | %----------------------------- un-recovered edge indexes
83 | ev = nv+(1:size(vm,1))';
84 | en = [econ(~in,+1), ev;
85 | econ(~in,+2), ev];
86 |
87 | %----------------------------- push new vert/edge arrays
88 | vert = [vert( :,:); vm];
89 | econ = [econ(in,:); en];
90 |
91 | end
92 |
93 | %--------------------------------- undo geomertic re-scaling
94 | vert = vert * vdel ;
95 | vert = vert + vmid ;
96 |
97 | end
98 |
99 | function [tria] = delaunay2(vert)
100 | %DELAUNAY2 thin wrapper for DELAUNAYN, so that we can have a
101 | % more efficient version in OCTAVE...
102 |
103 | isoctave = exist( ...
104 | 'OCTAVE_VERSION','builtin')>+0;
105 |
106 | if (isoctave)
107 |
108 | %-- call QHULL and then filter zero-volume simplexes via
109 | %-- vectorised area comparisons.
110 |
111 | %-- note silliness re. EVAL, so that MATLAB doesn't com-
112 | %-- plain re. OCTAVE '__' names.
113 |
114 | tria = eval( ...
115 | '__delaunayn__(vert)') ;
116 |
117 | ab = vert(tria(:,2),:) ...
118 | - vert(tria(:,1),:) ;
119 | ac = vert(tria(:,3),:) ...
120 | - vert(tria(:,1),:) ;
121 |
122 | aa = ab(:,1).* ac(:,2) ...
123 | - ab(:,2).* ac(:,1) ;
124 |
125 | lb = sumsq(ab,2) ;
126 | lc = sumsq(ac,2) ;
127 |
128 | ll = max (lb,lc) ;
129 |
130 | keep = abs(aa) >= ll * eps^.8 ;
131 |
132 | tria = tria(keep,:);
133 |
134 | else
135 |
136 | %-- the default call in MATLAB seems to be fast enough!!
137 |
138 | tria = ...
139 | delaunay (vert(:,1),vert(:,2));
140 |
141 | end
142 |
143 | end
144 |
145 |
146 |
--------------------------------------------------------------------------------
/mesh2d/compile.m:
--------------------------------------------------------------------------------
1 | function compile
2 | %COMPILE compile any MESH2D dependencies into MEX//OCT files
3 | %for MATLAB//OCTAVE.
4 | %
5 | % See also TRIDEMO, REFINE2, SMOOTH2
6 |
7 | %-----------------------------------------------------------
8 | % Darren Engwirda : 2017 --
9 | % Email : de2363@columbia.edu
10 | % Last updated : 17/07/2017
11 | %-----------------------------------------------------------
12 |
13 | if (exist('OCTAVE_VERSION','builtin') > 0)
14 |
15 | fprintf(1,'\n');
16 |
17 | fprintf(1,'OCTAVE detected: Compiling OCT files.\n');
18 |
19 | fprintf(1,'\n');
20 |
21 | fprintf(1,'Compiling INPOLY2...\n');
22 | mkoctfile('inpoly2_oct.cpp');
23 |
24 | %%!! add additional oct-file builds here...
25 |
26 |
27 | fprintf(1,'\n');
28 |
29 | fprintf(1,'Compiling MESH2D for OCTAVE: DONE.\n');
30 |
31 | fprintf(1,'\n');
32 |
33 | else
34 |
35 | fprintf(1,'\n');
36 |
37 | fprintf(1,'MATLAB detected: nothing to compile!!\n');
38 |
39 | fprintf(1,'\n');
40 |
41 | fprintf(1,'Compiling MESH2D for MATLAB: DONE.\n');
42 |
43 | fprintf(1,'\n');
44 |
45 | end
46 |
47 | end
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/mesh2d/deltri2.m:
--------------------------------------------------------------------------------
1 | function [vert,conn,tria,tnum] = deltri2(varargin)
2 | %DELTRI2 compute a constrained 2-simplex Delaunay triangula-
3 | %tion in the two-dimensional plane.
4 | % [VERT,CONN,TRIA,TNUM]=DELTRI2(VERT,CONN,NODE,PSLG,PART)
5 | % computes the Delaunay trianguation {VERT,TRIA}, the con-
6 | % straints CONN, and the "inside" status vector TNUM. VERT
7 | % is an V-by-2 array of XY coordinates to be triangulated,
8 | % TRIA is a T-by-3 array of vertex indexing, where each
9 | % row defines a triangle, such that VERT(TRIA(II,1),:),
10 | % VERT(TRIA(II,2),:) and VERT(TRIA(II,3),:) are the coord-
11 | % inates of the II-TH triangle. CONN is a C-by-2 array of
12 | % constraining edges, where each row defines an edge, as
13 | % per TRIA. The additional arguments NODE,PSLG and PART
14 | % define a (mutliply-connected) polygonal region, where
15 | % NODE is an N-by-2 array of vertices and PSLG is a P-by-2
16 | % array of edges (a piecewise-straight-line-graph), where
17 | % each row defines an edge as a pair of indices into NODE.
18 | % PART is a cell-array of polygonal "parts", where each
19 | % element PART{KK} is an array of edge indices defining a
20 | % polygonal region. PSLG(PART{KK},:) is the set of edges
21 | % in the KK-TH part. TNUM is a T-by-1 array of part index-
22 | % ing, such that TNUM(II) is the index of the part in whi-
23 | % ch the II-TH triangle resides.
24 | %
25 | % See also DELAUNAYTRIANGULATION, DELAUNAYTRI, DELAUNAYN
26 |
27 | % Darren Engwirda : 2017 --
28 | % Email : de2363@columbia.edu
29 | % Last updated : 10/07/2017
30 |
31 | vert = []; conn = []; node = []; PSLG = [];
32 | part = {}; kind = 'constrained';
33 |
34 | %---------------------------------------------- extract args
35 | if (nargin>=+1), vert = varargin{1}; end
36 | if (nargin>=+2), conn = varargin{2}; end
37 | if (nargin>=+3), node = varargin{3}; end
38 | if (nargin>=+4), PSLG = varargin{4}; end
39 | if (nargin>=+5), part = varargin{5}; end
40 | if (nargin>=+6), kind = varargin{6}; end
41 |
42 | %---------------------------------------------- basic checks
43 | if (~isnumeric(vert) || ~isnumeric(conn) || ...
44 | ~isnumeric(node) || ~isnumeric(PSLG) || ...
45 | ~iscell (part) || ~ischar (kind) )
46 | error('deltri2:incorrectInputClass' , ...
47 | 'Incorrect input class.') ;
48 | end
49 |
50 | nvrt = size(vert,+1) ; nnod = size(node,+1) ;
51 | nedg = size(PSLG,+1) ;
52 |
53 | %---------------------------------------------- basic checks
54 | if (ndims(vert) ~= +2 || ndims(conn) ~= +2)
55 | error('deltri2:incorrectDimensions' , ...
56 | 'Incorrect input dimensions.');
57 | end
58 | if (size(vert,2)~= +2 || size(conn,2)~= +2)
59 | error('deltri2:incorrectDimensions' , ...
60 | 'Incorrect input dimensions.');
61 | end
62 |
63 | if (min([conn(:)])<+1 || max([conn(:)])>nvrt)
64 | error('deltri2:invalidInputs', ...
65 | 'Invalid CONN input array.') ;
66 | end
67 |
68 | %---------------------------------------------- basic checks
69 | if (nargin >= +3)
70 |
71 | if (ndims(node) ~= +2 || ndims(PSLG) ~= +2)
72 | error('deltri2:incorrectDimensions' , ...
73 | 'Incorrect input dimensions.');
74 | end
75 | if (size(node,2)~= +2 || size(PSLG,2)~= +2)
76 | error('deltri2:incorrectDimensions' , ...
77 | 'Incorrect input dimensions.');
78 | end
79 |
80 | if (min([PSLG(:)])<+1 || max([PSLG(:)])>nnod)
81 | error('deltri2:invalidInputs', ...
82 | 'Invalid EDGE input array.') ;
83 | end
84 |
85 | pmin = cellfun(@min,part);
86 | pmax = cellfun(@max,part);
87 |
88 | if (min([pmin(:)])<+1 || max([pmax(:)])>nedg)
89 | error('deltri2:invalidInputs', ...
90 | 'Invalid PART input array.') ;
91 | end
92 |
93 | end
94 |
95 | %------------------------------------ compute Delaunay tria.
96 | switch (lower(kind))
97 | case 'constrained'
98 |
99 | if (exist( ...
100 | 'delaunayTriangulation') == +2 )
101 | %-------------------------------- use class if available
102 | dtri = ...
103 | delaunayTriangulation(vert,conn) ;
104 | vert = dtri.Points;
105 | conn = dtri.Constraints;
106 | tria = dtri.ConnectivityList;
107 | else
108 | if (exist('DelaunayTri') == +2 )
109 | %-------------------------------- use class if available
110 | dtri = DelaunayTri (vert,conn) ;
111 | vert = dtri.X;
112 | conn = dtri.Constraints;
113 | tria = dtri.Triangulation;
114 | else
115 | %-------------------------------- *fall-back* onto qhull
116 | [vert,conn,tria] ...
117 | = cfmtri2(vert,conn) ;
118 | end
119 | end
120 |
121 | case 'conforming'
122 |
123 | %-------------------------------- "conforming" delaunay!
124 | [vert,conn,tria] ...
125 | = cfmtri2(vert,conn) ;
126 |
127 | otherwise
128 | error('deltri2:invalidInputs', ...
129 | 'Invalid KIND selection.') ;
130 |
131 | end
132 |
133 | %------------------------------------ calc. "inside" status!
134 | tnum = zeros(size(tria,+1),+1) ;
135 |
136 | if (nargin >= +3)
137 |
138 | tmid = vert(tria(:,1),:) ...
139 | + vert(tria(:,2),:) ...
140 | + vert(tria(:,3),:) ;
141 | tmid = tmid / +3.0;
142 |
143 | for ppos = 1 : length(part)
144 |
145 | [stat] = inpoly2( ...
146 | tmid,node , ...
147 | PSLG(part{ppos},:)) ;
148 |
149 | tnum(stat) = ppos ;
150 |
151 | end
152 |
153 | %------------------------------------ keep "interior" tria's
154 | tria = tria(tnum>+0,:) ;
155 | tnum = tnum(tnum>+0,:) ;
156 |
157 | end
158 |
159 | %------------------------------------ flip for correct signs
160 | area = triarea(vert,tria) ;
161 |
162 | tria(area<0.,:) = ...
163 | tria(area<0.,[1,3,2]) ;
164 |
165 | end
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/mesh2d/fixgeo2.m:
--------------------------------------------------------------------------------
1 | function [node,PSLG,part] = fixgeo2(varargin)
2 | %FIXGEO2 attempts to "fix" issues with geometry definitions.
3 | % [NNEW,ENEW,PNEW] = FIXGEO2(NODE,EDGE,PART) returns a new
4 | % "repaired" geometry definition. Currently, the following
5 | % operations are performed:
6 | %
7 | % (1) redundant nodes are "zipped" together.
8 | % (2) redundant edges are deleted.
9 | % (3) edges are split about intersecting nodes.
10 | % (4) edges are split about intersecting edges.
11 | %
12 | % See also REFINE2, BFSGEO2
13 |
14 | %-----------------------------------------------------------
15 | % Darren Engwirda : 2017 --
16 | % Email : de2363@columbia.edu
17 | % Last updated : 10/10/2017
18 | %-----------------------------------------------------------
19 |
20 | filename = mfilename('fullpath') ;
21 | filepath = fileparts( filename ) ;
22 |
23 | addpath([filepath,'/aabb-tree']) ;
24 |
25 | %---------------------------------------------- extract ARGS
26 | node = []; PSLG = []; part = {};
27 |
28 | if (nargin>=+1), node = varargin{1}; end
29 | if (nargin>=+2), PSLG = varargin{2}; end
30 | if (nargin>=+3), part = varargin{3}; end
31 |
32 | if (isempty(node)), return ; end
33 |
34 | %---------------------------------------------- default EDGE
35 | nnum = size(node,1);
36 |
37 | if (isempty(PSLG))
38 | PSLG = [(1:nnum-1)',(2:nnum-0)';nnum,1];
39 | end
40 |
41 | %---------------------------------------------- default PART
42 | enum = size(PSLG,1);
43 |
44 | if (isempty(part)), part{1} = (1:enum)'; end
45 |
46 | %---------------------------------------------- basic checks
47 | if ( ~isnumeric(node) || ...
48 | ~isnumeric(PSLG) || ...
49 | ~iscell(part) )
50 | error('fixgeo2:incorrectInputClass', ...
51 | 'Incorrect input class.') ;
52 | end
53 |
54 | %---------------------------------------------- basic checks
55 | if (ndims(node) ~= +2 || ...
56 | ndims(PSLG) ~= +2 )
57 | error('fixgeo2:incorrectDimensions', ...
58 | 'Incorrect input dimensions.') ;
59 | end
60 | if (size(node,2)~= +2 || ...
61 | size(PSLG,2)~= +2 )
62 | error('fixgeo2:incorrectDimensions', ...
63 | 'Incorrect input dimensions.') ;
64 | end
65 |
66 | %---------------------------------------------- basic checks
67 | if (min([PSLG(:)])<+1 || ...
68 | max([PSLG(:)]) > nnum)
69 | error('fixgeo2:invalidInputs', ...
70 | 'Invalid EDGE input array.') ;
71 | end
72 |
73 | pmin = cellfun(@min,part);
74 | pmax = cellfun(@max,part);
75 |
76 | if (min([pmin(:)])<+1 || ...
77 | max([pmax(:)]) > enum)
78 | error('fixgeo2:invalidInputs', ...
79 | 'Invalid PART input array.') ;
80 | end
81 |
82 | %------------------------------------- try to "fix" geometry
83 | while (true)
84 |
85 | nnum = size(node,1) ;
86 | enum = size(PSLG,1) ;
87 |
88 | %--------------------------------- prune redundant nodes
89 | [node,PSLG,part] = ...
90 | prunenode(node,PSLG,part) ;
91 |
92 | %--------------------------------- prune redundant edges
93 | [node,PSLG,part] = ...
94 | pruneedge(node,PSLG,part) ;
95 |
96 | %--------------------------------- node//edge intersect!
97 | done = false;
98 | while (~done)
99 |
100 | [node,PSLG,part,done] = ...
101 | splitnode(node,PSLG,part) ;
102 |
103 | end
104 |
105 | %--------------------------------- edge//edge intersect!
106 | done = false;
107 | while (~done)
108 |
109 | [node,PSLG,part,done] = ...
110 | splitedge(node,PSLG,part) ;
111 |
112 | end
113 |
114 | if (size(node,1) == nnum && ...
115 | size(PSLG,1) == enum )
116 | %--------------------------------- iterate if any change
117 | break ;
118 | end
119 |
120 | end
121 |
122 | end
123 |
124 | function [node,PSLG,part,done] ...
125 | = prunenode(node,PSLG,part)
126 | %PRUNENODE "prune" redundant nodes by "zipping" those within
127 | %tolerance of each other.
128 |
129 | done = true ;
130 |
131 | %------------------------------------- calc. "zip" tolerance
132 | nmin = min(node,[],+1) ;
133 | nmax = max(node,[],+1) ;
134 | ndel = nmax - nmin;
135 | ztol = eps ^ 0.80;
136 | zlen = ztol * max(ndel);
137 |
138 | %------------------------------------- index clustered nodes
139 | ball = zeros(size(node,1),3) ;
140 | ball(:,1:2) = node(:,1:2);
141 | ball(:, 3) = zlen * zlen;
142 |
143 | [vp,vi] = ...
144 | findball(ball,node(:,1:2)) ;
145 |
146 | %------------------------------------- "zip" clustered nodes
147 | [vt,iv] = ...
148 | sort(vp(:,2) - vp(:,1));
149 |
150 | izip = zeros(size(node,1),1) ;
151 | imap = zeros(size(node,1),1) ;
152 |
153 | for kk = size(vp,1):-1:+1
154 | ii = iv(kk);
155 | for ip = vp(ii,1) : vp(ii,2)
156 | jj = vi(ip) ;
157 | if (izip(ii) == 0 && ...
158 | izip(jj) == 0 && ...
159 | ii~= jj )
160 |
161 | done = false ;
162 |
163 | %----------------------------- "zip" node JJ into II
164 | izip(jj) = ii ;
165 |
166 | end
167 | end
168 | end
169 |
170 | %------------------------------------- re-index nodes//edges
171 | next = +1 ;
172 | for kk = +1:+1:size(vp,1)
173 | if (izip(kk) == +0)
174 | imap(kk) = next ;
175 | next = next + 1 ;
176 | end
177 | end
178 |
179 | imap(izip ~= 0) = ...
180 | imap(izip(izip ~= 0));
181 |
182 | PSLG = imap(PSLG) ;
183 |
184 | node = node(izip == 0,:);
185 |
186 | end
187 |
188 | function [node,PSLG,part] = pruneedge(node,PSLG,part)
189 | %PRUNEEDGE "prune" redundant topology.
190 |
191 | %------------------------------------- prune redundant topo.
192 | [ptmp,ivec,jvec] = ...
193 | unique(sort(PSLG,+2),'rows') ;
194 |
195 | PSLG = PSLG(ivec,:);
196 |
197 | for ppos = +1 : length(part)
198 |
199 | %--------------------------------- re-index part labels!
200 | part{ppos} = ...
201 | unique(jvec(part{ppos})) ;
202 |
203 | end
204 |
205 | %------------------------------------- prune collapsed topo.
206 | keep = diff(PSLG,[],2) ~= +0 ;
207 |
208 | jvec = zeros(size(PSLG,1),1) ;
209 | jvec(keep) = +1;
210 | jvec = cumsum(jvec);
211 |
212 | PSLG = PSLG(keep,:);
213 |
214 | for ppos = +1 : length(part)
215 |
216 | %--------------------------------- re-index part labels!
217 | part{ppos} = ...
218 | unique(jvec(part{ppos})) ;
219 |
220 | end
221 |
222 | end
223 |
224 | function [node,PSLG,part,done] ...
225 | = splitnode(node,PSLG,part)
226 | %SPLITNODE "split" PSLG about intersecting nodes.
227 |
228 | done = true ;
229 |
230 | mark = false(size(PSLG,1),1);
231 | ediv = zeros(size(PSLG,1),1);
232 | pair = zeros(size(PSLG,1),2);
233 |
234 | %------------------------------------- node//edge intersect!
235 | [lp,li] = findline ( ...
236 | node(PSLG(:,1),1:2), ...
237 | node(PSLG(:,2),1:2),node(:,1:2)) ;
238 |
239 | %------------------------------------- node//edge splitting!
240 | nn = +0 ;
241 | for ii = +1:+1:size(lp,1)
242 | for ip = lp(ii,1) : lp(ii,2)
243 | jj = li(ip) ;
244 | ni = PSLG(jj,1) ;
245 | nj = PSLG(jj,2) ;
246 | if (ni~=ii && ...
247 | nj~=ii && ~mark(jj))
248 |
249 | done = false;
250 |
251 | %----------------------------- mark seen, descendent
252 | mark(jj) = true ;
253 |
254 | nn = nn + 1 ;
255 |
256 | pair(nn,1) = jj ;
257 | pair(nn,2) = ii ;
258 |
259 | end
260 | end
261 | end
262 |
263 | if (nn == +0), return ; end
264 |
265 | %------------------------------------- re-index intersection
266 | pair = pair(1:nn,:);
267 |
268 | inod = PSLG(pair(:,1),1);
269 | jnod = PSLG(pair(:,1),2);
270 |
271 | xnod = pair(:,2);
272 |
273 | ediv(pair(:,1)) = ...
274 | (+1:nn)' + size(PSLG,1);
275 |
276 | PSLG(pair(:,1),1) = inod;
277 | PSLG(pair(:,1),2) = xnod;
278 |
279 | PSLG = [PSLG; xnod,jnod];
280 |
281 | %------------------------------------- re-index edge in part
282 | for ppos = +1:length(part)
283 |
284 | enew = ediv(part{ppos});
285 | enew = enew(enew ~= 0) ;
286 |
287 | part{ppos} = ...
288 | [part{ppos}; enew] ;
289 |
290 | end
291 |
292 | end
293 |
294 | function [node,PSLG,part,done] ...
295 | = splitedge(node,PSLG,part)
296 | %SPLITEDGE "split" PSLG about intersecting edges.
297 |
298 | done = true ;
299 |
300 | mark = zeros(size(PSLG,1),2);
301 | pair = zeros(size(PSLG,1),2);
302 | ediv = zeros(size(PSLG,1),1);
303 | flag = zeros(size(node,1),1);
304 |
305 | %------------------------------------- edge//edge intersect!
306 | [lp,li] = lineline( ...
307 | node(PSLG(:,1),1:2), ...
308 | node(PSLG(:,2),1:2), ...
309 | node(PSLG(:,1),1:2), ...
310 | node(PSLG(:,2),1:2)) ;
311 |
312 | %------------------------------------- edge//edge splitting!
313 | nn = +0 ;
314 | %---------------------------------- parse NaN delimited data
315 | for ii = +1 : +1 : size(lp,1)
316 |
317 | flag(PSLG(ii,1)) = ii ;
318 | flag(PSLG(ii,2)) = ii ;
319 |
320 | for ip = lp(ii,1) : lp(ii,2)
321 | jj = li(ip) ;
322 | if (mark(ii) == +0 && ...
323 | mark(jj) == +0 && ...
324 | ii ~= jj)
325 |
326 | ni = PSLG(jj,1) ;
327 | nj = PSLG(jj,2) ;
328 |
329 | if (flag(ni) ~= ii && ...
330 | flag(nj) ~= ii )
331 |
332 | done = false;
333 |
334 | %------------------------- mark seen, edge-pairs
335 | mark(ii) = +1 ;
336 | mark(jj) = +1 ;
337 |
338 | nn = nn + 1 ;
339 |
340 | pair(nn,1) = ii ;
341 | pair(nn,2) = jj ;
342 |
343 | end
344 |
345 | end
346 | end
347 |
348 | end
349 |
350 | if (nn == +0), return ; end
351 |
352 | %------------------------------------- re-index intersection
353 | pair = pair(1:nn,:) ;
354 |
355 | [okay,tval,sval] = linenear ( ...
356 | node(PSLG(pair(:,1),1),1:2), ...
357 | node(PSLG(pair(:,1),2),1:2), ...
358 | node(PSLG(pair(:,2),1),1:2), ...
359 | node(PSLG(pair(:,2),2),1:2)) ;
360 |
361 | pmid = .5 * ( ...
362 | node(PSLG(pair(:,1),2),1:2)+ ...
363 | node(PSLG(pair(:,1),1),1:2)) ;
364 | pdel = .5 * ( ...
365 | node(PSLG(pair(:,1),2),1:2)- ...
366 | node(PSLG(pair(:,1),1),1:2)) ;
367 |
368 | ppos = pmid+[tval,tval].*pdel;
369 |
370 | qmid = .5 * ( ...
371 | node(PSLG(pair(:,2),2),1:2)+ ...
372 | node(PSLG(pair(:,2),1),1:2)) ;
373 | qdel = .5 * ( ...
374 | node(PSLG(pair(:,2),2),1:2)- ...
375 | node(PSLG(pair(:,2),1),1:2)) ;
376 |
377 | qpos = qmid+[sval,sval].*qdel;
378 |
379 | inod = PSLG(pair(:,1),1);
380 | jnod = PSLG(pair(:,1),2);
381 |
382 | anod = PSLG(pair(:,2),1);
383 | bnod = PSLG(pair(:,2),2);
384 |
385 | xnod = (+1:nn)'+size(node,1) ;
386 |
387 | iedg = (+1:nn)'+size(PSLG,1) ...
388 | + 0 * size(pair,1) ;
389 | jedg = (+1:nn)'+size(PSLG,1) ...
390 | + 1 * size(pair,1) ;
391 |
392 | ediv(pair(:,1),1) = iedg;
393 | ediv(pair(:,2),1) = jedg;
394 |
395 | PSLG(pair(:,1),1) = inod;
396 | PSLG(pair(:,1),2) = xnod;
397 |
398 | PSLG(pair(:,2),1) = anod;
399 | PSLG(pair(:,2),2) = xnod;
400 |
401 | PSLG = [PSLG; xnod,jnod];
402 | PSLG = [PSLG; xnod,bnod];
403 |
404 | node = [node;(ppos+qpos)*.5] ;
405 |
406 | %------------------------------------- re-index edge in part
407 | for ppos = +1:length(part)
408 |
409 | enew = ediv(part{ppos});
410 | enew = enew(enew ~= 0) ;
411 |
412 | part{ppos} = ...
413 | [part{ppos}; enew] ;
414 |
415 | end
416 |
417 | end
418 |
419 |
420 |
--------------------------------------------------------------------------------
/mesh2d/getnan2.m:
--------------------------------------------------------------------------------
1 | function [node,edge] = getnan2(varargin)
2 | %GETNAN2 parse a NaN delimited polygon into a PSLG.
3 | % [NODE,EDGE] = GETNAN2(NANS,FILT) converts a set of NaN
4 | % delimited polygons to a PSLG representation. NANS is an
5 | % D-by-2 array of coordinates, with polygon vertices spec-
6 | % ified in connsecutive order, and delimited by NaN values
7 | % where breaks between polygons occur. FILT is a length 2
8 | % vector of "small-feature" filter values. Small polygons
9 | % wiith axis-aligned extents less than FILT are stripped
10 | % from the output. NODE is an N-by-2 array of coordinates
11 | % and EDGE is an E-by-2 array of edge indexing. Each row
12 | % in EDGE represents an edge of the polygon, such that
13 | % NODE(EDGE(JJ,1),:) and NODE(EDGE(JJ,2),:) are the coord-
14 | % inates of the endpoints of the JJ-TH edge.
15 | %
16 | % See also FIXGEO2, BFSGEO2, REFINE2
17 |
18 | %-----------------------------------------------------------
19 | % Darren Engwirda : 2017 --
20 | % Email : de2363@columbia.edu
21 | % Last updated : 06/10/2017
22 | %-----------------------------------------------------------
23 |
24 | data = [] ; filt = +0. ;
25 |
26 | if (nargin>=+1), data = varargin{1}; end
27 | if (nargin>=+2), filt = varargin{2}; end
28 |
29 | %---------------------------------------------- basic checks
30 | if ( ~isnumeric(data) || ...
31 | ~isnumeric(filt) )
32 | error('getnan2:incorrectInputClass' , ...
33 | 'Incorrect input class.') ;
34 | end
35 |
36 | %---------------------------------------------- basic checks
37 | if (ndims(data) ~= +2 )
38 | error('getnan2:incorrectDimensions' , ...
39 | 'Incorrect input dimensions.');
40 | end
41 |
42 | if (size(data,2)~= +2 || ...
43 | size(filt,2)>= +2 )
44 | error('getnan2:incorrectDimensions' , ...
45 | 'Incorrect input dimensions.');
46 | end
47 |
48 | %---------------------------------- parse NaN delimited data
49 | nvec = find(isnan(data(:,1))) ;
50 |
51 | if (isempty(nvec)) % no NaN's at all!
52 | nvec = [nvec ; size(data,1) ] ;
53 | end
54 |
55 | if (nvec(end)~=size(data,1) ) % append last poly
56 | nvec = [nvec ; size(data,1) ] ;
57 | end
58 |
59 | node = zeros(size(data,1), 2) ;
60 | edge = zeros(size(data,1), 2) ;
61 |
62 | next = +1; nout = +1; eout = +1 ;
63 |
64 | for npos = +1 : length(nvec)
65 |
66 | stop = nvec(npos) ;
67 |
68 | pnew = data(next:stop-1,1:2);
69 |
70 | pmin = min(pnew,[],1) ;
71 | pmax = max(pnew,[],1) ;
72 |
73 | pdel = pmax-pmin;
74 |
75 | if (~isempty(pnew))
76 | if (any(pdel>filt))
77 | %---------------------------------- push polygon onto output
78 | nnew = size(pnew,1);
79 |
80 | enew = [(1:nnew-1)', ...
81 | (2:nnew-0)'; ...
82 | nnew, +1 ] ;
83 | enew = enew+nout-1 ;
84 |
85 | mnew = size(enew,1);
86 |
87 | pvec = nout:nout+nnew-1;
88 | evec = eout:eout+mnew-1;
89 |
90 | node(pvec,:) = pnew;
91 | edge(evec,:) = enew;
92 |
93 | nout = nout + nnew ;
94 | eout = eout + mnew ;
95 |
96 | end
97 | end
98 |
99 | next = stop + 1 ;
100 |
101 | end
102 |
103 | node = node(+1:nout-1,:) ;
104 | edge = edge(+1:eout-1,:) ;
105 |
106 | end
107 |
108 |
109 |
--------------------------------------------------------------------------------
/mesh2d/idxtri2.m:
--------------------------------------------------------------------------------
1 | function [tree] = idxtri2(vert,tria)
2 | %IDXTRI2 create a spatial-indexing structure for a 2-simplex
3 | %triangulation embedded in the two-dimensional plane.
4 | % [TREE] = IDXTRI2(VERT,TRIA) returns an AABB-tree design-
5 | % ed to accelerate spatial queries into the triangulation
6 | % defined by {VERT,TRIA}. VERT is a V-by-2 array of XY co-
7 | % ordinates and TRIA is a T-by-3 array of triangles. Each
8 | % row defines a triangle, such that VERT(TRIA(II,1),:),
9 | % VERT(TRIA(II,2),:) and VERT(TRIA(II,3),:) are the coord-
10 | % inates of the II-TH triangle.
11 | %
12 | % See also TRIHFN2, LFSHFN2, MAKETREE
13 |
14 | % Darren Engwirda : 2017 --
15 | % Email : de2363@columbia.edu
16 | % Last updated : 03/07/2017
17 |
18 | filename = mfilename('fullpath');
19 | filepath = fileparts( filename );
20 |
21 | addpath([filepath,'/aabb-tree']);
22 |
23 | %---------------------------------------------- basic checks
24 | if ( ~isnumeric(vert) || ...
25 | ~isnumeric(tria) )
26 | error('idxtri2:incorrectInputClass' , ...
27 | 'Incorrect input class.') ;
28 | end
29 |
30 | %---------------------------------------------- basic checks
31 | if (ndims(vert) ~= +2 || ...
32 | ndims(tria) ~= +2 )
33 | error('idxtri2:incorrectDimensions' , ...
34 | 'Incorrect input dimensions.');
35 | end
36 | if (size(vert,2)~= +2 || ...
37 | size(tria,2) < +3 )
38 | error('idxtri2:incorrectDimensions' , ...
39 | 'Incorrect input dimensions.');
40 | end
41 |
42 | nvrt = size(vert,1) ;
43 |
44 | %---------------------------------------------- basic checks
45 | if (min(min(tria(:,1:3))) < +1 || ...
46 | max(max(tria(:,1:3))) > nvrt )
47 | error('idxtri2:invalidInputs', ...
48 | 'Invalid TRIA input array.') ;
49 | end
50 |
51 | %------------------------------ calc. AABB indexing for TRIA
52 | bmin = vert(tria(:,1),:);
53 | bmax = vert(tria(:,1),:);
54 |
55 | for ii = 2 : size(tria,2)
56 | bmin = ...
57 | min(bmin,vert(tria(:,ii), :));
58 | bmax = ...
59 | max(bmax,vert(tria(:,ii), :));
60 | end
61 |
62 | isoctave = exist( ...
63 | 'OCTAVE_VERSION','builtin') > +0 ;
64 |
65 | if (isoctave)
66 |
67 | %-- faster for OCTAVE with large tree block size; slower
68 | %-- loop execution...
69 |
70 | opts.nobj = +256 ;
71 |
72 | else
73 |
74 | %-- faster for MATLAB with small tree block size; better
75 | %-- loop execution...
76 |
77 | opts.nobj = + 16 ;
78 |
79 | end
80 |
81 | tree = maketree([bmin,bmax],opts);
82 |
83 | end
84 |
85 |
86 |
--------------------------------------------------------------------------------
/mesh2d/inpoly2.m:
--------------------------------------------------------------------------------
1 | function [stat] = inpoly2(varargin)
2 | %INPOLY2 compute "points-in-polygon" queries.
3 | % [STAT] = INPOLY2(VERT,NODE,EDGE) returns the "inside/ou-
4 | % tside" status for a set of vertices VERT and a polygon
5 | % {NODE,EDGE} embedded in a two-dimensional plane. General
6 | % non-convex and multiply-connected polygonal regions can
7 | % be handled. VERT is an N-by-2 array of XY coordinates to
8 | % be tested. STAT is an associated N-by-1 logical array,
9 | % with STAT(II) = TRUE if VERT(II,:) is an interior point.
10 | % The polygonal region is defined as a piecewise-straight-
11 | % line-graph, where NODE is an M-by-2 array of polygon ve-
12 | % rtices and EDGE is a P-by-2 array of edge indexing. Each
13 | % row in EDGE represents an edge of the polygon, such that
14 | % NODE(EDGE(KK,1),:) and NODE(EDGE(KK,2),:) are the coord-
15 | % inates of the endpoints of the KK-TH edge. If the argum-
16 | % ent EDGE is omitted it assumed that the vertices in NODE
17 | % are connected in ascending order.
18 | %
19 | % See also INPOLYGON
20 |
21 | % This algorithm is based on a "crossing-number" test, co-
22 | % unting the number of times a line extending from each
23 | % point past the right-most region of the polygon interse-
24 | % cts with the polygonal boundary. Points with odd counts
25 | % are "inside". A simple implementation requires that each
26 | % edge intersection be checked for each point, leading to
27 | % O(N*M) complexity...
28 | %
29 | % This implementation seeks to improve these bounds:
30 | %
31 | % * Sorting the query points by y-value and determining can-
32 | % didate edge intersection sets via binary-search. Given a
33 | % configuration with N test points, M edges and an average
34 | % point-edge "overlap" of H, the overall complexity scales
35 | % like O(M*H + M*LOG(N) + N*LOG(N)), where O(N*LOG(N))
36 | % operations are required for sorting, O(M*LOG(N)) operat-
37 | % ions required for the set of binary-searches, and O(M*H)
38 | % operations required for the intersection tests, where H
39 | % is typically small on average, such that H << N.
40 | %
41 | % * Carefully checking points against the bounding-box asso-
42 | % ciated with each polygon edge. This minimises the number
43 | % of calls to the (relatively) expensive edge intersection
44 | % test.
45 |
46 | % Darren Engwirda : 2017 --
47 | % Email : de2363@columbia.edu
48 | % Last updated : 17/07/2017
49 |
50 | %---------------------------------------------- extract args
51 | node = []; edge = []; vert = [];
52 |
53 | if (nargin>=+1), vert = varargin{1}; end
54 | if (nargin>=+2), node = varargin{2}; end
55 | if (nargin>=+3), edge = varargin{3}; end
56 |
57 | %---------------------------------------------- default args
58 | nnod = size(node,1) ;
59 | nvrt = size(vert,1) ;
60 |
61 | if (isempty(edge))
62 | edge = [(1:nnod-1)',(2:nnod)'; nnod,1];
63 | end
64 |
65 | %---------------------------------------------- basic checks
66 | if ( ~isnumeric(node) || ...
67 | ~isnumeric(edge) || ...
68 | ~isnumeric(vert) )
69 | error('inpoly2:incorrectInputClass' , ...
70 | 'Incorrect input class.') ;
71 | end
72 |
73 | %---------------------------------------------- basic checks
74 | if (ndims(node) ~= +2 || ...
75 | ndims(edge) ~= +2 || ...
76 | ndims(vert) ~= +2 )
77 | error('inpoly2:incorrectDimensions' , ...
78 | 'Incorrect input dimensions.');
79 | end
80 | if (size(node,2)~= +2 || ...
81 | size(edge,2)~= +2 || ...
82 | size(vert,2)~= +2 )
83 | error('inpoly2:incorrectDimensions' , ...
84 | 'Incorrect input dimensions.');
85 | end
86 |
87 | %---------------------------------------------- basic checks
88 | if (min([edge(:)]) < +1 || ...
89 | max([edge(:)]) > nnod)
90 | error('inpoly2:invalidInputs', ...
91 | 'Invalid EDGE input array.') ;
92 | end
93 |
94 | %-------------- flip to ensure the y-axis is the "long" axis
95 | vmin = min(vert,[],1);
96 | vmax = max(vert,[],1);
97 | ddxy = vmax - vmin ;
98 |
99 | if (ddxy(1) > ddxy(2))
100 | vert = vert(:,[2,1]) ;
101 | node = node(:,[2,1]) ;
102 | end
103 |
104 | %----------------------------------- sort points via y-value
105 | swap = ...
106 | node(edge(:,2),2) ...
107 | < node(edge(:,1),2) ;
108 |
109 | edge(swap,[1,2]) = ...
110 | edge(swap,[2,1]) ;
111 |
112 | [~,ivec] = ...
113 | sort(vert(:,+2)) ;
114 | vert = vert (ivec,:) ;
115 |
116 | if (exist( ...
117 | 'OCTAVE_VERSION','builtin') > +0)
118 |
119 | if (exist('inpoly2_oct','file') == +3)
120 |
121 | %-- delegate to the compiled version of the code if it's
122 | %-- available
123 |
124 | [stat] = ...
125 | inpoly2_oct(vert,node,edge) ;
126 |
127 | else
128 |
129 | %-- otherwise, just call the native m-code version
130 |
131 | [stat] = ...
132 | inpoly2_loc(vert,node,edge) ;
133 |
134 | end
135 |
136 | else
137 |
138 | %-- MATLAB's JIT is generally smart enough these days to
139 | %-- run this efficiently
140 |
141 | [stat] = ...
142 | inpoly2_loc(vert,node,edge) ;
143 |
144 | end
145 |
146 | stat(ivec) = stat ;
147 |
148 | end
149 |
150 | function [stat] = inpoly2_loc(vert,node,edge)
151 | %INPOLY2_LOC the local m-code version of the crossing-number
152 | %test. Loop over edges; do a binary-search for the first ve-
153 | %rtex that intersects with the edge y-range; do crossing-nu-
154 | %mber comparisons; break when the local y-range is exceeded.
155 |
156 | nvrt = size (vert,1) ;
157 | nnod = size (node,1) ;
158 | nedg = size (edge,1) ;
159 |
160 | stat = false(nvrt,1) ;
161 |
162 | %----------------------------------- loop over polygon edges
163 | for epos = +1 : size(edge,1)
164 |
165 | inod = edge(epos,1) ;
166 | jnod = edge(epos,2) ;
167 |
168 | %------------------------------- calc. edge bounding-box
169 | yone = node(inod,2) ;
170 | ytwo = node(jnod,2) ;
171 | xone = node(inod,1) ;
172 | xtwo = node(jnod,1) ;
173 |
174 | ydel = ytwo - yone;
175 | xdel = xtwo - xone;
176 |
177 | xmin = min(xone,xtwo) ;
178 |
179 | %------------------------------- find VERT(IPOS,2)<=YONE
180 | ilow = +1 ; iupp = nvrt ;
181 |
182 | while (ilow < iupp - 1)
183 | imid = ilow ...
184 | + floor((iupp-ilow) / 2);
185 |
186 | if (vert(imid,2) < yone)
187 | ilow = imid ;
188 | else
189 | iupp = imid ;
190 | end
191 | end
192 |
193 | %------------------------------- calc. edge-intersection
194 | for jpos = ilow+1 : nvrt
195 |
196 | ypos = vert(jpos,2) ;
197 | if (ypos < ytwo)
198 | xpos = vert(jpos,1) ;
199 | if (xpos >= xmin)
200 | if ( ...
201 | ydel* (xpos-xone) < ...
202 | xdel* (ypos-yone) )
203 |
204 | stat(jpos) = ...
205 | ~stat(jpos) ;
206 | end
207 | else
208 | stat(jpos) = ...
209 | ~stat(jpos) ;
210 | end
211 | else
212 | break ; % done -- due to the sort
213 | end
214 |
215 | end
216 |
217 | end
218 |
219 | end
220 |
221 |
222 |
223 |
--------------------------------------------------------------------------------
/mesh2d/inpoly2_oct.cpp:
--------------------------------------------------------------------------------
1 |
2 | // a fast pre-sorted variant of the crossing-number test for
3 | // INPOLY2.m
4 |
5 | //----------------------------------------------------------
6 | // Darren Engwirda : 2017 --
7 | // Email : de2363@columbia.edu
8 | // Last updated : 17/07/2017
9 | //----------------------------------------------------------
10 |
11 | #include
12 |
13 | DEFUN_DLD (inpoly2_oct, args, ,
14 | "-- INPOLY2-OCT: low-level routine called by INPOLY2 to \n"
15 | "-- compute point-in-polygon queries.")
16 | {
17 | octave_value_list rval;
18 |
19 | int const nargin = args.length () ;
20 | if (nargin != +3)
21 | {
22 | print_usage () ;
23 | return rval ;
24 | }
25 |
26 | Matrix const vert (
27 | args(0).matrix_value ()) ;
28 | Matrix const node (
29 | args(1).matrix_value ()) ;
30 | Matrix const edge (
31 | args(2).matrix_value ()) ;
32 |
33 | if (error_state) return rval ;
34 |
35 | octave_idx_type const nvrt
36 | = vert.rows () ;
37 | octave_idx_type const nnod
38 | = node.rows () ;
39 | octave_idx_type const nedg
40 | = edge.rows () ;
41 |
42 | //---------------------------------- init. crossing no. bool
43 | boolMatrix stat(nvrt, 1) ;
44 |
45 | octave_idx_type vpos ;
46 | for (vpos = +0; vpos != nvrt; ++vpos)
47 | {
48 | stat(vpos) = false ;
49 | }
50 |
51 | //---------------------------------- loop over polygon edges
52 | octave_idx_type epos ;
53 | for (epos = +0; epos != nedg; ++epos)
54 | {
55 | octave_idx_type const inod
56 | = edge(epos,0) - 1 ;
57 | octave_idx_type const jnod
58 | = edge(epos,1) - 1 ;
59 |
60 | //------------------------------ calc. edge bounding-box
61 | double yone = node (inod,1) ;
62 | double ytwo = node (jnod,1) ;
63 | double xone = node (inod,0) ;
64 | double xtwo = node (jnod,0) ;
65 |
66 | double ydel = ytwo - yone ;
67 | double xdel = xtwo - xone ;
68 |
69 | double xmin = xone < xtwo
70 | ? xone : xtwo ;
71 |
72 | //------------------------------ find VERT(IPOS,2)<=YONE
73 | octave_idx_type ilow = +0 ;
74 | octave_idx_type iupp = nvrt-1;
75 | octave_idx_type imid = +0 ;
76 |
77 | while (ilow < iupp - 1)
78 | {
79 | imid = ilow
80 | + (iupp - ilow) / 2;
81 |
82 | if (vert(imid,1) < yone)
83 | {
84 | ilow = imid ;
85 | }
86 | else
87 | {
88 | iupp = imid ;
89 | }
90 | }
91 |
92 | //------------------------------ calc. edge-intersection
93 | octave_idx_type vpos = ilow+1 ;
94 | for ( ; vpos != nvrt; ++vpos)
95 | {
96 | double xpos = vert(vpos,0);
97 | double ypos = vert(vpos,1);
98 |
99 | if (ypos < ytwo)
100 | {
101 | if (xpos >= xmin)
102 | {
103 | if (
104 | ydel* (xpos-xone) <
105 | xdel* (ypos-yone) )
106 | {
107 | stat(vpos) =
108 | ! stat(vpos) ;
109 | }
110 | }
111 | else
112 | {
113 | stat(vpos) =
114 | ! stat(vpos) ;
115 | }
116 | }
117 | else
118 | {
119 | break ; // done -- due to the sort
120 | }
121 | }
122 |
123 | }
124 |
125 | rval(0) = stat;
126 |
127 | return rval;
128 | }
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/mesh2d/isfeat2.m:
--------------------------------------------------------------------------------
1 | function [is,bv] = isfeat2(pp,ee,tt)
2 | %ISFEAT2 return "feature" status for the triangles in a two-
3 | %dimensional constrained triangulation.
4 | % [STAT] = ISFEAT2(VERT,EDGE,TRIA) returns STAT(II) = TRUE
5 | % for any triangle including a sufficiently "sharp" angle
6 | % located at the apex of any two constrained edges. Sharp
7 | % features have angles of greater-than ACOS(+0.8) degrees.
8 |
9 | % Darren Engwirda : 2017 --
10 | % Email : de2363@columbia.edu
11 | % Last updated : 27/01/2017
12 |
13 | %---------------------------------------------- basic checks
14 | if (~isnumeric(pp) || ~isnumeric(ee) || ...
15 | ~isnumeric(tt) )
16 | error('isfeat2:incorrectInputClass' , ...
17 | 'Incorrect input class.') ;
18 | end
19 |
20 | %---------------------------------------------- basic checks
21 | if (ndims(pp) ~= +2 || ndims(ee) ~= +2 || ...
22 | ndims(tt) ~= +2 )
23 | error('isfeat2:incorrectDimensions' , ...
24 | 'Incorrect input dimensions.');
25 | end
26 | if (size(pp,2)~= +2 || size(ee,2) < +5 || ...
27 | size(tt,2) < +6 )
28 | error('isfeat2:incorrectDimensions' , ...
29 | 'Incorrect input dimensions.');
30 | end
31 |
32 | nnod = size(pp,1) ; nedg = size(ee,1) ;
33 | ntri = size(tt,1) ;
34 |
35 | %---------------------------------------------- basic checks
36 | if (min(min(tt(:,1:3))) < +1 || ...
37 | max(max(tt(:,1:3))) > nnod)
38 | error('isfeat2:invalidInputs', ...
39 | 'Invalid TRIA input array.') ;
40 | end
41 | if (min(min(tt(:,4:6))) < +1 || ...
42 | max(max(tt(:,4:6))) > nedg)
43 | error('isfeat2:invalidInputs', ...
44 | 'Invalid TRIA input array.') ;
45 | end
46 |
47 | if (min(min(ee(:,1:2))) < +1 || ...
48 | max(max(ee(:,1:2))) > nnod)
49 | error('isfeat2:invalidInputs', ...
50 | 'Invalid EDGE input array.') ;
51 | end
52 | if (min(min(ee(:,3:4))) < +0 || ...
53 | max(max(ee(:,3:4))) > ntri)
54 | error('isfeat2:invalidInputs', ...
55 | 'Invalid EDGE input array.') ;
56 | end
57 |
58 | %----------------------------------------- compute "feature"
59 | is = false(size(tt,1),1);
60 | bv = false(size(tt,1),3);
61 |
62 | EI = [3, 1, 2] ;
63 | EJ = [1, 2, 3] ;
64 | NI = [3, 1, 2] ;
65 | NJ = [1, 2, 3] ;
66 | NK = [2, 3, 1] ;
67 |
68 | for ii = +1 : +3
69 |
70 | %------------------------------------- common edge index
71 | ei = tt( :,EI(ii)+3);
72 | ej = tt( :,EJ(ii)+3);
73 |
74 | %------------------------------------- is boundary edge?
75 | bi = ee(ei,5) >= +1 ;
76 | bj = ee(ej,5) >= +1 ;
77 |
78 | ok = bi & bj ;
79 |
80 | if (~any(ok)), continue; end
81 |
82 | ni = tt(ok,NI(ii)+0);
83 | nj = tt(ok,NJ(ii)+0);
84 | nk = tt(ok,NK(ii)+0);
85 |
86 | %------------------------------------- adj. edge vectors
87 | vi = pp(ni,:)-pp(nj,:) ;
88 | vj = pp(nk,:)-pp(nj,:) ;
89 |
90 | %------------------------------------- adj. edge lengths
91 | li = sqrt(sum(vi.^2,2));
92 | lj = sqrt(sum(vj.^2,2));
93 |
94 | ll = li .* lj ;
95 |
96 | %------------------------------------- adj. dot-product!
97 | aa = sum(vi.*vj,2)./ll ;
98 |
99 | bv(ok,ii) = aa >= +.80 ;
100 |
101 | is(ok) = ...
102 | is(ok) | bv(ok,ii) ;
103 |
104 | end
105 |
106 | end
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/mesh2d/lfshfn2.m:
--------------------------------------------------------------------------------
1 | function [vert,tria,hlfs] = lfshfn2(varargin)
2 | %LFSHFN2 calc. a discrete "local-feature-size" estimate for
3 | %a polygonal domain embedded in R^2.
4 | % [VERT,TRIA,HFUN] = LFSHFN2(NODE,EDGE) returns the trian-
5 | % gulated "feature-size" estimate for the polygonal region
6 | % {NODE,EDGE}. NODE is an N-by-2 array of polygonal verti-
7 | % ces and EDGE is an E-by-2 array of edge indexing. Each
8 | % row in EDGE represents an edge of the polygon, such that
9 | % NODE(EDGE(JJ,1),:) and NODE(EDGE(JJ,2),:) are the coord-
10 | % inates of the endpoints of the JJ-TH edge. If the argum-
11 | % ent EDGE is omitted it assumed that the vertices in NODE
12 | % are connected in ascending order.
13 | %
14 | % [...] = LFSHFN2(NODE,EDGE,PART) computes a size-estimate
15 | % for a multiply-connected geometry. PART is a cell-array
16 | % of polygonal "parts", where each element PART{KK} is an
17 | % array of edge indices defining a given polygonal region.
18 | % EDGE(PART{KK}, :) is the set of edges in the KK-TH part.
19 | %
20 | % VERT is a V-by-2 array of XY coordinates, TRIA is a T-by
21 | % -3 array of triangles and HFUN is a V-by-1 array of mesh
22 | % -size values. Each row of TRIA defines a triangle, such
23 | % that VERT(TRIA(II,1),:), VERT(TRIA(II,2),:) and VERT(
24 | % TRIA(II,3),:) are the coordinates of the II-TH triangle.
25 | %
26 | % See also TRIHFN2, LIMHFN2, IDXTRI2
27 |
28 | % Darren Engwirda : 2017 --
29 | % Email : de2363@columbia.edu
30 | % Last updated : 24/01/2017
31 |
32 | %---------------------------------------------- extract args
33 | node = []; PSLG = []; part = {}; opts = [] ;
34 |
35 | if (nargin>=+1), node = varargin{1}; end
36 | if (nargin>=+2), PSLG = varargin{2}; end
37 | if (nargin>=+3), part = varargin{3}; end
38 | if (nargin>=+4), opts = varargin{4}; end
39 |
40 | %------------------------------ build coarse background grid
41 | [opts] = makeopt(opts);
42 |
43 | [vert,conn,tria,tnum] = ...
44 | refine2(node,PSLG,part,opts) ;
45 |
46 | %------------------------------ estimate local-feature-size!
47 | hlfs = ...
48 | +inf * ones(size(vert,1),1) ;
49 |
50 | %------------------------------ calc. LFS based on edge-len.
51 | evec = vert(conn(:,2),:) - ...
52 | vert(conn(:,1),:) ;
53 | elen = sqrt(sum(evec.^2,2)) ;
54 | hlen = elen * +1. ;
55 |
56 | for epos = +1 : size(conn,+1)
57 |
58 | ivrt = conn(epos,1) ;
59 | jvrt = conn(epos,2) ;
60 |
61 | hlfs(ivrt) = min( ...
62 | hlfs(ivrt),hlen(epos)) ;
63 | hlfs(jvrt) = min( ...
64 | hlfs(jvrt),hlen(epos)) ;
65 |
66 | end
67 |
68 | %------------------------------ push gradient limits on HFUN
69 | DHDX = opts.dhdx;
70 |
71 | hlfs = ...
72 | limhfn2(vert,tria,hlfs,DHDX) ;
73 |
74 | end
75 |
76 | function [opts] = makeopt(opts)
77 | %MAKEOPT setup the options structure for LFSHFN2.
78 |
79 | if (~isfield(opts,'kind'))
80 | opts.kind = 'delaunay';
81 | else
82 | if (~strcmpi(opts.kind,'delfront') && ...
83 | ~strcmpi(opts.kind,'delaunay') )
84 | error( ...
85 | 'lfshfn2:invalidOption','Invalid refinement KIND.');
86 | end
87 | end
88 |
89 | if (~isfield(opts,'rho2'))
90 | opts.rho2 = sqrt(+2.) ;
91 | else
92 | if (~isnumeric(opts.rho2))
93 | error('lfshfn2:incorrectInputClass', ...
94 | 'Incorrect input class.');
95 | end
96 | if (numel(opts.rho2)~= +1)
97 | error('lfshfn2:incorrectDimensions', ...
98 | 'Incorrect input dimensions.') ;
99 | end
100 | if (opts.rho2 < +1.)
101 | error('lfshfn2:invalidOptionValues', ...
102 | 'Invalid OPT.RHO2 selection.') ;
103 | end
104 | end
105 |
106 | if (~isfield(opts,'dhdx'))
107 | opts.dhdx = +0.2500 ;
108 | else
109 | if (~isnumeric(opts.dhdx))
110 | error('lfshfn2:incorrectInputClass', ...
111 | 'Incorrect input class.');
112 | end
113 | if (numel(opts.dhdx)~= +1)
114 | error('lfshfn2:incorrectDimensions', ...
115 | 'Incorrect input dimensions.') ;
116 | end
117 | if (opts.dhdx <= 0.)
118 | error('lfshfn2:invalidOptionValues', ...
119 | 'Invalid OPT.DHDX selection.') ;
120 | end
121 | end
122 |
123 | end
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/mesh2d/limgrad.m:
--------------------------------------------------------------------------------
1 | function [ffun,flag] = limgrad(edge,elen,ffun,dfdx,imax)
2 | %LIMGRAD impose "gradient-limits" on a function defined over
3 | %an undirected graph.
4 | % [FNEW] = LIMGRAD(EDGE,ELEN,FFUN,DFDX,ITER) computes a
5 | % "gradient-limited" function FNEW on the undirected graph
6 | % {EDGE,ELEN}, where EDGE is an NE-by-2 array of edge ind-
7 | % ices, and ELEN is an NE-by-1 array of edge lengths.
8 | % Gradients are limited over the graph edges, such that
9 | %
10 | % ABS(FNEW(N2)-FNEW(N1)) <= ELEN(II) * DFDX,
11 | %
12 | % where N1=EDGE(II,1) and N2=EDGE(II,2) are the two nodes
13 | % in the II-TH edge. An iterative algorithm is used, swee-
14 | % ping over an "active-set" of graph edges until converge-
15 | % nce is achieved. A maximum of IMAX iterations are done.
16 | %
17 | % [FNEW,FLAG] = LIMGRAD(...) also returns a boolean FLAG,
18 | % with FLAG=TRUE denoting convergence.
19 | %
20 | % See also LIMHFN2
21 |
22 | % Darren Engwirda : 2017 --
23 | % Email : de2363@columbia.edu
24 | % Last updated : 18/04/2017
25 |
26 | %---------------------------------------------- basic checks
27 | if ( ~isnumeric(edge) || ...
28 | ~isnumeric(elen) || ...
29 | ~isnumeric(ffun) || ...
30 | ~isnumeric(dfdx) || ...
31 | ~isnumeric(imax) )
32 | error('limgrad:incorrectInputClass' , ...
33 | 'Incorrect input class.') ;
34 | end
35 | %---------------------------------------------- basic checks
36 | if (ndims(edge) ~= +2 || ...
37 | ndims(elen) > +2 || ...
38 | ndims(ffun) > +2 || ...
39 | numel(dfdx) ~= +1 || ...
40 | numel(imax) ~= +1 )
41 | error('limgrad:incorrectDimensions' , ...
42 | 'Incorrect input dimensions.');
43 | end
44 | if (size(edge,2) < +2 || ...
45 | size(elen,2)~= +1 || ...
46 | size(ffun,2)~= +1 || ...
47 | size(edge,1)~= size(elen,1) )
48 | error('limgrad:incorrectDimensions' , ...
49 | 'Incorrect input dimensions.');
50 | end
51 |
52 | nnod = size(ffun,1) ;
53 |
54 | %---------------------------------------------- basic checks
55 | if (dfdx < +0. || imax < +0)
56 | error('limgrad:invalidInputArgument', ...
57 | 'Invalid input parameter.');
58 | end
59 | if (min(min(edge(:,1:2))) < +1 || ...
60 | max(max(edge(:,1:2))) > nnod )
61 | error('limgrad:invalidInputArgument', ...
62 | 'Invalid EDGE input array.') ;
63 | end
64 |
65 | %-- IVEC(NPTR(II,1):NPTR(II,2)) are edges adj. to II-TH node
66 | nvec = [edge(:,1); edge(:,2)];
67 | ivec = [(1:size(edge,1))'; ...
68 | (1:size(edge,1))'] ;
69 |
70 | [nvec,pidx] = sort (nvec) ;
71 | ivec = ivec (pidx) ;
72 |
73 | mark = false(nnod,1) ;
74 | mark(edge(:,1)) = true ;
75 | mark(edge(:,2)) = true ;
76 |
77 | idxx = find(diff(nvec) > +0) ;
78 |
79 | nptr = zeros(nnod,2) ;
80 | nptr(:,2) = -1 ;
81 | nptr(mark,1) = [+1; idxx+1];
82 | nptr(mark,2) = [idxx; nnod];
83 |
84 | %----------------------------- ASET=ITER if node is "active"
85 | aset = zeros(size(ffun,1),1) ;
86 |
87 | %----------------------------- exhaustive 'til all satisfied
88 | ftol = min(ffun) * sqrt(eps) ;
89 |
90 | for iter = +1 : imax
91 |
92 | %------------------------- find "active" nodes this pass
93 | aidx = find(aset == iter - 1) ;
94 |
95 | if (isempty(aidx)), break; end
96 |
97 | %------------------------- reorder => better convergence
98 | [aval,idxx] = sort(ffun(aidx)) ;
99 |
100 | aidx = aidx(idxx);
101 |
102 | %------------------------- visit adj. edges and set DFDX
103 | for ipos = 1 : length(aidx)
104 | npos = aidx(ipos) ;
105 | for jpos = nptr(npos,1) ...
106 | : nptr(npos,2)
107 |
108 | epos = ivec(jpos,1) ;
109 |
110 | nod1 = edge(epos,1) ;
111 | nod2 = edge(epos,2) ;
112 |
113 | %----------------- calc. limits about min.-value
114 | if (ffun(nod1) > ffun(nod2))
115 |
116 | fun1 = ffun(nod2) ...
117 | + elen(epos) * dfdx ;
118 |
119 | if (ffun(nod1) > fun1+ftol)
120 | ffun(nod1) = fun1;
121 | aset(nod1) = iter;
122 | end
123 |
124 | else
125 |
126 | fun2 = ffun(nod1) ...
127 | + elen(epos) * dfdx ;
128 |
129 | if (ffun(nod2) > fun2+ftol)
130 | ffun(nod2) = fun2;
131 | aset(nod2) = iter;
132 | end
133 |
134 | end
135 |
136 | end
137 | end
138 |
139 | end
140 |
141 | flag = (iter < imax) ;
142 |
143 | end
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/mesh2d/limhfn2.m:
--------------------------------------------------------------------------------
1 | function [hfun] = limhfn2(vert,tria,hfun,dhdx)
2 | %LIMHFN2 impose gradient limits on a discrete mesh-size fun-
3 | %ction defined over a 2-simplex triangulation.
4 | % [HFUN] = LIMHFN2(VERT,TRIA,HFUN,DHDX) returns a "gradie-
5 | % nt-limited" function HFUN, defined over a triangulation
6 | % {VERT,TRIA}. HFUN is a T-by-1 vector of function values,
7 | % VERT is a V-by-2 array of XY coordinates and TRIA is a
8 | % T-by-3 array of triangles. Each row of TRIA
9 | % defines a triangle, such that VERT(TRIA(II,1),:), VERT(
10 | % TRIA(II,2),:) and VERT(TRIA(II,3),:) are the coordinates
11 | % of the II-TH triangle. DHDX is a scalar gradient-limit.
12 | % HFUN is "limited" to control variation over the elements
13 | % in the triangulation, such that (HFUN(V2)-HFUN(V1))/LL<=
14 | % DHDX, where {V1,V2} are the vertices of a given triangle
15 | % edge and LL is the edge-length. Limits are enforced exh-
16 | % austively over all edges.
17 | %
18 | % See also TRIHFN2, LFSHFN2
19 |
20 | % This function is based on a very simplified version of:
21 | % Persson, P.O. "Mesh size functions for implicit geometr-
22 | % ies and PDE-based gradient limiting." Engineering with
23 | % Computers 22 (2006): 95-109.
24 |
25 | % Darren Engwirda : 2017 --
26 | % Email : de2363@columbia.edu
27 | % Last updated : 18/04/2017
28 |
29 | %---------------------------------------------- basic checks
30 | if ( ~isnumeric(vert) || ...
31 | ~isnumeric(tria) || ...
32 | ~isnumeric(hfun) || ...
33 | ~isnumeric(dhdx) )
34 | error('limhfn2:incorrectInputClass' , ...
35 | 'Incorrect input class.') ;
36 | end
37 |
38 | %---------------------------------------------- basic checks
39 | if (ndims(vert) ~= +2 || ...
40 | ndims(tria) ~= +2 || ...
41 | ndims(hfun) ~= +2 || ...
42 | numel(dhdx) ~= +1 )
43 | error('limhfn2:incorrectDimensions' , ...
44 | 'Incorrect input dimensions.');
45 | end
46 | if (size(vert,2)~= +2 || ...
47 | size(tria,2) < +3 || ...
48 | size(hfun,2)~= +1 || ...
49 | size(vert,1)~= size(hfun,1) )
50 | error('limhfn2:incorrectDimensions' , ...
51 | 'Incorrect input dimensions.');
52 | end
53 |
54 | nvrt = size(vert,1) ;
55 |
56 | %---------------------------------------------- basic checks
57 | if (min(min(tria(:,1:3))) < +1 || ...
58 | max(max(tria(:,1:3))) > nvrt )
59 | error('limhfn2:invalidInputArgument', ...
60 | 'Invalid TRIA input array.') ;
61 | end
62 |
63 | %-------------------- impose gradient limits over mesh edges
64 | [edge,tria] = tricon2(tria);
65 |
66 | evec = vert(edge(:,2),:) - ...
67 | vert(edge(:,1),:) ;
68 | elen = sqrt(sum(evec.^2,2)) ;
69 |
70 | %-------------------- impose gradient limits over edge-graph
71 | [hfun] = limgrad( ...
72 | edge,elen,hfun,dhdx,sqrt(nvrt)) ;
73 |
74 | end
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/mesh2d/minlen2.m:
--------------------------------------------------------------------------------
1 | function [ll,ei] = minlen2(pp,tt)
2 | %MINLEN2 return the minimum length edge for each triangle in
3 | %a two-dimensional triangulation.
4 | % [ELEN,IMIN] = MINLEN2(VERT,TRIA) returns the minimum le-
5 | % ngth ELEN and local edge index IMIN for all triangles in
6 | % the triangulation {VERT,TRIA}.
7 |
8 | % Darren Engwirda : 2017 --
9 | % Email : de2363@columbia.edu
10 | % Last updated : 16/01/2017
11 |
12 | %---------------------------------------------- basic checks
13 | if (~isnumeric(pp) || ~isnumeric(tt) )
14 | error('minlen2:incorrectInputClass' , ...
15 | 'Incorrect input class.') ;
16 | end
17 |
18 | %---------------------------------------------- basic checks
19 | if (ndims(pp) ~= +2 || ndims(tt) ~= +2 )
20 | error('minlen2:incorrectDimensions' , ...
21 | 'Incorrect input dimensions.');
22 | end
23 | if (size(pp,2)~= +2 || size(tt,2) < +3 )
24 | error('minlen2:incorrectDimensions' , ...
25 | 'Incorrect input dimensions.');
26 | end
27 |
28 | nnod = size(pp,1) ;
29 |
30 | %---------------------------------------------- basic checks
31 | if (min(min(tt(:,1:3))) < +1 || ...
32 | max(max(tt(:,1:3))) > nnod )
33 | error('minlen2:invalidInputs', ...
34 | 'Invalid TRIA input array.') ;
35 | end
36 |
37 | %------------------------------------------ compute edge-len
38 | l1 = sum((pp(tt(:,2),:) ...
39 | -pp(tt(:,1),:)).^2,2);
40 | l2 = sum((pp(tt(:,3),:) ...
41 | -pp(tt(:,2),:)).^2,2);
42 | l3 = sum((pp(tt(:,1),:) ...
43 | -pp(tt(:,3),:)).^2,2);
44 |
45 | %------------------------------------------ compute min.-len
46 | [ll,ei] = min([l1,l2,l3],[],+2);
47 |
48 | end
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/mesh2d/poly-data/lake-1-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metorm/Unstructured-Level-Set-Method/f55d6ffc7223fc4c6a75aadd083c8525e7642309/mesh2d/poly-data/lake-1-small.png
--------------------------------------------------------------------------------
/mesh2d/poly-data/lake-2-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/metorm/Unstructured-Level-Set-Method/f55d6ffc7223fc4c6a75aadd083c8525e7642309/mesh2d/poly-data/lake-2-small.png
--------------------------------------------------------------------------------
/mesh2d/poly-data/wavy-channel.msh:
--------------------------------------------------------------------------------
1 | # channel.msh file, created by JIGSAW
2 | # https://github.com/dengwirda/jigsaw-matlab
3 | mshid=1
4 | ndims=2
5 | point=103
6 | 0;0;0
7 | 5;0;0
8 | 5;1.1;0
9 | 4.95;1.096858316112863;0
10 | 4.9;1.087630668004386;0
11 | 4.85;1.072896862742141;0
12 | 4.8;1.0535826794979;0
13 | 4.75;1.030901699437495;0
14 | 4.7;1.006279051952931;0
15 | 4.65;0.9812618685414277;0
16 | 4.6;0.9574220708434924;0
17 | 4.55;0.9362576010251309;0
18 | 4.5;0.9190983005625052;0
19 | 4.45;0.9070223514111748;0
20 | 4.4;0.9007885298685523;0
21 | 4.35;0.9007885298685523;0
22 | 4.3;0.9070223514111749;0
23 | 4.25;0.9190983005625053;0
24 | 4.2;0.936257601025131;0
25 | 4.15;0.9574220708434925;0
26 | 4.1;0.9812618685414279;0
27 | 4.05;1.006279051952932;0
28 | 4;1.030901699437495;0
29 | 3.95;1.0535826794979;0
30 | 3.9;1.072896862742141;0
31 | 3.85;1.087630668004387;0
32 | 3.8;1.096858316112863;0
33 | 3.75;1.1;0
34 | 3.7;1.096858316112863;0
35 | 3.65;1.087630668004386;0
36 | 3.6;1.072896862742141;0
37 | 3.55;1.0535826794979;0
38 | 3.5;1.030901699437495;0
39 | 3.45;1.006279051952931;0
40 | 3.4;0.9812618685414274;0
41 | 3.35;0.9574220708434924;0
42 | 3.3;0.9362576010251309;0
43 | 3.25;0.9190983005625052;0
44 | 3.2;0.9070223514111748;0
45 | 3.15;0.9007885298685522;0
46 | 3.1;0.9007885298685523;0
47 | 3.05;0.9070223514111749;0
48 | 3;0.9190983005625053;0
49 | 2.95;0.9362576010251312;0
50 | 2.9;0.9574220708434928;0
51 | 2.85;0.9812618685414275;0
52 | 2.8;1.006279051952931;0
53 | 2.75;1.030901699437495;0
54 | 2.7;1.0535826794979;0
55 | 2.65;1.072896862742141;0
56 | 2.6;1.087630668004386;0
57 | 2.55;1.096858316112863;0
58 | 2.5;1.1;0
59 | 2.45;1.096858316112863;0
60 | 2.4;1.087630668004386;0
61 | 2.35;1.072896862742141;0
62 | 2.3;1.0535826794979;0
63 | 2.25;1.030901699437495;0
64 | 2.2;1.006279051952931;0
65 | 2.15;0.9812618685414274;0
66 | 2.1;0.9574220708434927;0
67 | 2.05;0.9362576010251311;0
68 | 2;0.9190983005625052;0
69 | 1.95;0.9070223514111749;0
70 | 1.9;0.9007885298685522;0
71 | 1.85;0.9007885298685522;0
72 | 1.8;0.9070223514111748;0
73 | 1.75;0.9190983005625053;0
74 | 1.7;0.9362576010251309;0
75 | 1.65;0.9574220708434926;0
76 | 1.6;0.9812618685414275;0
77 | 1.55;1.006279051952931;0
78 | 1.5;1.030901699437495;0
79 | 1.45;1.0535826794979;0
80 | 1.4;1.072896862742141;0
81 | 1.35;1.087630668004386;0
82 | 1.3;1.096858316112863;0
83 | 1.25;1.1;0
84 | 1.2;1.096858316112863;0
85 | 1.15;1.087630668004386;0
86 | 1.1;1.072896862742141;0
87 | 1.05;1.0535826794979;0
88 | 1;1.030901699437495;0
89 | 0.9500000000000001;1.006279051952931;0
90 | 0.9;0.9812618685414275;0
91 | 0.8500000000000001;0.9574220708434927;0
92 | 0.8;0.936257601025131;0
93 | 0.75;0.9190983005625053;0
94 | 0.7000000000000001;0.9070223514111748;0
95 | 0.65;0.9007885298685522;0
96 | 0.6000000000000001;0.9007885298685522;0
97 | 0.55;0.9070223514111748;0
98 | 0.5;0.9190983005625053;0
99 | 0.45;0.936257601025131;0
100 | 0.4;0.9574220708434927;0
101 | 0.35;0.9812618685414275;0
102 | 0.3;1.006279051952931;0
103 | 0.25;1.030901699437495;0
104 | 0.2;1.0535826794979;0
105 | 0.15;1.072896862742141;0
106 | 0.1;1.087630668004386;0
107 | 0.05;1.096858316112863;0
108 | 0;1.1;0
109 | edge2=103
110 | 0;1;0
111 | 1;2;0
112 | 2;3;0
113 | 3;4;0
114 | 4;5;0
115 | 5;6;0
116 | 6;7;0
117 | 7;8;0
118 | 8;9;0
119 | 9;10;0
120 | 10;11;0
121 | 11;12;0
122 | 12;13;0
123 | 13;14;0
124 | 14;15;0
125 | 15;16;0
126 | 16;17;0
127 | 17;18;0
128 | 18;19;0
129 | 19;20;0
130 | 20;21;0
131 | 21;22;0
132 | 22;23;0
133 | 23;24;0
134 | 24;25;0
135 | 25;26;0
136 | 26;27;0
137 | 27;28;0
138 | 28;29;0
139 | 29;30;0
140 | 30;31;0
141 | 31;32;0
142 | 32;33;0
143 | 33;34;0
144 | 34;35;0
145 | 35;36;0
146 | 36;37;0
147 | 37;38;0
148 | 38;39;0
149 | 39;40;0
150 | 40;41;0
151 | 41;42;0
152 | 42;43;0
153 | 43;44;0
154 | 44;45;0
155 | 45;46;0
156 | 46;47;0
157 | 47;48;0
158 | 48;49;0
159 | 49;50;0
160 | 50;51;0
161 | 51;52;0
162 | 52;53;0
163 | 53;54;0
164 | 54;55;0
165 | 55;56;0
166 | 56;57;0
167 | 57;58;0
168 | 58;59;0
169 | 59;60;0
170 | 60;61;0
171 | 61;62;0
172 | 62;63;0
173 | 63;64;0
174 | 64;65;0
175 | 65;66;0
176 | 66;67;0
177 | 67;68;0
178 | 68;69;0
179 | 69;70;0
180 | 70;71;0
181 | 71;72;0
182 | 72;73;0
183 | 73;74;0
184 | 74;75;0
185 | 75;76;0
186 | 76;77;0
187 | 77;78;0
188 | 78;79;0
189 | 79;80;0
190 | 80;81;0
191 | 81;82;0
192 | 82;83;0
193 | 83;84;0
194 | 84;85;0
195 | 85;86;0
196 | 86;87;0
197 | 87;88;0
198 | 88;89;0
199 | 89;90;0
200 | 90;91;0
201 | 91;92;0
202 | 92;93;0
203 | 93;94;0
204 | 94;95;0
205 | 95;96;0
206 | 96;97;0
207 | 97;98;0
208 | 98;99;0
209 | 99;100;0
210 | 100;101;0
211 | 101;102;0
212 | 102;0;0
213 |
--------------------------------------------------------------------------------
/mesh2d/relhfn2.m:
--------------------------------------------------------------------------------
1 | function [hrel] = relhfn2(pp,tt,hv)
2 | %RELHFN2 calc. relative edge-length for a 2-simplex triangu-
3 | %lation embedded in the two-dimensional plane.
4 | % [HREL] = RELHFN2(VERT,TRIA) returns the rel. edge-len.,
5 | % indicating conformance to the imposed mesh-spacing cons-
6 | % traints, where HREL is a E-by-1 vector, VERT is a V-by-2
7 | % array of XY coordinates, and TRIA is a T-by-3 array of
8 | % vertex indexing, where each row defines a triangle, such
9 | % that VERT(TRIA(II,1),:), VERT(TRIA(II,2),:) and VERT(
10 | % TRIA(II,3),:) are the coordinates of the II-TH triangle.
11 | %
12 | % See also TRISCR2, TRIAREA, TRIANG2, TRIBAL2
13 |
14 | % Darren Engwirda : 2017 --
15 | % Email : de2363@columbia.edu
16 | % Last updated : 11/07/2017
17 |
18 | %---------------------------------------------- basic checks
19 | if (~isnumeric(pp) || ~isnumeric(tt) || ...
20 | ~isnumeric(hv) )
21 | error('relhfn2:incorrectInputClass' , ...
22 | 'Incorrect input class.') ;
23 | end
24 |
25 | %---------------------------------------------- basic checks
26 | if (ndims(pp) ~= +2 || ndims(tt) ~= +2 )
27 | error('relhfn2:incorrectDimensions' , ...
28 | 'Incorrect input dimensions.');
29 | end
30 | if (size(pp,2)~= +2 || size(tt,2) < +3 )
31 | error('relhfn2:incorrectDimensions' , ...
32 | 'Incorrect input dimensions.');
33 | end
34 | if (size(hv,2)~= +1 || size(hv,1) ~= size(pp,1) )
35 | error('relhfn2:incorrectDimensions' , ...
36 | 'Incorrect input dimensions.');
37 | end
38 |
39 | nnod = size(pp,1) ;
40 |
41 | %---------------------------------------------- basic checks
42 | if (min(min(tt(:,1:3))) < +1 || ...
43 | max(max(tt(:,1:3))) > nnod )
44 | error('relhfn2:invalidInputs', ...
45 | 'Invalid TRIA input array.') ;
46 | end
47 |
48 | %----------------------------------- compute rel. mesh-sizes
49 | [ee,tt] = tricon2(tt);
50 |
51 | evec = pp(ee(:,2),:)-pp(ee(:,1),:) ;
52 |
53 | elen = sqrt(sum(evec.^2,+2));
54 |
55 | hmid = hv(ee(:,2),:)+hv(ee(:,1),:) ;
56 | hmid = hmid * +0.50 ;
57 | hrel = elen ./ hmid ;
58 |
59 | end
60 |
61 |
62 |
--------------------------------------------------------------------------------
/mesh2d/setset2.m:
--------------------------------------------------------------------------------
1 | function [same,sloc] = setset2(iset,jset)
2 | %SETSET2 a (much) faster variant of ISMEMBER for edge lists.
3 | % [IN] = SETSET2(ISET,JSET) returns an I-by-1 array IN,
4 | % with IN(K) = TRUE if ISET(K,:) is present in JSET. This
5 | % routine is essentially an optimised ISMEMBER variant de-
6 | % signed for processing lists of edge indexing. ISET is an
7 | % I-by-2 array of "query" edges, JSET is a J-by-2 array of
8 | % edges to test against.
9 | %
10 | % See also ISMEMBER
11 |
12 | % Darren Engwirda : 2017 --
13 | % Email : de2363@columbia.edu
14 | % Last updated : 29/01/2017
15 |
16 | %---------------------------------------------- basic checks
17 | if ( ~isnumeric(iset) || ~isnumeric(jset) )
18 | error('setset2:incorrectInputClass' , ...
19 | 'Incorrect input class.') ;
20 | end
21 |
22 | %---------------------------------------------- basic checks
23 | if (ndims(iset) ~= +2 || ndims(jset) ~= +2)
24 | error('setset2:incorrectDimensions' , ...
25 | 'Incorrect input dimensions.');
26 | end
27 | if (size(iset,2)~= +2 || size(jset,2)~= +2)
28 | error('setset2:incorrectDimensions' , ...
29 | 'Incorrect input dimensions.');
30 | end
31 |
32 | %---------------------------------------------- set v1 <= v2
33 | iset = sort(iset,2) ;
34 | jset = sort(jset,2) ;
35 |
36 | %-- this is the slow, but easy-to-undertsand version of what
37 | %-- is happening here...
38 |
39 | % if (nargout == +1)
40 | % same = ismember(iset,jset,'rows') ;
41 | % else
42 | % [same,sloc] = ...
43 | % ismember(iset,jset,'rows') ;
44 |
45 | %-- as above, the 'ROWS' based call to ISMEMBER can be sped
46 | %-- up by casting the edge lists (i.e. pairs of UINT32 valu-
47 | %-- es) to DOUBLE, and performing the sorted queries on vec-
48 | %-- tor inputs!
49 | if (nargout == +1)
50 | same = ismember( ...
51 | iset*[2^31;1], jset*[2^31;1]) ;
52 | else
53 | [same,sloc] = ismember( ...
54 | iset*[2^31;1], jset*[2^31;1]) ;
55 | end
56 |
57 | end
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/mesh2d/triang2.m:
--------------------------------------------------------------------------------
1 | function [dcos] = triang2(pp,tt)
2 | %TRIANG2 calc. enclosed angles for a 2-simplex triangulation
3 | %embedded in the two-dimensional plane.
4 | % [ADEG] = TRIANG2(VERT,TRIA) returns the enclosed angles
5 | % associated with each triangle, where ADEG is a T-by-3
6 | % array of the angles subtended at each vertex, VERT is a
7 | % V-by-2 array of XY coordinates, and TRIA is a T-by-3 ar-
8 | % ray of vertex indexing, where each row defines a triang-
9 | % le, such that VERT(TRIA(II,1),:), VERT(TRIA(II,2),:) and
10 | % VERT(TRIA(II,3),:) are the coordinates of the II-TH tri-
11 | % angle. Angles are returned in degrees.
12 | %
13 | % See also TRISCR2, TRIAREA, TRIBAL2
14 |
15 | % Darren Engwirda : 2017 --
16 | % Email : de2363@columbia.edu
17 | % Last updated : 17/01/2017
18 |
19 | %---------------------------------------------- basic checks
20 | if (~isnumeric(pp) || ~isnumeric(tt) )
21 | error('triang2:incorrectInputClass' , ...
22 | 'Incorrect input class.') ;
23 | end
24 |
25 | %---------------------------------------------- basic checks
26 | if (ndims(pp) ~= +2 || ndims(tt) ~= +2 )
27 | error('triang2:incorrectDimensions' , ...
28 | 'Incorrect input dimensions.');
29 | end
30 | if (size(pp,2)~= +2 || size(tt,2) < +3 )
31 | error('triang2:incorrectDimensions' , ...
32 | 'Incorrect input dimensions.');
33 | end
34 |
35 | nnod = size(pp,1) ;
36 |
37 | %---------------------------------------------- basic checks
38 | if (min(min(tt(:,1:3))) < +1 || ...
39 | max(max(tt(:,1:3))) > nnod )
40 | error('triang2:invalidInputs', ...
41 | 'Invalid TRIA input array.') ;
42 | end
43 |
44 | %----------------------------------- compute enclosed angles
45 | dcos = zeros(size(tt,1),3) ;
46 |
47 | ev12 = pp(tt(:,2),:)-pp(tt(:,1),:);
48 | ev23 = pp(tt(:,3),:)-pp(tt(:,2),:);
49 | ev31 = pp(tt(:,1),:)-pp(tt(:,3),:);
50 |
51 | lv11 = sqrt(sum(ev12.^2,2));
52 | lv22 = sqrt(sum(ev23.^2,2));
53 | lv33 = sqrt(sum(ev31.^2,2));
54 |
55 | ev12 = ev12 ./ ...
56 | lv11(:,ones(1,size(pp,2)));
57 | ev23 = ev23 ./ ...
58 | lv22(:,ones(1,size(pp,2)));
59 | ev31 = ev31 ./ ...
60 | lv33(:,ones(1,size(pp,2)));
61 |
62 | dcos(:,1) = sum(-ev12.*ev23,2);
63 | dcos(:,2) = sum(-ev23.*ev31,2);
64 | dcos(:,3) = sum(-ev31.*ev12,2);
65 |
66 | dcos = acos(dcos) * 180. / pi ;
67 |
68 | end
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/mesh2d/triarea.m:
--------------------------------------------------------------------------------
1 | function [area] = triarea(pp,tt)
2 | %TRIAREA calc. triangle areas for a 2-simplex triangulation
3 | %embedded in the two-dimensional plane.
4 | % [AREA] = TRIAREA(VERT,TRIA) returns the signed triangle
5 | % areas, where AREA is a T-by-1 vector, VERT is a V-by-2
6 | % array of XY coordinates, and TRIA is a T-by-3 array of
7 | % vertex indexing, where each row defines a triangle, such
8 | % that VERT(TRIA(II,1),:), VERT(TRIA(II,2),:) and VERT(
9 | % TRIA(II,3),:) are the coordinates of the II-TH triangle.
10 | %
11 | % See also TRISCR2, TRIANG2, TRIBAL2
12 |
13 | % Darren Engwirda : 2017 --
14 | % Email : engwirda@mit.edu
15 | % Last updated : 17/01/2017
16 |
17 | %---------------------------------------------- basic checks
18 | if (~isnumeric(pp) || ~isnumeric(tt) )
19 | error('triarea:incorrectInputClass' , ...
20 | 'Incorrect input class.') ;
21 | end
22 |
23 | %---------------------------------------------- basic checks
24 | if (ndims(pp) ~= +2 || ndims(tt) ~= +2 )
25 | error('triarea:incorrectDimensions' , ...
26 | 'Incorrect input dimensions.');
27 | end
28 | if (size(pp,2)~= +2 || size(tt,2) < +3 )
29 | error('triarea:incorrectDimensions' , ...
30 | 'Incorrect input dimensions.');
31 | end
32 |
33 | nnod = size(pp,1) ;
34 |
35 | %---------------------------------------------- basic checks
36 | if (min(min(tt(:,1:3))) < +1 || ...
37 | max(max(tt(:,1:3))) > nnod )
38 | error('triarea:invalidInputs', ...
39 | 'Invalid TRIA input array.') ;
40 | end
41 |
42 | %--------------------------------------- compute signed area
43 | ev12 = pp(tt(:,2),:)-pp(tt(:,1),:) ;
44 | ev13 = pp(tt(:,3),:)-pp(tt(:,1),:) ;
45 |
46 | switch (size(pp,2))
47 | case +2
48 |
49 | area = ev12(:,1).*ev13(:,2) ...
50 | - ev12(:,2).*ev13(:,1) ;
51 | area = 0.5 * area;
52 |
53 | case +3
54 |
55 | avec = cross(ev12,ev13);
56 | area = sqrt(sum(avec.^2,2)) ;
57 | area = 0.5 * area;
58 |
59 | otherwise
60 | error('Unsupported dimension.') ;
61 | end
62 |
63 | end
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/mesh2d/tribal2.m:
--------------------------------------------------------------------------------
1 | function [bb] = tribal2(pp,tt)
2 | %TRIBAL2 compute the circumballs associated with a 2-simplex
3 | %triangulation embedded in R^2.
4 | % [BB] = TRIBAL2(PP,TT) returns the circumscribing balls
5 | % associated with the triangles in [PP,TT], such that BB =
6 | % [XC,YC,RC.^2].
7 |
8 | % Darren Engwirda : 2017 --
9 | % Email : engwirda@mit.edu
10 | % Last updated : 29/04/2017
11 |
12 | %---------------------------------------------- basic checks
13 | if (~isnumeric(pp) || ...
14 | ~isnumeric(tt) )
15 | error('tribal2:incorrectInputClass' , ...
16 | 'Incorrect input class.') ;
17 | end
18 | %---------------------------------------------- basic checks
19 | if (ndims(pp) ~= +2 || ndims(tt) ~= +2)
20 | error('tribal2:incorrectDimensions' , ...
21 | 'Incorrect input dimensions.');
22 | end
23 | if (size(pp,2)~= +2 || size(tt,2) < +3)
24 | error('tribal2:incorrectDimensions' , ...
25 | 'Incorrect input dimensions.');
26 | end
27 |
28 | %------------------------------------------------ lhs matrix
29 | ab = pp(tt(:,2),:)-pp(tt(:,1),:) ;
30 | ac = pp(tt(:,3),:)-pp(tt(:,1),:) ;
31 |
32 | %------------------------------------------------ rhs vector
33 | rv11 = sum(ab.*ab,2) ;
34 | rv22 = sum(ac.*ac,2) ;
35 |
36 | %------------------------------------------------ solve sys.
37 | dd = ab(:,1) .* ac(:,2) - ...
38 | ab(:,2) .* ac(:,1) ;
39 |
40 | bb = zeros(size(tt,1),3) ;
41 | bb(:,1) = (ac(:,2) .* rv11 - ...
42 | ab(:,2) .* rv22 ) ...
43 | ./ dd * +.5 ;
44 |
45 | bb(:,2) = (ab(:,1) .* rv22 - ...
46 | ac(:,1) .* rv11 ) ...
47 | ./ dd * +.5 ;
48 |
49 | bb(:,3) = sum(bb(:,1:2).^2,2) ;
50 | bb(:,1:2) = ...
51 | pp(tt(:,1),:) + bb(:,1:2) ;
52 |
53 | end
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/mesh2d/tricon2.m:
--------------------------------------------------------------------------------
1 | function [ee,tt] = tricon2(varargin)
2 | %TRICON2 edge-centred connectivity for a conforming 2-simpl-
3 | %ex triangulation in the two-dimensional plane.
4 | % [EE,TT] = TRICON2(TT,CC) returns the edge-based adjacen-
5 | % cy for a mesh of 2-simlexes (triangles). EE = [V1,V2,T1,
6 | % T2,CE] is the set of unique 1-simplexes (edges) in the
7 | % mesh TT. Each row of {V1,V2} defines an edge, each row
8 | % of {T1,T2} defines the two triangles adjacent to an edge
9 | % and CE is a "constraint" flag, indicating which row in
10 | % CC (if any) the edge matches. TT = [V1,V2,V3,E1,E2,E3],
11 | % is the set of unique 2-simplexes in the mesh, where
12 | % {E1,E2,E3} define the tria-to-edge mapping. Each row of
13 | % {E1,E2,E3} are the indicies of the three edges that make
14 | % up each triangle.
15 |
16 | % Darren Engwirda : 2014 --
17 | % Email : de2363@columbia.edu
18 | % Last updated : 01/10/2017
19 |
20 | %---------------------------------------------- extract args
21 | tt = []; cc = [];
22 |
23 | if (nargin>=1), tt = varargin{1}; end
24 | if (nargin>=2), cc = varargin{2}; end
25 |
26 | %---------------------------------------------- basic checks
27 | if (~isnumeric(tt))
28 | error('tricon2:incorrectInputClass' , ...
29 | 'Incorrect input class.') ;
30 | end
31 | if (~isnumeric(cc))
32 | error('tricon2:incorrectInputClass' , ...
33 | 'Incorrect input class.') ;
34 | end
35 |
36 | %---------------------------------------------- basic checks
37 | if (ndims(tt) ~= +2 || size(tt,2) ~= +3)
38 | error('tricon2:incorrectDimensions' , ...
39 | 'Incorrect input dimensions.');
40 | end
41 | if (min(tt(:)) < +1 )
42 | error('tricon2:invalidInputs', ...
43 | 'Invalid TRIA input array.') ;
44 | end
45 |
46 | if (~isempty(cc))
47 | if (ndims(cc) ~= +2 || size(cc,2) ~= +2)
48 | error('tricon2:incorrectDimensions' , ...
49 | 'Incorrect input dimensions.');
50 | end
51 | end
52 |
53 | isoctave = ...
54 | exist('OCTAVE_VERSION','builtin') > +0;
55 |
56 | nt = size(tt,1);
57 | nc = size(cc,1);
58 |
59 | %------------------------------ assemble non-unique edge set
60 | ee = zeros(nt*3,2);
61 | ee((1:nt)+nt*0,:) = tt(:,[1,2]);
62 | ee((1:nt)+nt*1,:) = tt(:,[2,3]);
63 | ee((1:nt)+nt*2,:) = tt(:,[3,1]);
64 |
65 | %------------------------------ unique edges and re-indexing
66 | %[ee, iv, jv] = ...
67 | % unique(sort(ee, 2), 'rows');
68 |
69 | %-- as a (much) faster alternative to the 'ROWS' based call
70 | %-- to UNIQUE above, the edge list (i.e. pairs of UINT32 va-
71 | %-- lues) can be cast to DOUBLE, and the sorted comparisons
72 | %-- performed on vector inputs!
73 | ee = sort(ee,2);
74 | [ed,iv,jv] = unique(ee*[2^31;1]);
75 | ee = ee (iv,:);
76 |
77 | %------------------- tria-to-edge indexing: 3 edges per tria
78 | tt = [tt, zeros(nt*1,3)] ;
79 | tt(:,4) = jv((1:nt)+nt*0);
80 | tt(:,5) = jv((1:nt)+nt*1);
81 | tt(:,6) = jv((1:nt)+nt*2);
82 |
83 | %------------------- edge-to-tria indexing: 2 trias per edge
84 |
85 | if (isoctave)
86 |
87 | %-- OCTAVE is *shockingly* bad at executing loops, so --
88 | %-- even though it involves far more operations! -- call
89 | %-- the vectorised version below.
90 |
91 | ne = size(ee,1);
92 | ee = [ee, zeros(ne*1,3)] ;
93 |
94 | ei = [tt(:,4);tt(:,5);tt(:,6)] ;
95 | ti = [(+1:nt),(+1:nt),(+1:nt)]';
96 |
97 | [ei,ix] = sort(ei,'ascend') ;
98 | ti = ti (ix,:);
99 |
100 | ix = find(diff(ei)>=+1);
101 |
102 | ni = length(ti);
103 |
104 | ep = [+1; ix+1];
105 | ep = [ep; ni+1];
106 |
107 | in = ep(2:ne+1)-ep(1:ne+0) > 1 ;
108 |
109 | ee( :,3) = ti(ep(1:ne)+0);
110 | ee(in,4) = ti(ep( in)+1);
111 |
112 | else
113 |
114 | %-- MATLAB is actually pretty good at JIT-ing code these
115 | %-- days, so use the asymptotically faster version based
116 | %-- on the pre-computed ordering.
117 |
118 | ne = size(ee,1);
119 | ee = [ee, zeros(ne*1,3)] ;
120 | ep = +3 * ones (ne*1,1) ;
121 | for ti = +1 : nt
122 | ei = tt(ti,4) ;
123 | ee(ei,ep(ei)) = ti;
124 | ej = tt(ti,5) ;
125 | ee(ej,ep(ej)) = ti;
126 | ek = tt(ti,6) ;
127 | ee(ek,ep(ek)) = ti;
128 |
129 | ep(ei) = ep(ei)+1 ;
130 | ep(ej) = ep(ej)+1 ;
131 | ep(ek) = ep(ek)+1 ;
132 | end
133 |
134 | end
135 |
136 | if (isempty(cc)), return; end
137 |
138 | %------------------------------------ find constrained edges
139 | %[ip,ip] = ismember( ...
140 | % ee(:,1:2),sort(cc,2),'rows');
141 |
142 | %-- as above, the 'ROWS' based call to ISMEMBER can be sped
143 | %-- up by casting the edge lists (i.e. pairs of UINT32 valu-
144 | %-- es) to DOUBLE, and performing the sorted queries on vec-
145 | %-- tor inputs!
146 | cc = sort(cc,2);
147 | [ip,ip] = ismember(ed, cc*[2^31;+1]);
148 |
149 | %------------------------------------ mark constrained edges
150 | ee(:,5) = ip;
151 |
152 | end
153 |
154 |
155 |
156 |
--------------------------------------------------------------------------------
/mesh2d/trideg2.m:
--------------------------------------------------------------------------------
1 | function [vdeg] = trideg2(pp,tt)
2 | %TRIDEG2 calc. topological degree for vertices in a 2-simpl-
3 | %ex triangulation.
4 | % [VDEG] = TRIDEG2(VERT,TRIA) returns the no. of triangles
5 | % incident to each vertex. VDEG is a V-by-1 array of vert-
6 | % ex degrees, VERT is a V-by-2 array of XY coordinates,
7 | % and TRIA is a T-by-3 array of vertex indexing, where
8 | % each row defines a triangle,
9 | % such that VERT(TRIA(II,1),:), VERT(TRIA(II,2),:) and
10 | % VERT(TRIA(II,3),:) are the coordinates of the II-TH tri-
11 | % angle.
12 | %
13 | % See also TRISCR2, TRIAREA, TRIANG2, TRIBAL2
14 |
15 | %-----------------------------------------------------------
16 | % Darren Engwirda : 2017 --
17 | % Email : de2363@columbia.edu
18 | % Last updated : 09/06/2017
19 | %-----------------------------------------------------------
20 |
21 | %---------------------------------------------- basic checks
22 | if (~isnumeric(pp) || ~isnumeric(tt) )
23 | error('trideg2:incorrectInputClass' , ...
24 | 'Incorrect input class.') ;
25 | end
26 |
27 | %---------------------------------------------- basic checks
28 | if (ndims(pp) ~= +2 || ndims(tt) ~= +2 )
29 | error('trideg2:incorrectDimensions' , ...
30 | 'Incorrect input dimensions.');
31 | end
32 | if (size(pp,2)~= +2 || size(tt,2) < +3 )
33 | error('trideg2:incorrectDimensions' , ...
34 | 'Incorrect input dimensions.');
35 | end
36 |
37 | nvrt = size(pp,1) ;
38 | ntri = size(tt,1) ;
39 |
40 | %---------------------------------------------- basic checks
41 | if (min(min(tt(:,1:3))) < +1 || ...
42 | max(max(tt(:,1:3))) > nvrt )
43 | error('trideg2:invalidInputs', ...
44 | 'Invalid TRIA input array.') ;
45 | end
46 |
47 | %------------------------------------- compute vertex degree
48 | tvec = (+1 : ntri)';
49 |
50 | sdeg = sparse(tt(:,1:3),[tvec,tvec,tvec],+1,nvrt,ntri) ;
51 |
52 | vdeg = ...
53 | sdeg * ones(ntri,1);
54 |
55 | end
56 |
57 |
58 |
--------------------------------------------------------------------------------
/mesh2d/tridiv2.m:
--------------------------------------------------------------------------------
1 | function [vert,conn,tria,tnum] = tridiv2(varargin)
2 | %TRIDIV2 "quadtree" refinement for 2-simplex triangulations.
3 | % [VERT,EDGE,TRIA,TNUM] = TRIDIV2(VERT,EDGE,TRIA,TNUM) re-
4 | % turns a globally refined triangulation, in which all ed-
5 | % ges are bisected about their midpoints. Such refinement
6 | % splits each triangle into four new sub-triangles accord-
7 | % ing to a shape-preserving scheme.
8 | %
9 | % VERT is a V-by-2 array of XY coordinates in the triangu-
10 | % lation, EDGE is an array of constrained edges, TRIA is a
11 | % T-by-3 array of triangles, and TNUM is a T-by-1 array of
12 | % part indices. Each row of TRIA and EDGE define an eleme-
13 | % nt. VERT(TRIA(II,1),:), VERT(TRIA(II,2),:) and VERT(TRIA
14 | % (II,3),:) are the coordinates of the II-TH triangle. The
15 | % edges in EDGE are defined in a similar manner. NUM is an
16 | % array of part indexing, such that TNUM(II) is the index
17 | % of the part in which the II-TH triangle resides.
18 | %
19 | % [VERT,EDGE,TRIA,TNUM] = TRIDIV2(... ,TDIV) returns a se-
20 | % lectively refined mesh, where TDIV is a T-by-1 logical
21 | % array, with TDIV(KK) = TRUE if TRIA(KK,:) is to be refi-
22 | % ned. Such triangles are refined using the four-way split
23 | % described above. Additionally, a "halo" of adjacent tri-
24 | % angles are also refined to preseve compatibility of the
25 | % mesh. Such triangles are refined using a two-way bisect-
26 | % ion type scheme.
27 |
28 | % See also REFINE2, SMOOTH2
29 |
30 | % Darren Engwirda : 2017 --
31 | % Email : de2363@columbia.edu
32 | % Last updated : 29/01/2017
33 |
34 | %---------------------------------------------- extract args
35 | vert = []; conn = []; tria = []; tnum = [] ;
36 | tdiv = [];
37 |
38 | if (nargin>=+1), vert = varargin{1}; end
39 | if (nargin>=+2), conn = varargin{2}; end
40 | if (nargin>=+3), tria = varargin{3}; end
41 | if (nargin>=+4), tnum = varargin{4}; end
42 | if (nargin>=+5), tdiv = varargin{5}; end
43 |
44 | %---------------------------------------------- default arg.
45 | if (isempty(tnum))
46 | tnum = ones(size(tria,1),1) ;
47 | end
48 | if (isempty(tdiv))
49 | tdiv = true(size(tria,1),1) ;
50 | end
51 |
52 | %---------------------------------------------- basic checks
53 | if ( ~isnumeric(vert) || ...
54 | ~isnumeric(conn) || ...
55 | ~isnumeric(tria) || ...
56 | ~isnumeric(tnum) || ...
57 | ~islogical(tdiv) )
58 | error('tridiv2:incorrectInputClass' , ...
59 | 'Incorrect input class.') ;
60 | end
61 |
62 | %---------------------------------------------- basic checks
63 | tnum = tnum(:) ; tdiv = tdiv(:) ;
64 | if (ndims(vert) ~= +2 || ...
65 | ndims(conn) ~= +2 || ...
66 | ndims(tria) ~= +2 || ...
67 | ndims(tnum) ~= +2 || ...
68 | ndims(tdiv) ~= +2 )
69 | error('tridiv2:incorrectDimensions' , ...
70 | 'Incorrect input dimensions.');
71 | end
72 | if (size(vert,2)~= +2 || ...
73 | size(conn,2)~= +2 || ...
74 | size(tria,2)~= +3 || ...
75 | size(tnum,2)~= +1 || ...
76 | size(tria,1)~= size(tnum,1) )
77 | error('tridiv2:incorrectDimensions' , ...
78 | 'Incorrect input dimensions.');
79 | end
80 |
81 | nvrt = size(vert,1) ;
82 |
83 | %---------------------------------------------- basic checks
84 | if (min(min(conn(:,1:2))) < +1 || ...
85 | max(max(conn(:,1:2))) > nvrt )
86 | error('tridiv2:invalidInputs', ...
87 | 'Invalid EDGE input array.') ;
88 | end
89 |
90 | if (min(min(tria(:,1:3))) < +1 || ...
91 | max(max(tria(:,1:3))) > nvrt )
92 | error('tridiv2:invalidInputs', ...
93 | 'Invalid TRIA input array.') ;
94 | end
95 |
96 | %------------------------------ assemble extended adj. info.
97 | [edge,tria] = tricon2(tria,conn) ;
98 |
99 | ediv = false(size(edge,1),1) ;
100 | ediv(tria(tdiv,4:6)) = true ;
101 |
102 | snum = length(find(ediv));
103 |
104 | while (true)
105 |
106 | %-------------------------- tria's with >= 2 edge splits
107 | div3 = sum(double( ...
108 | ediv(tria(:,4:6))),2)>=2;
109 |
110 | %-------------------------- expand onto adj. edge splits
111 | ediv(tria(div3,4:6)) = true ;
112 |
113 | snew = length(find(ediv)) ;
114 |
115 | if (snew == snum), break; end
116 |
117 | snum = snew ;
118 |
119 | end
120 |
121 | %------------------------------ tria's with == 1 edge splits
122 | div1 = sum( ...
123 | double( ediv(tria(:,4:6))),2)==1;
124 |
125 | %------------------------------ indexing for mid-point vert.
126 | ivec = zeros(size(edge,1),1);
127 | ivec(ediv) = ...
128 | (1:snum)' + size(vert,1);
129 |
130 | %------------------------------------------ update vert. set
131 | emid = vert(edge(ediv,1),:) ...
132 | + vert(edge(ediv,2),:) ;
133 | vert =[vert ; emid * 0.5] ;
134 |
135 | %------------------------------------------ update edge. set
136 | [cvec,eloc] = ...
137 | setset2(conn,edge(ediv, 1:2)) ;
138 |
139 | epos = ivec(ediv);
140 | epos = epos(eloc(eloc>0)) ;
141 |
142 | conn = [conn(~cvec,1:2) ; ...
143 | conn(cvec,1) , epos ; ...
144 | conn(cvec,2) , epos ] ;
145 |
146 | %------------------------------------ push 1-to-4 refinement
147 | tr41 = ones(length(find(div3)),3) ;
148 | tn41 = ones(length(find(div3)),1) ;
149 | tn41(:,1) = tnum(div3,1);
150 | tr41(:,1) = tria(div3,1);
151 | tr41(:,2) = ivec(tria(div3,4));
152 | tr41(:,3) = ivec(tria(div3,6));
153 |
154 | tr42 = ones(length(find(div3)),3) ;
155 | tn42 = ones(length(find(div3)),1) ;
156 | tn42(:,1) = tnum(div3,1);
157 | tr42(:,1) = ivec(tria(div3,4));
158 | tr42(:,2) = tria(div3,2);
159 | tr42(:,3) = ivec(tria(div3,5));
160 |
161 | tr43 = ones(length(find(div3)),3) ;
162 | tn43 = ones(length(find(div3)),1) ;
163 | tn43(:,1) = tnum(div3,1);
164 | tr43(:,1) = ivec(tria(div3,6));
165 | tr43(:,2) = ivec(tria(div3,5));
166 | tr43(:,3) = tria(div3,3);
167 |
168 | tr44 = ones(length(find(div3)),3) ;
169 | tn44 = ones(length(find(div3)),1) ;
170 | tn44(:,1) = tnum(div3,1);
171 | tr44(:,1) = ivec(tria(div3,6));
172 | tr44(:,2) = ivec(tria(div3,4));
173 | tr44(:,3) = ivec(tria(div3,5));
174 |
175 | %----------------------- push 1-to-2 refinement about edge 1
176 | tvec = false(size(tria,1), 1) ;
177 | tvec(ediv(tria(:,4))&div1) = true ;
178 |
179 | tr21 = ones(length(find(tvec)),3) ;
180 | tn21 = ones(length(find(tvec)),1) ;
181 | tn21(:,1) = tnum(tvec,1);
182 | tr21(:,1) = ivec(tria(tvec,4));
183 | tr21(:,2) = tria(tvec,3);
184 | tr21(:,3) = tria(tvec,1);
185 |
186 | tr22 = ones(length(find(tvec)),3) ;
187 | tn22 = ones(length(find(tvec)),1) ;
188 | tn22(:,1) = tnum(tvec,1);
189 | tr22(:,1) = ivec(tria(tvec,4));
190 | tr22(:,2) = tria(tvec,2);
191 | tr22(:,3) = tria(tvec,3);
192 |
193 | %----------------------- push 1-to-2 refinement about edge 2
194 | tvec = false(size(tria,1), 1) ;
195 | tvec(ediv(tria(:,5))&div1) = true ;
196 |
197 | tr23 = ones(length(find(tvec)),3) ;
198 | tn23 = ones(length(find(tvec)),1) ;
199 | tn23(:,1) = tnum(tvec,1);
200 | tr23(:,1) = ivec(tria(tvec,5));
201 | tr23(:,2) = tria(tvec,1);
202 | tr23(:,3) = tria(tvec,2);
203 |
204 | tr24 = ones(length(find(tvec)),3) ;
205 | tn24 = ones(length(find(tvec)),1) ;
206 | tn24(:,1) = tnum(tvec,1);
207 | tr24(:,1) = ivec(tria(tvec,5));
208 | tr24(:,2) = tria(tvec,3);
209 | tr24(:,3) = tria(tvec,1);
210 |
211 | %----------------------- push 1-to-2 refinement about edge 3
212 | tvec = false(size(tria,1), 1) ;
213 | tvec(ediv(tria(:,6))&div1) = true ;
214 |
215 | tr25 = ones(length(find(tvec)),3) ;
216 | tn25 = ones(length(find(tvec)),1) ;
217 | tn25(:,1) = tnum(tvec,1);
218 | tr25(:,1) = ivec(tria(tvec,6));
219 | tr25(:,2) = tria(tvec,2);
220 | tr25(:,3) = tria(tvec,3);
221 |
222 | tr26 = ones(length(find(tvec)),3) ;
223 | tn26 = ones(length(find(tvec)),1) ;
224 | tn26(:,1) = tnum(tvec,1);
225 | tr26(:,1) = ivec(tria(tvec,6));
226 | tr26(:,2) = tria(tvec,1);
227 | tr26(:,3) = tria(tvec,2);
228 |
229 | %------------------------------------------ update tria. set
230 | tria = [tria(~div1&~div3,1:3) ; ...
231 | tr41 ; tr42 ; tr43 ; tr44 ; ...
232 | tr21 ; tr22 ;
233 | tr23 ; tr24 ;
234 | tr25 ; tr26 ] ;
235 |
236 | tnum = [tnum(~div1&~div3,1:1) ; ...
237 | tn41 ; tn42 ; tn43 ; tn44 ; ...
238 | tn21 ; tn22 ;
239 | tn23 ; tn24 ;
240 | tn25 ; tn26 ] ;
241 |
242 | end
243 |
244 |
245 |
246 |
--------------------------------------------------------------------------------
/mesh2d/trihfn2.m:
--------------------------------------------------------------------------------
1 | function [hval] = trihfn2(test,vert,tria,tree,hfun)
2 | %TRIHFN2 evaluate a discrete mesh-size function defined on a
3 | %2-simplex triangulation embedded in R^2.
4 | % [HVAL] = TRIHFN2(TEST,VERT,TRIA,TREE,HFUN) returns an
5 | % interpolation of the discrete mesh-size function HFUN to
6 | % the queries points TEST. HVAL is a Q-by-1 array of func-
7 | % tion values associated with the Q-by-2 array of XY coor-
8 | % dinates TEST. {VERT,TRIA,HFUN} is a discrete mesh-size
9 | % function, where VERT is a V-by-2 array of XY coordinates
10 | % TRIA is a T-by-3 array of triangles and HFUN is a V-by-1
11 | % array of mesh-size values. Each row of TRIA defines a
12 | % triangle, such that VERT(TRIA(II,1),:),
13 | % VERT(TRIA(II,2),:) and VERT(TRIA(II,3),:) are the coord-
14 | % inates of the II-TH triangle. TREE is a spatial indexing
15 | % structure for {VERT,TRIA}, as returned by IDXTRI2.
16 | %
17 | % See also LFSHFN2, LIMHFN2, IDXTRI2
18 |
19 | % Darren Engwirda : 2017 --
20 | % Email : de2363@columbia.edu
21 | % Last updated : 07/04/2017
22 |
23 | %---------------------------------------------- basic checks
24 | if ( ~isnumeric(test) || ...
25 | ~isnumeric(vert) || ...
26 | ~isnumeric(tria) || ...
27 | ~isnumeric(hfun) || ...
28 | ~isstruct (tree) )
29 | error('trihfn2:incorrectInputClass' , ...
30 | 'Incorrect input class.') ;
31 | end
32 |
33 | %---------------------------------------------- basic checks
34 | if (ndims(test) ~= +2 || ...
35 | ndims(vert) ~= +2 || ...
36 | ndims(tria) ~= +2 || ...
37 | ndims(hfun) ~= +2 )
38 | error('trihfn2:incorrectDimensions' , ...
39 | 'Incorrect input dimensions.');
40 | end
41 | if (size(test,2)~= +2 || ...
42 | size(vert,2)~= +2 || ...
43 | size(tria,2) < +3 || ...
44 | size(hfun,2)~= +1 || ...
45 | size(vert,1)~= size(hfun,1) )
46 | error('trihfn2:incorrectDimensions' , ...
47 | 'Incorrect input dimensions.');
48 | end
49 |
50 | nvrt = size(vert,1) ;
51 |
52 | %---------------------------------------------- basic checks
53 | if (min(min(tria(:,1:3))) < +1 || ...
54 | max(max(tria(:,1:3))) > nvrt )
55 | error('trihfn2:invalidInputs', ...
56 | 'Invalid TRIA input array.') ;
57 | end
58 |
59 | %-------------------------------------- test-to-tria queries
60 | [tp,tj] = ...
61 | findtria (vert,tria,test,tree);
62 |
63 | if (isempty(tp))
64 | in = false(size(test,1),1);
65 | ti = [];
66 | else
67 | in = tp(:,1) > +0 ;
68 | ti = tj(tp(in,+1));
69 | end
70 |
71 | %-------------------------------------- calc. linear interp.
72 | hval = max(hfun) * ones(size(test,1),1) ;
73 |
74 | if (any(in))
75 |
76 | d1 = test(in,:) - vert(tria(ti,1),:);
77 | d2 = test(in,:) - vert(tria(ti,2),:);
78 | d3 = test(in,:) - vert(tria(ti,3),:);
79 |
80 | a3 = abs(d1(:,1) .* d2(:,2) ...
81 | - d1(:,2) .* d2(:,1) ) ;
82 | a2 = abs(d1(:,1) .* d3(:,2) ...
83 | - d1(:,2) .* d3(:,1) ) ;
84 | a1 = abs(d3(:,1) .* d2(:,2) ...
85 | - d3(:,2) .* d2(:,1) ) ;
86 |
87 | hval(in) = a1.*hfun(tria(ti,1)) ...
88 | + a2.*hfun(tria(ti,2)) ...
89 | + a3.*hfun(tria(ti,3)) ;
90 |
91 | hval(in) = hval(in)./(a1+a2+a3) ;
92 |
93 | end
94 |
95 | end
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/mesh2d/triread.m:
--------------------------------------------------------------------------------
1 | function [vert,edge,tria,tnum] = triread(name)
2 | %TRIREAD read two-dimensional triangulation data from file.
3 | % [VERT,EDGE,TRIA,TNUM] = TRIREAD(NAME) returns the 2-sim-
4 | % plex triangulation {VERT,TRIA} contained in the mesh-fi-
5 | % le NAME.
6 | %
7 | % VERT is a V-by-2 array of XY coordinates in the triangu-
8 | % lation, EDGE is an array of constrained edges, TRIA is a
9 | % T-by-3 array of triangles, and TNUM is a T-by-1 array of
10 | % part indices. Each row of TRIA and EDGE define an eleme-
11 | % nt. VERT(TRIA(II,1),:), VERT(TRIA(II,2),:) and VERT(TRIA
12 | % (II,3),:) are the coordinates of the II-TH triangle. The
13 | % edges in EDGE are defined in a similar manner. NUM is an
14 | % array of part indexing, such that TNUM(II) is the index
15 | % of the part in which the II-TH triangle resides.
16 | %
17 | % Data is returned non-empty if it is present in the file.
18 | %
19 | % See also TRISAVE
20 |
21 | % This routine borrows functionality from the JIGSAW pack-
22 | % age: github.com/dengwirda/jigsaw-matlab.
23 |
24 | % Darren Engwirda : 2017 --
25 | % Email : de2363@columbia.edu
26 | % Last updated : 01/10/2017
27 |
28 | filename = mfilename('fullpath');
29 | filepath = fileparts( filename );
30 |
31 | addpath([filepath,'/mesh-file']);
32 |
33 | vert = []; edge = []; tria = []; tnum = [];
34 |
35 | %---------------------------------------------- basic checks
36 | if (~ischar(name))
37 | error('triread:incorrectInputClass' , ...
38 | 'Incorrect input class.') ;
39 | end
40 |
41 | %----------------------------------- borrow JIGSAW I/O func!
42 | [mesh] = loadmsh(name) ;
43 |
44 | %----------------------------------- extract data if present
45 | if (isfield(mesh,'point') && ...
46 | isfield(mesh.point,'coord'))
47 | vert = mesh.point.coord(:,1:2) ;
48 | end
49 | if (isfield(mesh,'edge2') && ...
50 | isfield(mesh.edge2,'index'))
51 | edge = mesh.edge2.index(:,1:2) ;
52 | end
53 | if (isfield(mesh,'tria3') && ...
54 | isfield(mesh.tria3,'index'))
55 | tria = mesh.tria3.index(:,1:3) ;
56 | tnum = mesh.tria3.index(:, 4) ;
57 | end
58 |
59 | end
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/mesh2d/triscr2.m:
--------------------------------------------------------------------------------
1 | function [tscr] = triscr2(pp,tt)
2 | %TRISCR2 calc. area-len. ratios for triangles in a 2-simplex
3 | %triangulation in the two-dimensional plane.
4 | % [SCR2] = TRISCR2(VERT,TRIA) returns the area-len. ratios
5 | % where SCR2 is a T-by-1 vector, VERT is a V-by-2 array of
6 | % XY coordinates, and TRIA is a T-by-3 array of
7 | % vertex indexing, where each row defines a triangle, such
8 | % that VERT(TRIA(II,1),:), VERT(TRIA(II,2),:) and VERT(
9 | % TRIA(II,3),:) are the coordinates of the II-TH triangle.
10 | %
11 | % See also TRIAREA, TRIANG2, TRIBAL2
12 |
13 | % Darren Engwirda : 2017 --
14 | % Email : de2363@columbia.edu
15 | % Last updated : 17/01/2017
16 |
17 | %--------------------------- compute signed area-len. ratios
18 | scal = 4.0 * sqrt(3.0) / 3.0;
19 |
20 | area = triarea(pp,tt) ; % also error checks!
21 |
22 | lrms = sum((pp(tt(:,2),:) ...
23 | - pp(tt(:,1),:)).^2,2) ...
24 | + sum((pp(tt(:,3),:) ...
25 | - pp(tt(:,2),:)).^2,2) ...
26 | + sum((pp(tt(:,3),:) ...
27 | - pp(tt(:,1),:)).^2,2) ;
28 |
29 | lrms =(lrms / 3.0) .^ 1.00;
30 |
31 | tscr = scal * area ./ lrms;
32 |
33 | end
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/GenerateBasicData.m:
--------------------------------------------------------------------------------
1 | clc;
2 | clear;
3 | addpath('./../mesh2d/')
4 | config;
5 | %% Rectangle
6 |
7 | [p,~,t,~]=refine2([0,0; 1,0; 1,1; 0,1;],[], [], [], scale);
8 |
9 | [C,NC,CMid,NCMid,NeighbourCells]=buildConnection(t);
10 |
11 | phi=fdNotchedCircle(p);
12 |
13 | save(['notchedCircle_' num2str(scale) '.mat'],'C','NC','CMid','NCMid','NeighbourCells','t','p','phi');
14 |
15 | %% 2 circles in a block
16 | phi=fd2Circles(p);
17 |
18 | save(['3Circles_' num2str(scale) '.mat'],'C','NC','CMid','NCMid','NeighbourCells','t','p','phi');
19 |
20 | %% U
21 |
22 | vertices=[0,0; 1,0; 1,1; 0.6,1; 0.6,0.6; 0.4,0.6; 0.4,1; 0,1];
23 | edges=[1, 2; 2, 3; 3, 4; 4, 5; 5, 6; 6, 7; 7, 8; 8, 1];
24 | [p,~,t,~]=refine2(vertices, edges, [], [], scale);
25 |
26 | [C,NC,CMid,NCMid,NeighbourCells]=buildConnection(t);
27 |
28 | phi=fdUpperBlock(p);
29 |
30 | save(['U_Region_' num2str(scale) '.mat'],'C','NC','CMid','NCMid','NeighbourCells','t','p','phi');
31 |
32 | %% Rate stick
33 |
34 | [p,~,t,~]=refine2([0,0; 1,0; 1,0.05; 0,0.05;],[], [], [], scaleStick);
35 |
36 | [C,NC,CMid,NCMid,NeighbourCells]=buildConnection(t);
37 | phi=drectangle(p, -1, 0, -1, 1);
38 |
39 | save(['RateStick_' num2str(scaleStick) '.mat'],'C','NC','CMid','NCMid','NeighbourCells','t','p','phi');
40 |
41 | %% Functions
42 |
43 | function d = fdRec( p )
44 | d=drectangle(p, -0, 1, -0, 1);
45 | end
46 |
47 | function d = fdU( p )
48 | dBlock=drectangle(p, -0, 1, -0, 5);
49 | dUR=fdUpperBlock(p);
50 | d=ddiff(dBlock, dUR);
51 | end
52 |
53 | function d = fdStick( p )
54 | d=drectangle(p, 0, 1, 0, 0.1);
55 | end
56 |
57 | function d = fdNotchedCircle( p )
58 | gRec1=drectangle(p, 0.43, 0.57, 0.65, 0.99);
59 | gCircle=dcircle(p, 0.5, 0.65, 0.25);
60 | d=ddiff(gCircle, gRec1);
61 | end
62 |
63 | function d = fdUpperBlock( p )
64 | d1=drectangle(p, 0.4, 0.6, 0.6, 1.1);
65 | d2=drectangle(p, 0, 1, 1, 1.1);
66 | d=dunion(d1,d2);
67 | end
68 |
69 | function d = fd2Circles( p )
70 | d1=dcircle(p, 0.3, 0.3, 0.2);
71 | d2=dcircle(p, 0.7, 0.7, 0.2);
72 | d=dunion(d1,d2);
73 | end
--------------------------------------------------------------------------------
/src/RateStickEvolve.m:
--------------------------------------------------------------------------------
1 | clc;
2 | clear;
3 | config;
4 | load(['RateStick_' num2str(scaleStick) '.mat']);
5 |
6 | %% load mesh
7 | NPoints=size(phi,1);
8 |
9 | %% initialization
10 | circleR=scaleStick/2;
11 | phi=dcircle(p, 0.0, 0.025, circleR);
12 |
13 | velocity=ones(size(p,1),1);
14 |
15 | %% auxiliary data
16 | disp('Building edges ...')
17 | ovData=buildOutgoingEdges(p,C,NC,CMid,NCMid);
18 | [A,edgeWeights]=buildMatrixA(ovData);
19 | disp('Building edges done.')
20 |
21 | %% evolve
22 | NEvolove=round(1/evolveStepStick);
23 | Result=cell(NEvolove,1);
24 | EmResult=zeros(NEvolove,1);
25 | EaResult=zeros(NEvolove,1);
26 |
27 | isoLines=[-0.2:0.02:-0.04, -0.02:0.005:0.02, 0.04:0.02:0.4];
28 |
29 | for e=1:NEvolove
30 | disp(['Step ' num2str(e) ' ' datestr(now,13)]);
31 | Result{e,1}=phi;
32 |
33 | GEvolve=calcGradient(p, phi, ovData, A, velocity, edgeWeights);
34 | GEvolveModule=sqrt(GEvolve(:,1).^2 + GEvolve(:,2).^2);
35 | deltaAmountEvolve=GEvolveModule .* velocity * evolveStepStick;
36 | intermediaPhi=phi-0.5*deltaAmountEvolve;
37 |
38 | GEvolve=calcGradient(p, intermediaPhi, ovData, A, velocity, edgeWeights);
39 | GEvolveModule=sqrt(GEvolve(:,1).^2 + GEvolve(:,2).^2);
40 | deltaAmountEvolve=GEvolveModule .* velocity * evolveStepStick;
41 | phi=phi-deltaAmountEvolve;
42 |
43 | %% error & record
44 | circleR=circleR + evolveStepStick;
45 | %% analytical data
46 | phiAnalytical=dcircle(p, 0.0, 0.025, circleR);
47 |
48 | ErrorTags=abs(phiAnalytical)<5*scaleStick;
49 | Error=abs(phi(ErrorTags)-phiAnalytical(ErrorTags));
50 | EmResult(e)=max(Error);
51 | EaResult(e)=mean(Error);
52 | disp(['Error: max->' num2str(EmResult(e)) ' average->' num2str(EaResult(e))]);
53 |
54 | NR=10;
55 | for r=1:NR
56 | disp(['Reinitial ' num2str(r) ' ' datestr(now,13)]);
57 |
58 | GReinitial=calcGradient(p, phi, ovData, A, phi, edgeWeights);
59 | S=phi./sqrt(phi.^2+(scaleStick*1)^2);
60 | deltaAmountReinitial=S .* (sqrt(GReinitial(:,1).^2+GReinitial(:,2).^2)-1) * reinitialStepStick;
61 | intermediaPhi=phi-0.5*deltaAmountReinitial;
62 |
63 | GReinitial=calcGradient(p, intermediaPhi, ovData, A, phi, edgeWeights);
64 | S=phi./sqrt(intermediaPhi.^2+(scaleStick*1)^2);
65 | deltaAmountReinitial=S .* (sqrt(GReinitial(:,1).^2+GReinitial(:,2).^2)-1) * reinitialStepStick;
66 | phi=phi-deltaAmountReinitial;
67 | end
68 | end
69 |
70 | %% save
71 | save(['resultData/RateStick_' num2str(scaleStick) '.mat'], 'Result', 'EmResult', 'EaResult', 'p', 't');
--------------------------------------------------------------------------------
/src/Reinitialize2D.m:
--------------------------------------------------------------------------------
1 | clc;
2 | clear;
3 | config;
4 |
5 | %% load mesh
6 | load(['3Circles_' num2str(scale) '.mat']);
7 | NPoints=size(phi,1);
8 |
9 | %% initial phi
10 | x1=0.25;
11 | x2=0.75;
12 | y1=0.25;
13 | y2=0.75;
14 |
15 | phi=-min(min(min(-y1+p(:,2), y2-p(:,2)),-x1+p(:,1)),x2-p(:,1));
16 | phi=phi.*((p(:,1)-0.3).^2 + (p(:,2)-0.2).^2 + 0.1);
17 |
18 | %% auxiliary data
19 | disp('Building edges ...')
20 | ovData=buildOutgoingEdges(p,C,NC,CMid,NCMid);
21 | [A,edgeWeights]=buildMatrixA(ovData);
22 | disp('Building edges done.')
23 |
24 | %% evolve
25 | NEvolove=80;
26 | NWriteInterval=2;
27 | Result=cell(round(NEvolove/NWriteInterval)+1,1);
28 |
29 | isoLines=-0.5:1e-2:0.5;
30 |
31 | for e=1:NEvolove
32 | disp(['Step ' num2str(e) ' ' datestr(now,13)]);
33 |
34 | if mod(e,NWriteInterval)==1
35 | Result{1+(e-1)/NWriteInterval,1}=phi;
36 | end
37 |
38 | GReinitial=calcGradient(p, phi, ovData, A, phi, edgeWeights);
39 | S=phi./sqrt(phi.^2+(scale*1)^2);
40 | deltaAmountReinitial=S .* (sqrt(GReinitial(:,1).^2+GReinitial(:,2).^2)-1) * reinitialStep;
41 | intermediaPhi=phi-0.5*deltaAmountReinitial;
42 |
43 | GReinitial=calcGradient(p, intermediaPhi, ovData, A, phi, edgeWeights);
44 | S=phi./sqrt(intermediaPhi.^2+(scale*1)^2);
45 | deltaAmountReinitial=S .* (sqrt(GReinitial(:,1).^2+GReinitial(:,2).^2)-1) * reinitialStep;
46 | phi=phi-deltaAmountReinitial;
47 |
48 | end
49 |
50 | %% draw
51 | clf;
52 | tricontour(p,t,phi,isoLines);
53 | xlim([0,1]);
54 | ylim([0,1]);
55 | drawnow;
--------------------------------------------------------------------------------
/src/ThreeCirclesEvolve.m:
--------------------------------------------------------------------------------
1 | clc;
2 | clear;
3 | config;
4 |
5 | %% load mesh
6 | load(['3Circles_' num2str(scale) '.mat']);
7 | NPoints=size(phi,1);
8 |
9 | %% initialization
10 | circleR=0.2;
11 | d1=dcircle(p, 0.3, 0.3, circleR);
12 | d2=dcircle(p, 0.7, 0.3, circleR);
13 | d3=dcircle(p, 0.5, 0.7, circleR);
14 | phi=dunion(dunion(d1,d2),d3);
15 |
16 | velocity=ones(size(p,1),1);
17 |
18 | %% auxiliary data
19 | disp('Building edges ...')
20 | ovData=buildOutgoingEdges(p,C,NC,CMid,NCMid);
21 | [A,edgeWeights]=buildMatrixA(ovData);
22 | disp('Building edges done.')
23 |
24 | %% evolve
25 | NEvolove=100;
26 | Result=cell(NEvolove,1);
27 | EmResult=zeros(NEvolove,1);
28 | EaResult=zeros(NEvolove,1);
29 |
30 | isoLines=[-0.2:0.02:-0.04, -0.02:0.01:0.02, 0.04:0.02:0.4];
31 |
32 | for e=1:NEvolove
33 | disp(['Step ' num2str(e) ' ' datestr(now,13)]);
34 | Result{e,1}=phi;
35 |
36 | GEvolve=calcGradient(p, phi, ovData, A, velocity, edgeWeights);
37 | GEvolveModule=sqrt(GEvolve(:,1).^2 + GEvolve(:,2).^2);
38 | deltaAmountEvolve=GEvolveModule .* velocity * evolveStep;
39 | intermediaPhi=phi-0.5*deltaAmountEvolve;
40 |
41 | GEvolve=calcGradient(p, intermediaPhi, ovData, A, velocity, edgeWeights);
42 | GEvolveModule=sqrt(GEvolve(:,1).^2 + GEvolve(:,2).^2);
43 | deltaAmountEvolve=GEvolveModule .* velocity * evolveStep;
44 | phi=phi-deltaAmountEvolve;
45 |
46 | %% error & record
47 | circleR=circleR+velocity * evolveStep;
48 | %% analytical data
49 | d1=dcircle(p, 0.3, 0.3, circleR);
50 | d2=dcircle(p, 0.7, 0.3, circleR);
51 | d3=dcircle(p, 0.5, 0.7, circleR);
52 | phiAnalytical=dunion(dunion(d1,d2),d3);
53 |
54 | ErrorTags=abs(phiAnalytical)<5*scale;
55 | Error=abs(phi(ErrorTags)-phiAnalytical(ErrorTags));
56 | EmResult(e)=max(Error);
57 | EaResult(e)=mean(Error);
58 | disp(['Error: max->' num2str(EmResult(e)) ' average->' num2str(EaResult(e))]);
59 | end
60 |
61 | %% save
62 | save(['resultData/3Circles_' num2str(scale) '.mat'], 'Result', 'EmResult', 'EaResult', 'p', 't');
--------------------------------------------------------------------------------
/src/UShapedAnalytical.m:
--------------------------------------------------------------------------------
1 | % this file contains necessary functions to calculate the U-shaped example
2 | function [phi]=UShapedAnalytical(e, p, arcStep)
3 | % main function
4 | % input: e: propagate distance of the slowest area
5 | % p: points
6 |
7 | %% coordinates of key vertices, left -> right
8 | Rsf=0.5;
9 | Rsfs2 = Rsf^2;
10 | taper = pi/2 + asin(Rsf);
11 | v1X = 0;
12 | v1Y = 1 - e;
13 |
14 | v3X = 0.15;
15 | v3Y = 1 - e/Rsf;
16 | [l23a, l23b, l23c] = pointK2ABC(v3X, v3Y, taper);
17 |
18 | % line l12: y = v1Y
19 | [v2X, v2Y] = lineCross(l23a, l23b, l23c, 0, 1, -v1Y);
20 |
21 | edges = [...
22 | v1X, v1Y;...
23 | v2X, v2Y;...
24 | v3X, v3Y;...
25 | ];
26 |
27 | % if v2X < 0, v1 disappears
28 | if v2X < 0
29 | edges(1, :) = [];
30 | edges(1, :) = [0, -l23c/l23b;];
31 | end
32 |
33 | if (0.4 - e/Rsf) < 0.15
34 | error('Analytical solution fails when (0.4 - e/Rsf) < 0.15');
35 | end
36 | v3aX = 0.4 - e/Rsf;
37 | v3aY = v3Y;
38 |
39 | v4X = v3aX;
40 | v4Y = 0.6;
41 |
42 | edges = [edges; v3aX, v3aY; v4X, v4Y];
43 |
44 | % there is a arc v4 ~ v5
45 |
46 | v6X = 0.6;
47 | v6Y = 0.6 - e/Rsfs2;
48 | [l56a, l56b, l56c] = pointK2ABC(v6X, v6Y, taper);
49 |
50 | v5aY = 0.6 - e/Rsf;
51 | v5aX = (-l56c - l56b*v5aY) / l56a;
52 |
53 | if v5aX > 0.4
54 | % horizon line v5 - v5a exists
55 | edges = [edges; arc2Poly(0.4, 0.6, e/Rsf, pi, 1.5*pi, arcStep);...
56 | v5aX, v5aY; v6X, v6Y];
57 | else
58 | % the line starts from v6 intersects with arc v4 ~ v5
59 | [v5X, v5Y] = lineCrossCircle(l56a, l56b, l56c, 0.4, 0.6, e/Rsf, v6X, v6Y);
60 | theta2 = atan2(v5X - 0.4, v5Y - 0.6);
61 | while theta2 < pi
62 | theta2 = theta2 + 2*pi;
63 | end
64 | edges = [edges; arc2Poly(0.4, 0.6, e/Rsf, pi, theta2, arcStep); v6X, v6Y];
65 | end
66 |
67 | % there is a arc v6 ~ v7
68 |
69 | v7X = 0.6 + e/Rsfs2;
70 | v7Y = 0.6;
71 |
72 | v9X = 1;
73 | v9Y = 1 - e/Rsfs2;
74 |
75 | v8X = v7X;
76 | v8Y = v9Y;
77 |
78 | if v8X > 1
79 | theta2 = 2*pi - acos(0.4/(e/Rsfs2));
80 | edges = [edges; arc2Poly(0.6, 0.6, e/Rsfs2, 1.5*pi, theta2, arcStep)];
81 | else
82 | edges = [edges; arc2Poly(0.6, 0.6, e/Rsfs2, 1.5*pi, 2*pi, arcStep)];
83 | edges = [edges; v7X, v7Y; v8X, v8Y; v9X, v9Y];
84 | end
85 |
86 | %% distance
87 | [~, phi, ~] = distance2curve(edges, p);
88 |
89 | %% sign
90 |
91 | if v2X > 0
92 | tag = p(:, 1) < v2X & p(:,2) > 1-e;
93 | phi(tag) = -phi(tag);
94 | tag = p(:, 1) >= v2X & p(:, 1) < v3X & isOnUpperOrRightOfLine(l23a, l23b, l23c, p(:,1), p(:,2));
95 | phi(tag) = -phi(tag);
96 | else
97 | tag = p(:, 1) < v3X & isOnUpperOrRightOfLine(l23a, l23b, l23c, p(:,1), p(:,2));
98 | phi(tag) = -phi(tag);
99 | end
100 |
101 | tag = p(:, 1) >= v3X & p(:, 1) < v3aX & p(:, 2) > 1-e/Rsf;
102 | phi(tag) = -phi(tag);
103 |
104 | if v5aX > 0.4
105 | % horizon line v5 - v5a exists
106 | tag = p(:, 1) >= v3aX & p(:, 1) < 0.4 & ...
107 | (p(:, 2) >= 0.6 | ...
108 | (p(:, 2) < 0.6 & sqrt( (p(:, 1) - 0.4).^2 + (p(:, 2) - 0.6).^2) < e/Rsf));
109 | tag = tag | (p(:, 1) >= 0.4 & p(:, 1) < v5aX & p(:, 2) > 0.6 - e/Rsf);
110 | tag = tag | (p(:, 1) >= v5aX & p(:, 1) < 0.6 & ...
111 | isOnUpperOrRightOfLine(l56a, l56b, l56c, p(:,1), p(:,2)));
112 |
113 | phi(tag) = -phi(tag);
114 | else
115 | % the line starts from v6 intersects with arc v4 ~ v5
116 | tag = p(:, 1) >= v3aX & p(:, 1) < v5X & ...
117 | (p(:, 2) >= 0.6 | ...
118 | (p(:, 2) < 0.6 & sqrt( (p(:, 1) - 0.4).^2 + (p(:, 2) - 0.6).^2) < e/Rsf));
119 | tag = tag | (p(:, 1) >= v5X & p(:, 1) < 0.6 & ...
120 | isOnUpperOrRightOfLine(l56a, l56b, l56c, p(:,1), p(:,2)));
121 |
122 | phi(tag) = -phi(tag);
123 | end
124 |
125 | if v8X > 1
126 | arcEndY = 0.6 - sqrt((e/Rsfs2)^2 - 0.16);
127 | tag = p(:, 1) >= 0.6 & (p(:, 2) >= arcEndY | ...
128 | (p(:, 2) < arcEndY & sqrt( (p(:, 1) - 0.6).^2 + (p(:, 2) - 0.6).^2) < e/Rsfs2));
129 | phi(tag) = -phi(tag);
130 | else
131 | tag = p(:, 1) >= 0.6 & p(:, 1) < v7X & ...
132 | (p(:, 2) >= 0.6 | ...
133 | (p(:, 2) < 0.6 & sqrt( (p(:, 1) - 0.6).^2 + (p(:, 2) - 0.6).^2) < e/Rsfs2));
134 | tag = tag | (p(:, 1) >= v7X & p(:, 2) > 1-e/Rsfs2);
135 |
136 | phi(tag) = -phi(tag);
137 | end
138 |
139 | end
140 |
141 | %% utility functions
142 | function [A, B, C] = pointK2ABC(x0, y0, theta)
143 | % converte line equation format
144 | % (x0, y0), theta ==>> Ax + By + C = 0
145 | %
146 | % y-y0=tan(theta)(x-x0)
147 | % y/tan(theta) - y0/tan(theta) = x - x0
148 |
149 | iPi_2=theta/(pi/2);
150 | iPi=theta/(pi);
151 | isPi_2= abs(iPi_2-round(iPi_2)) < eps && abs(iPi - round(iPi)) >= eps;
152 | isPi = abs(iPi - round(iPi)) (-C/A);
170 | else
171 | r = y > (-C - A*x)/B;
172 | end
173 | end
174 |
175 | function [rX, rY] = lineCross(A1, B1, C1, A2, B2, C2)
176 | r=[A1, B1; A2, B2]\[-C1;-C2];
177 | rX=r(1);
178 | rY=r(2);
179 | end
180 |
181 | function [rX, rY] = lineCrossCircle(A, B, C, cX, cY, R, xN, yN)
182 | % find the intersection of circle and line, then choose the nearer one to
183 | % [xN, yN] and return it
184 | %
185 | % Ax + By + C = 0
186 | % x^2 - 2 cX x + y^2 - 2 cY y + cX^2 + cY^2 = R^2
187 | % (x - cX)^2 + (y - cY)^2 = R^2
188 | if abs(A) t1 x + t2
209 | t1 = -A/B;
210 | t2 = -C/B - cY;
211 | % x^2 - 2 cX x + cX^2 + t1^2 x^2 + 2 t1 t2 x + t2^2 = R^2
212 | % (1+t1^2) x^2 + (2 t1 t2 - 2cX) x + cX^2 + t2^2 - R^2 = 0
213 | b = 2 * t1 * t2 - 2 * cX;
214 | a = 1+t1^2;
215 | c = cX^2 + t2^2 - R^2;
216 |
217 | dlt = sqrt(b^2 - 4 * a * c);
218 | if ~isreal(dlt)
219 | if abs(imag(dlt)) < eps
220 | dlt = real(dlt);
221 | else
222 | error('No real root!');
223 | end
224 | end
225 | rX1 = (-b + dlt) / (2*a);
226 | rY1 = - (C + A*rX1) / B;
227 | rX2 = (-b - dlt) / (2*a);
228 | rY2 = - (C + A*rX2) / B;
229 | end
230 |
231 | d1 = (rX1 - xN)^2 + (rY1 - yN)^2;
232 | d2 = (rX2 - xN)^2 + (rY2 - yN)^2;
233 |
234 | if d1 > d2
235 | rX = rX2;
236 | rY = rY2;
237 | else
238 | rX = rX1;
239 | rY = rY1;
240 | end
241 |
242 | end
243 |
244 | function [r] = arc2Poly(cX, cY, R, theta1, theta2, stepScale)
245 | % theta1, theta2 must be counter-clockwise
246 |
247 | step = stepScale / R; % (2*pi) / (2*R*pi/stepScale)
248 | thetas = (theta1:step:theta2)';
249 | if abs(thetas(end) - theta2) > eps
250 | thetas = [thetas; theta2];
251 | end
252 |
253 | r = repmat([cX, cY], numel(thetas), 1) + [cos(thetas), sin(thetas)] * R;
254 |
255 | end
--------------------------------------------------------------------------------
/src/UShapedEvolve.m:
--------------------------------------------------------------------------------
1 | clc;
2 | clear;
3 | config;
4 | load(['U_Region_' num2str(scale) '.mat']);
5 |
6 | NPoints=size(phi,1);
7 | Rsf=0.5;
8 |
9 | % set velocity
10 | velocity=ones(size(p,1),1);
11 | velocity(p(:,1)<0.6)=Rsf;
12 | velocity(p(:,1)<0.15)=Rsf*Rsf;
13 |
14 | disp('Building edges ...')
15 | ovData=buildOutgoingEdges(p,C,NC,CMid,NCMid);
16 | [A,edgeWeights]=buildMatrixA(ovData);
17 | disp('Building edges done.')
18 |
19 | disp('Building triangle basic data ...')
20 | [triangleOutNorms ,cornerNorm, areas]=generateTriangleNorms(p, t);
21 | disp('Building triangle basic data done.')
22 |
23 | NEvolove=round(0.124/(evolveStep*Rsf*Rsf));
24 | Result=cell(NEvolove,1);
25 | AnalyticalResult=cell(NEvolove,1);
26 | EmResult=zeros(NEvolove,1);
27 | EaResult=zeros(NEvolove,1);
28 |
29 | isoLines=[-0.2:0.02:-0.04, -0.02:0.01:0.02, 0.04:0.02:0.4];
30 |
31 | for r=1:30
32 | disp(['Reinitial ' num2str(r) ' ' datestr(now,13)]);
33 |
34 | GReinitial=calcGradient(p, phi, ovData, A, phi, edgeWeights);
35 | S=phi./sqrt(phi.^2+(scale*0.8)^2);
36 | deltaAmountReinitial=S .* (sqrt(GReinitial(:,1).^2+GReinitial(:,2).^2)-1) * reinitialStep;
37 | phi=phi-deltaAmountReinitial;
38 | end
39 |
40 | e = 0;
41 | for idx=1:NEvolove
42 | disp(['Step ' num2str(idx) ' ' datestr(now,13)]);
43 |
44 | %% Evolve
45 | GEvolve=calcGradient(p, phi, ovData, A, velocity, edgeWeights);
46 | GEvolveModule=sqrt(GEvolve(:,1).^2 + GEvolve(:,2).^2);
47 | deltaAmountEvolve=GEvolveModule .* velocity * evolveStep;
48 | intermediaPhi=phi-0.5*deltaAmountEvolve;
49 |
50 | GEvolve=calcGradient(p, intermediaPhi, ovData, A, velocity, edgeWeights);
51 | GEvolveModule=sqrt(GEvolve(:,1).^2 + GEvolve(:,2).^2);
52 | deltaAmountEvolve=GEvolveModule .* velocity * evolveStep;
53 | phi=phi-deltaAmountEvolve;
54 |
55 | %% Reinitial
56 | NR=3;
57 | for r=1:NR
58 | disp(['Reinitial ' num2str(r) ' ' datestr(now,13)]);
59 |
60 | GReinitial=calcGradient(p, phi, ovData, A, phi, edgeWeights);
61 | S=phi./sqrt(phi.^2+(scale*1)^2);
62 | deltaAmountReinitial=S .* (sqrt(GReinitial(:,1).^2+GReinitial(:,2).^2)-1) * reinitialStep;
63 | intermediaPhi=phi-0.5*deltaAmountReinitial;
64 |
65 | GReinitial=calcGradient(p, intermediaPhi, ovData, A, phi, edgeWeights);
66 | S=phi./sqrt(intermediaPhi.^2+(scale*1)^2);
67 | deltaAmountReinitial=S .* (sqrt(GReinitial(:,1).^2+GReinitial(:,2).^2)-1) * reinitialStep;
68 | phi=phi-deltaAmountReinitial;
69 | end
70 |
71 | %% Error % record
72 | e = e + evolveStep*Rsf*Rsf;
73 | phiAnalytical = UShapedAnalytical(e, p, scale/100);
74 |
75 | Result{idx, 1} = phi;
76 | AnalyticalResult{idx, 1} = phiAnalytical;
77 |
78 | ErrorTags=abs(phiAnalytical)<5*scale;
79 | Error=abs(phi(ErrorTags)-phiAnalytical(ErrorTags));
80 | EmResult(idx)=max(Error);
81 | EaResult(idx)=mean(Error);
82 | disp(['Error: max->' num2str(EmResult(idx)) ' average->' num2str(EaResult(idx))]);
83 |
84 | hold on;
85 | [~, h]=tricontour(p,t,phi,[0,0]);
86 | for idxH=1:numel(h)
87 | h(idxH).EdgeColor='b';
88 | end
89 | [~, h]=tricontour(p,t,phiAnalytical,[0,0]);
90 | for idxH=1:numel(h)
91 | h(idxH).EdgeColor='r';
92 | end
93 | drawnow;
94 | end
95 |
96 | save(['resultData/UShaped_' num2str(scale) '.mat'], 'Result', 'AnalyticalResult', 'EmResult', 'EaResult', 'p', 't');
97 | %save(['resultData/UShaped_ExplictDisabled_' num2str(scale) '.mat'], 'Result', 'AnalyticalResult', 'EmResult', 'EaResult', 'p', 't');
--------------------------------------------------------------------------------
/src/buildConnection.m:
--------------------------------------------------------------------------------
1 | function [C, NC, CMid, NCMid, NeighbourCells] = buildConnection(t)
2 |
3 | MAX_CONNECTION_PER_VERTEX=10;
4 | MAX_NEIGHBOR_CELLS_PER_VERTEX=8;
5 |
6 | NPoints=max(max(t));
7 | C=zeros(NPoints, MAX_CONNECTION_PER_VERTEX);
8 | CMid=zeros(NPoints, MAX_NEIGHBOR_CELLS_PER_VERTEX, 2);
9 | NeighbourCells=zeros(NPoints, MAX_NEIGHBOR_CELLS_PER_VERTEX);
10 |
11 | for i=1:NPoints
12 |
13 | logicalInvolvedTag=sum(t==i, 2)>0;
14 | [r,~,~]=find(logicalInvolvedTag);
15 | NeighbourCells(i,1:numel(r))=r';
16 |
17 | sourceMat=t(logicalInvolvedTag,:);
18 | writeCounter=1;
19 |
20 | for p=1:size(sourceMat,1)
21 | triTags=sourceMat(p,:);
22 | b=ismember(triTags,i);
23 | for bi=1:size(b,2)
24 | if((~b(bi)) && (~sum(ismember(C(i,:),triTags(bi)))))
25 | if(writeCounter>MAX_CONNECTION_PER_VERTEX)
26 | error(['writeCounter > ' num2str(MAX_CONNECTION_PER_VERTEX)]);
27 | else
28 | C(i,writeCounter)=triTags(bi);
29 | writeCounter=writeCounter+1;
30 | end
31 | end
32 | end
33 | CMid(i,p,:)=triTags(~b);
34 | end
35 | end
36 |
37 | NC=sum(C~=0,2);
38 | NCMid=sum(sum(CMid~=0,3)/2,2);
39 | end
--------------------------------------------------------------------------------
/src/buildMatrixA.m:
--------------------------------------------------------------------------------
1 | function [rA, edgeWeights] = buildMatrixA(ovData)
2 | NPoints=size(ovData,1);
3 | rA(NPoints,4).OK=true;
4 | rA(NPoints,4).A=[];
5 | edgeWeights=cell(NPoints,4);
6 |
7 | for idxP=1:NPoints
8 | for idxC=1:4
9 | OV=ovData{idxP,idxC}{1,1};
10 | OVMid=ovData{idxP,idxC}{1,3};
11 | A=[OV(:,1:2);OVMid(:,1:2)];
12 |
13 | edgeWeights{idxP,idxC}=[ovData{idxP,idxC}{1,5};ovData{idxP,idxC}{1,6}];
14 |
15 | rA(idxP,idxC).A=A .* edgeWeights{idxP,idxC};
16 | rA(idxP,idxC).OK=size(A,1)>=2;
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/src/buildOutgoingEdges.m:
--------------------------------------------------------------------------------
1 | function OV = buildOutgoingEdges(p, C, NC, CMid, NCMid)
2 |
3 | NVertices=size(NC,1);
4 | OV=cell(NVertices,5);
5 |
6 | xPos=[1, 0];
7 | xNeg=[-1, 0];
8 | yPos=[0, 1];
9 | yNeg=[0, -1];
10 |
11 | global divergenceThreshold;
12 |
13 | for idxP=1:NVertices
14 | crdT=[p(idxP, 1) p(idxP, 2)];
15 |
16 | neighborTag=C(idxP,1:NC(idxP))';
17 | crdV=p(neighborTag,:);
18 | crdOV=crdV-crdT;
19 | crdOVNorm=normr(crdOV);
20 |
21 | neighborTagMid=CMid(idxP,1:NCMid(idxP),:);
22 | neighborTagMid=reshape(neighborTagMid, [NCMid(idxP) 2]);
23 | crdVMid=(p(neighborTagMid(:,1),:)+p(neighborTagMid(:,2),:))./2;
24 | crdOVMid=crdVMid-crdT;
25 | crdOVNormMid=normr(crdOVMid);
26 |
27 | % xPos -> 1
28 | divergence=dot(crdOVNorm',repmat(xPos,NC(idxP),1)')';
29 | logicTag=divergence>divergenceThreshold;
30 | divergenceMid=dot(crdOVNormMid',repmat(xPos,NCMid(idxP),1)')';
31 | logicTagMid=divergenceMid>divergenceThreshold;
32 | OV{idxP,1}=compactOVInfo(logicTag, crdOV, neighborTag, logicTagMid,...
33 | crdOVMid, neighborTagMid, divergence, divergenceMid);
34 |
35 | % xNeg -> 2
36 | divergence=dot(crdOVNorm',repmat(xNeg,NC(idxP),1)')';
37 | logicTag=divergence>divergenceThreshold;
38 | divergenceMid=dot(crdOVNormMid',repmat(xNeg,NCMid(idxP),1)')';
39 | logicTagMid=divergenceMid>divergenceThreshold;
40 | OV{idxP,2}=compactOVInfo(logicTag, crdOV, neighborTag, logicTagMid,...
41 | crdOVMid, neighborTagMid, divergence, divergenceMid);
42 |
43 | % yPos -> 3
44 | divergence=dot(crdOVNorm',repmat(yPos,NC(idxP),1)')';
45 | logicTag=divergence>divergenceThreshold;
46 | divergenceMid=dot(crdOVNormMid',repmat(yPos,NCMid(idxP),1)')';
47 | logicTagMid=divergenceMid>divergenceThreshold;
48 | OV{idxP,3}=compactOVInfo(logicTag, crdOV, neighborTag, logicTagMid,...
49 | crdOVMid, neighborTagMid, divergence, divergenceMid);
50 |
51 | % yNeg -> 4
52 | divergence=dot(crdOVNorm',repmat(yNeg,NC(idxP),1)')';
53 | logicTag=divergence>divergenceThreshold;
54 | divergenceMid=dot(crdOVNormMid',repmat(yNeg,NCMid(idxP),1)')';
55 | logicTagMid=divergenceMid>divergenceThreshold;
56 | OV{idxP,4}=compactOVInfo(logicTag, crdOV, neighborTag, logicTagMid,...
57 | crdOVMid, neighborTagMid, divergence, divergenceMid);
58 |
59 | % all -> 5
60 | OV{idxP,5}={[crdOV, normr(crdOV), sqrt(crdOV(:,1).^2+crdOV(:,2).^2)], neighborTag,...
61 | [crdOVMid, normr(crdOVMid), sqrt(crdOVMid(:,1).^2+crdOVMid(:,2).^2)], neighborTagMid};
62 |
63 | end
64 |
65 | end
66 |
67 | function OVInfoCell = compactOVInfo(logicTag, crdOV, neighborTag, logicTagMid,...
68 | crdOVMid, neighborTagMid, divergence, divergenceMid)
69 | selectedcrdOV=crdOV(logicTag,:);
70 | selectedneighborTag=neighborTag(logicTag);
71 | selectedcrdOVMid=crdOVMid(logicTagMid,:);
72 | selectedneighborTagMid=neighborTagMid(logicTagMid,:);
73 | OVInfoCell={[selectedcrdOV, sqrt(selectedcrdOV(:,1).^2+selectedcrdOV(:,2).^2)], selectedneighborTag, ...
74 | [selectedcrdOVMid, sqrt(selectedcrdOVMid(:,1).^2+selectedcrdOVMid(:,2).^2)], selectedneighborTagMid...
75 | divergence(logicTag), divergenceMid(logicTagMid)};
76 | end
77 |
--------------------------------------------------------------------------------
/src/calcGradient.m:
--------------------------------------------------------------------------------
1 | function G = calcGradient(p, phi, ovData, A, S, edgeWeights)
2 | global eps_;
3 |
4 | NPoints=size(phi,1);
5 | % 2D
6 | G=zeros(NPoints, 2);
7 |
8 | if size(S,2) > 1
9 | isUseSxyAsMainAxis=true;
10 | Sx=S(:,1);
11 | Sy=S(:,2);
12 | else
13 | isUseSxyAsMainAxis=false;
14 | Sx=S;
15 | Sy=S;
16 | end
17 |
18 | ovData5=ovData(:,5);
19 |
20 |
21 | for idxP=1:NPoints
22 |
23 | crtPhi=phi(idxP);
24 | crtCoordinate=p(idxP,:);
25 | ovEndPhi=phi(ovData5{idxP}{2});
26 | isOnZeroContour=(crtPhi > eps_ && any(ovEndPhi < -eps_)) ...
27 | || (crtPhi < -eps_ && any(ovEndPhi > eps_));
28 | % set isOnZeroContour = false here to disable explicit searching branch
29 | %isOnZeroContour = false;
30 |
31 | myOVEdges=ovData5{idxP}{1};
32 | myOVEdgesMid=ovData5{idxP}{3};
33 | myOVEdgesMidIndices=ovData5{idxP}{4};
34 |
35 |
36 | if isOnZeroContour
37 | [GExplicit,OK]=calcGradientViaExplicitGeometry(p, idxP, myOVEdgesMidIndices, crtCoordinate, crtPhi, phi, isUseSxyAsMainAxis);
38 | if OK
39 | G(idxP,:)=GExplicit;
40 | else
41 | error('calcGradientViaExplicitGeometry Error')
42 | end
43 |
44 | else
45 |
46 | changingAmount=ovEndPhi-crtPhi;
47 | changingRate=(changingAmount)./(myOVEdges(:,5));
48 |
49 |
50 | % find the vertex aiming at zero or aiming to upwind of S
51 | if isUseSxyAsMainAxis
52 | aimingDirection=-normr([Sx(idxP), Sy(idxP)]);
53 | else
54 | if crtPhi > 0
55 | [~,im]=min(changingRate);
56 | else
57 | [~,im]=max(changingRate);
58 | end
59 | aimingDirection=myOVEdges(im,3:4);
60 | end
61 |
62 | [GAiming1,OK1] = estimateGradientOnOneDirection(aimingDirection, myOVEdges, changingAmount,...
63 | myOVEdgesMid, myOVEdgesMidIndices, crtPhi, phi);
64 | [GAiming2,OK2] = estimateGradientOnOneDirection(-aimingDirection, myOVEdges, changingAmount,...
65 | myOVEdgesMid, myOVEdgesMidIndices, crtPhi, phi);
66 |
67 | if OK1 && OK2
68 | [~, tag]=Godunov(dot(GAiming2, aimingDirection, 2),dot(GAiming1, aimingDirection, 2), S(idxP));
69 | if tag == 1
70 | G(idxP,:)=GAiming1;
71 | elseif tag == -1
72 | G(idxP,:)=GAiming2;
73 | else
74 | G(idxP,:)=[0, 0];
75 | end
76 | elseif OK1 && (~OK2)
77 | G(idxP,:)=GAiming1;
78 | elseif (~OK1) && OK2
79 | G(idxP,:)=GAiming2;
80 | else
81 | error('aimingDirection unavailable');
82 | end
83 | end
84 | end
85 |
86 | end
87 |
88 | function [G,OK]=calcGradientViaExplicitGeometry(p, idxP, myOVEdgesMidIndices, crtCoordinate, crtPhi, phi, isUseSxyAsMainAxis)
89 | global eps_;
90 | % zeroContourEdges: [x1, y1, x2, y2];
91 | zeroContourEdges1=zeros(size(myOVEdgesMidIndices));
92 | zeroContourEdges2=zeros(size(myOVEdgesMidIndices));
93 | nZeroContourEdges=0;
94 |
95 | for i = 1:size(myOVEdgesMidIndices,1)
96 | triangleOtherVertices=myOVEdgesMidIndices(i,:);
97 | phiArray=[crtPhi;phi(triangleOtherVertices)];
98 |
99 | % see if this triangle is on zero contour
100 | if sum(phiArray>eps_)>0 && sum(phiArray<-eps_)>0
101 |
102 | % see if we need to filter the triangle
103 | if isUseSxyAsMainAxis
104 | divergence=dot([Sx(idxP) Sy(idxP)], myOVEdgesMid(i, 3:4));
105 |
106 | % check for safe
107 | if abs(divergence) > 1
108 | error('divergence cannot > 1')
109 | end
110 |
111 | if divergence < divergenceThreshold + 0.1
112 | continue;
113 | end
114 | end
115 |
116 | nZeroContourEdges=nZeroContourEdges+1;
117 |
118 | % find the edge and add to zeroContourEdges
119 | [lineSeg1, lineSeg2] = findContourInTriangle(p([idxP, triangleOtherVertices],:), phiArray);
120 | zeroContourEdges1(nZeroContourEdges,:)=lineSeg1;
121 | zeroContourEdges2(nZeroContourEdges,:)=lineSeg2;
122 | end
123 | end
124 |
125 | zeroContourEdges1(nZeroContourEdges+1:end,:)=[];
126 | zeroContourEdges2(nZeroContourEdges+1:end,:)=[];
127 |
128 | % find the nearest point
129 | nearestPoint=[];
130 | nearestDistance=Inf;
131 | for i=1:nZeroContourEdges
132 | [xy,dis,~]=distance2curve([zeroContourEdges1(i,:);zeroContourEdges2(i,:)],crtCoordinate);
133 | if dis < nearestDistance
134 | nearestPoint=xy;
135 | nearestDistance=dis;
136 | end
137 | end
138 |
139 | % calc real gradient
140 | gradientDirection=normr(nearestPoint-crtCoordinate);
141 | G=gradientDirection*(-crtPhi)/nearestDistance;
142 |
143 | OK=true;
144 | end
145 |
146 | function [G] = calcGradient1Axis1Vertex(phi,crtPhi,OVTags,OVMidTags,dA,weight)
147 | changingAmount=(phi(OVTags)-crtPhi);
148 |
149 | phiMid=(phi(OVMidTags(:,1))+phi(OVMidTags(:,2)))./2;
150 | changingAmountMid=(phiMid-crtPhi);
151 |
152 | b=[changingAmount;changingAmountMid] .* weight;
153 | G=dA\b;
154 | end
155 |
156 | function [G,OK] = estimateGradientOnOneDirection(aimingDirection, myOVEdges, changingAmount,...
157 | myOVEdgesMid, myOVEdgesMidIndices, crtPhi, phi)
158 |
159 | global divergenceThreshold;
160 | crtDivergenceThreshold=divergenceThreshold;
161 |
162 | % find the region where gradient vector may exist
163 | % construct A
164 | realEdgeDivergence=dot(myOVEdges(:,3:4),repmat(aimingDirection,size(myOVEdges,1),1),2);
165 | midEdgeDivergence=dot(myOVEdgesMid(:,3:4),repmat(aimingDirection,size(myOVEdgesMid,1),1),2);
166 | isRealEdgeDivergenceAccepted=realEdgeDivergence>crtDivergenceThreshold;
167 | isMidEdgeDivergenceAccepted=midEdgeDivergence>crtDivergenceThreshold;
168 |
169 | OK = (sum(isRealEdgeDivergenceAccepted) + sum(isMidEdgeDivergenceAccepted)) >= 2;
170 |
171 | if ~OK
172 | G=[0,0];
173 | return;
174 | end
175 |
176 | weights=[realEdgeDivergence(isRealEdgeDivergenceAccepted);...
177 | midEdgeDivergence(isMidEdgeDivergenceAccepted)];
178 | A=[myOVEdges(isRealEdgeDivergenceAccepted,1:2);myOVEdgesMid(isMidEdgeDivergenceAccepted,1:2)]...
179 | .*weights;
180 | b=[changingAmount(isRealEdgeDivergenceAccepted);...
181 | (phi(myOVEdgesMidIndices(isMidEdgeDivergenceAccepted,1)) +...
182 | phi(myOVEdgesMidIndices(isMidEdgeDivergenceAccepted,2)))./2 - crtPhi]...
183 | .*weights;
184 | gradientEstimatedVia1stStep=A\b;
185 | G=gradientEstimatedVia1stStep';
186 | end
187 |
188 | function [R, returnTag] = Godunov(negativeDiff,positiveDiff,S)
189 | if S>0
190 | if (negativeDiff <= 0 && positiveDiff <= 0)
191 | returnTag=1;
192 | R=positiveDiff;
193 | return;
194 | end
195 | if (negativeDiff >= 0 && positiveDiff >= 0)
196 | returnTag=-1;
197 | R=negativeDiff;
198 | return;
199 | end
200 | if (negativeDiff > 0 && positiveDiff < 0)
201 | if (abs(negativeDiff) > abs(positiveDiff))
202 | returnTag=-1;
203 | R=negativeDiff;
204 | return;
205 | else
206 | returnTag=1;
207 | R=positiveDiff;
208 | return;
209 | end
210 | else
211 | returnTag=0;
212 | R=0;
213 | return;
214 | end
215 | else
216 | if (negativeDiff <= 0 && positiveDiff <= 0)
217 | returnTag=-1;
218 | R=negativeDiff;
219 | return;
220 | end
221 | if (negativeDiff >= 0 && positiveDiff >= 0)
222 | returnTag=1;
223 | R=positiveDiff;
224 | return;
225 | end
226 | if (negativeDiff < 0 && positiveDiff > 0)
227 | if (abs(negativeDiff) > abs(positiveDiff))
228 | returnTag=-1;
229 | R=negativeDiff;
230 | return;
231 | else
232 | returnTag=1;
233 | R=positiveDiff;
234 | return;
235 | end
236 | else
237 | returnTag=0;
238 | R=0;
239 | return;
240 | end
241 | end
242 | end
243 |
--------------------------------------------------------------------------------
/src/config.m:
--------------------------------------------------------------------------------
1 | global scale;
2 | scale=0.01;
3 |
4 | global divergenceThreshold;
5 | divergenceThreshold=0.65;
6 |
7 | global eps_;
8 | eps_=1e-3;
9 |
10 | center=[0.5, 0.5];
11 | evolveStep=0.004;
12 | reinitialStep=0.007;
13 |
14 | global scaleStick;
15 | scaleStick=0.005;
16 | evolveStepStick=0.003;
17 | reinitialStepStick=0.001;
--------------------------------------------------------------------------------
/src/dcircle.m:
--------------------------------------------------------------------------------
1 | function d=dcircle(p,xc,yc,r)
2 |
3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details.
4 |
5 | d=sqrt((p(:,1)-xc).^2+(p(:,2)-yc).^2)-r;
6 |
--------------------------------------------------------------------------------
/src/ddiff.m:
--------------------------------------------------------------------------------
1 | function d=ddiff(d1,d2), d=max(d1,-d2);
2 |
3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details.
4 |
--------------------------------------------------------------------------------
/src/distLineSeg2Point.m:
--------------------------------------------------------------------------------
1 | function [dist] = distLineSeg2Point(x, a, b)
2 | % input: [x,y] x: point a&b: line segment
3 | % output: dist
4 |
5 | d_ab = norm(a-b);
6 | d_ax = norm(a-x);
7 | d_bx = norm(b-x);
8 |
9 | if dot(a-b,x-b)*dot(b-a,x-a)>=0
10 | A = [a,1;b,1;x,1];
11 | dist = abs(det(A))/d_ab;
12 | else
13 | dist = min(d_ax, d_bx);
14 | end
15 | end
--------------------------------------------------------------------------------
/src/drawResult.m:
--------------------------------------------------------------------------------
1 | %% config
2 | config;
3 | meshEdgeColor=[.2,.2,.2];
4 | startColor=[0, 0, 1];
5 | endColor=[1, 0, 0];
6 | fontSize=15;
7 |
8 | %% U region
9 | drawURegion=false;
10 | if drawURegion
11 | %% read data
12 | load(['resultData/UShaped_ExplictDisabled_' num2str(scale) '.mat']);
13 | disEaResult=EaResult;
14 | disEmResult=EmResult;
15 | disResult=Result;
16 | load(['resultData/UShaped_' num2str(scale) '.mat']);
17 |
18 | %% draw
19 | hold on;
20 | patch('faces',t,'vertices',p,'facecolor','w','edgecolor',meshEdgeColor);
21 | initialLineX=[0.0, 0.4, 0.4, 0.6, 0.6, 1.0];
22 | initialLineY=[1.0, 1.0, 0.6, 0.6, 1.0, 1.0];
23 | plot(initialLineX, initialLineY, 'r-', 'LineWidth', 3);
24 | ax = gca;
25 | ax.FontSize = fontSize;
26 |
27 | pause
28 | clf;
29 | hold on;
30 | xlim([0,1]);
31 | ylim([0,1]);
32 | vertices=[0,0; 1,0; 1,1; 0.6,1; 0.6,0.6; 0.4,0.6; 0.4,1; 0,1];
33 | plot(vertices(:,1), vertices(:,2), 'Color', meshEdgeColor);
34 | plot([vertices(end,1), vertices(end,1)], [vertices(end,2) vertices(1,2)], 'Color', meshEdgeColor);
35 | for idxStep=1:12:length(EaResult)
36 | [~, ha]=tricontour(p, t, AnalyticalResult{idxStep}, [0, 0]);
37 | for idxH=1:numel(ha)
38 | ha(idxH).EdgeColor='r';
39 | end
40 |
41 | [~, hd]=tricontour(p, t, disResult{idxStep}, [0, 0]);
42 | for idxH=1:numel(hd)
43 | hd(idxH).EdgeColor='b';
44 | end
45 |
46 | [~, hr]=tricontour(p, t, Result{idxStep}, [0, 0]);
47 | for idxH=1:numel(hr)
48 | hr(idxH).EdgeColor='k';
49 | end
50 | end
51 | legend([ha(1) hd(1) hr(1)],...
52 | {'Analytical result',...
53 | 'Simulation result without EC','Simulation result with EC'},...
54 | 'Location','SouthWest', 'FontSize', fontSize);
55 | ax = gca;
56 | ax.FontSize = fontSize;
57 |
58 | pause;
59 | clf;
60 | hold on;
61 | plot(disEmResult, 'b-.', 'LineWidth', 1);
62 | plot(disEaResult, 'r-.', 'LineWidth', 1);
63 | plot(EmResult, 'b-', 'LineWidth', 2);
64 | plot(EaResult, 'r-', 'LineWidth', 2);
65 | h=legend('E_m without EC','E_a without EC',...
66 | 'E_m with EC','E_a with EC',...
67 | 'Location','NorthWest');
68 | h.FontSize = fontSize;
69 | xlabel('Step','FontSize', fontSize);
70 | ylabel('Error','FontSize', fontSize);
71 | ax = gca;
72 | ax.FontSize = fontSize;
73 |
74 | end
75 |
76 | %% 3 circles
77 | drawCircles=false;
78 | drawCirclesGap=3;
79 | if drawCircles
80 | load(['resultData/3Circles_' num2str(scale) '.mat']);
81 |
82 | %% iso lines
83 | idxArray=1:drawCirclesGap:size(Result,1);
84 | nLines=numel(idxArray);
85 |
86 | hold on;
87 | for idx=1:nLines
88 | phi=Result{idxArray(idx)};
89 | [C, h]=tricontour(p, t, phi, [0, 0]);
90 | crtColor=(startColor * (nLines-idx) + endColor * idx)/nLines;
91 | for idxH=1:numel(h)
92 | h(idxH).EdgeColor=crtColor;
93 | end
94 | end
95 | ax = gca;
96 | ax.FontSize = fontSize;
97 |
98 |
99 | pause;
100 | %% error
101 | clf;
102 | hold on;
103 | plot(EmResult);
104 | plot(EaResult);
105 | ax = gca;
106 | ax.FontSize = fontSize;
107 | h=legend('E_m','E_a');
108 | h.FontSize = fontSize;
109 | xlabel('Step','FontSize', fontSize);
110 | ylabel('Error','FontSize', fontSize);
111 | ax = gca;
112 | ax.FontSize = fontSize;
113 | end
114 |
115 | %% Rate stick
116 | drawRateStick=false;
117 | drawRateStickGap=8;
118 | if drawRateStick
119 | load(['resultData/RateStick_' num2str(scaleStick) '.mat']);
120 |
121 | %% mesh
122 | hold on;
123 | pbaspect([1 0.05 1]);
124 | patch('faces',t,'vertices',p,'facecolor','w','edgecolor',meshEdgeColor);
125 | plot(0, 0.025, 'ro', 'MarkerSize', 5, 'MarkerFaceColor', 'r');
126 | text(-0.05, 0.025, '$\vec{x_0}$', 'Interpreter', 'latex', 'FontSize', fontSize+1);
127 | ax = gca;
128 | ax.YAxisLocation = 'right';
129 | ax.FontSize = fontSize+1;
130 |
131 | %% contour
132 | pause;
133 | idxArray=1:drawRateStickGap:size(Result,1);
134 | nLines=numel(idxArray);
135 |
136 | clf;
137 | hold on;
138 | plot(0, 0.025, 'ro', 'MarkerSize', 5, 'MarkerFaceColor', 'r');
139 | text(-0.05, 0.025, '$\vec{x_0}$', 'Interpreter', 'latex', 'FontSize', fontSize+1);
140 | pbaspect([1 0.05 1]);
141 | for idx=1:nLines
142 | phi=Result{idxArray(idx)};
143 | [C, h]=tricontour(p, t, phi, [0, 0]);
144 | crtColor=(startColor * (nLines-idx) + endColor * idx)/nLines;
145 | for idxH=1:numel(h)
146 | h(idxH).EdgeColor=crtColor;
147 | end
148 | end
149 | ax = gca;
150 | ax.YAxisLocation = 'right';
151 | ax.FontSize = fontSize+1;
152 |
153 |
154 | pause;
155 | %% error
156 | clf;
157 | hold on;
158 | pbaspect auto;
159 | plot(EmResult);
160 | plot(EaResult);
161 | h=legend('E_m','E_a','Location','SouthEast');
162 | h.FontSize = fontSize;
163 | xlabel('Step','FontSize', fontSize);
164 | ylabel('Error','FontSize', fontSize);
165 | ax = gca;
166 | ax.FontSize = fontSize;
167 |
168 | end
169 |
170 | %% Redistance
171 | drawRedistance=true;
172 | if drawRedistance
173 | x = -1:scale:1;
174 | y = x;
175 | [gX, gY] = meshgrid(x, y);
176 | %gX = reshape(gX, numel(gX), 1);
177 | %gY = reshape(gY, numel(gY), 1);
178 | % only draw the z = 0 2D section
179 | gZ = 0;
180 | h = 0.5;
181 | phi = -min(min(min(min(h+gX, h-gX)), min(h+gY, h-gY)), min(h+gZ, h-gZ));
182 |
183 | hold on;
184 | contour(gX, gY, phi, [0, 0], 'r-', 'LineWidth', 2);
185 |
186 | phi = phi .* ((gX-0.3).^2 + (gY-0.2).^2 + (gZ-0.1).^2 + 0.005);
187 | contour(gX, gY, phi, min(min(phi)):0.03:max(max(phi)));
188 |
189 | ax = gca;
190 | ax.FontSize = fontSize;
191 | end
192 |
--------------------------------------------------------------------------------
/src/drectangle.m:
--------------------------------------------------------------------------------
1 | function d=drectangle(p,x1,x2,y1,y2)
2 |
3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details.
4 |
5 | d=-min(min(min(-y1+p(:,2),y2-p(:,2)),-x1+p(:,1)),x2-p(:,1));
6 |
--------------------------------------------------------------------------------
/src/dunion.m:
--------------------------------------------------------------------------------
1 | function d=dunion(d1,d2), d=min(d1,d2);
2 |
3 | % Copyright (C) 2004-2012 Per-Olof Persson. See COPYRIGHT.TXT for details.
4 |
--------------------------------------------------------------------------------
/src/findContourInTriangle.m:
--------------------------------------------------------------------------------
1 | function [lineSeg1, lineSeg2] = findContourInTriangle(coordinates, phi)
2 | % coordinates*: [x, y; x, y; x, y] coordinates of 3 vertices
3 | % phi*: 3x1 value of phi on 3 vertices
4 | % output: lineSeg*: [x, y] 2 ends of the line segment
5 |
6 | if any(size(phi) ~= [3, 1])
7 | error('size(phi) shall be 3x1');
8 | end
9 |
10 |
11 | isEdge12Cutted= (phi(1)>=0 && phi(2) < 0) || (phi(1)<=0 && phi(2) > 0);
12 | isEdge23Cutted= (phi(2)>=0 && phi(3) < 0) || (phi(2)<=0 && phi(3) > 0);
13 | isEdge31Cutted= (phi(3)>=0 && phi(1) < 0) || (phi(3)<=0 && phi(1) > 0);
14 |
15 |
16 | if (isEdge31Cutted && isEdge23Cutted)
17 | % check on Edge 13 and 23
18 | lineSeg1 = findZeroOnEdgeWrapper(1, 3, coordinates, phi);
19 | lineSeg2 = findZeroOnEdgeWrapper(2, 3, coordinates, phi);
20 | elseif (isEdge12Cutted && isEdge23Cutted)
21 | % check on Edge 12 and 23
22 | lineSeg1 = findZeroOnEdgeWrapper(1, 2, coordinates, phi);
23 | lineSeg2 = findZeroOnEdgeWrapper(2, 3, coordinates, phi);
24 | elseif (isEdge12Cutted && isEdge31Cutted)
25 | % check on Edge 12 and 13
26 | lineSeg1 = findZeroOnEdgeWrapper(1, 2, coordinates, phi);
27 | lineSeg2 = findZeroOnEdgeWrapper(1, 3, coordinates, phi);
28 | else
29 | error('All phi have the same sign!')
30 | end
31 |
32 | end
33 |
34 | function [R] = findZeroOnEdgeWrapper(t1, t2, coordinates, phi)
35 | R = findZeroOnEdge(coordinates(t1,:),coordinates(t2,:),phi(t1),phi(t2));
36 | end
--------------------------------------------------------------------------------
/src/findZeroOnEdge.m:
--------------------------------------------------------------------------------
1 | function [Z] = findZeroOnEdge(a,b,phiA,phiB)
2 | % input: a&b: [x,y] line segment phiA/B: phi value on a and b
3 | % output: Z: [x,y] point on ab where phi=0, or NaN if there's no zero point
4 |
5 | if sign(phiA) == sign(phiB)
6 | Z=NaN;
7 | return;
8 | end
9 |
10 | Z=a+(abs(phiA)/(abs(phiA)+abs(phiB)))*(b-a);
11 | end
12 |
13 |
--------------------------------------------------------------------------------
/src/generateTriangleNorms.m:
--------------------------------------------------------------------------------
1 | function [triangleOutNorms ,cornerNorm, areas] = generateTriangleNorms(p, t)
2 | % input: p, t: mesh
3 | % output: triangleOutNorm of size (nTriangles, 3, 2)
4 | % cornerNorm of size (nTriangles, 3, 2)
5 |
6 | nTriangles=size(t, 1);
7 | triangleOutNorms=zeros(nTriangles, 3, 2);
8 | cornerNorm=zeros(nTriangles, 3, 2);
9 | areas=zeros(nTriangles, 1);
10 |
11 | for idxT=1:nTriangles
12 | triangleIdxP=t(idxT,:);
13 | triangleCoordinates=p(triangleIdxP,:);
14 |
15 | % edge out norm: e12, e23, e31
16 | TON=triangleOutNorm(triangleCoordinates);
17 | triangleOutNorms(idxT, :, :)=TON;
18 |
19 | % corner norm
20 | % Nc1= e12 + e31
21 | cornerNorm(idxT, 1, :)=normr(TON(1, :) + TON(3, :));
22 | % Nc2= e12 + e23
23 | cornerNorm(idxT, 2, :)=normr(TON(1, :) + TON(2, :));
24 | % Nc3= e23 + e31
25 | cornerNorm(idxT, 3, :)=normr(TON(2, :) + TON(3, :));
26 |
27 | areas(idxT, 1)=polyarea(triangleCoordinates(:,1), triangleCoordinates(:,2));
28 | end
29 | end
30 |
31 |
--------------------------------------------------------------------------------
/src/rayLineSegmentIntersection.m:
--------------------------------------------------------------------------------
1 | function [Intersetion, L, isCrossed] = rayLineSegmentIntersection(...
2 | rayOrigin, rayDirection, lineSegEnd1, lineSegEnd2)
3 | % input: [x,y] rayOrigin, rayDirection, lineSegEnd1, lineSegEnd2
4 | % output: Intersetion: [x,y] corss point
5 | % L: length from rayOrigin to Intersetion
6 | % isCrossed: true/false indicating if corss point is found
7 |
8 | rayDirection=normr(rayDirection);
9 | lineSegVector=lineSegEnd2-lineSegEnd1;
10 | segDirection=normr(lineSegVector);
11 | segLength=norm(lineSegVector);
12 |
13 | % if they are parallel
14 | if abs(dot(rayDirection,segDirection)-1)0 && st>0 && st