1 /*
2     GNU Octave level-set package.
3     Copyright (C) 2013-2014  Daniel Kraft <d@domob.eu>
4 
5     This program is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 /* Internal C++ implementation for mesh building.  Inside and outside elements
20    are simply split in two triangles, while boundary elements are split
21    depending on how they are intersected by the boundary.  For them, the
22    geom.internal data about "boundary segments" is used.  */
23 
24 #include <octave/oct.h>
25 #include <octave/Cell.h>
26 #include <octave/ov-struct.h>
27 
28 #include <stdexcept>
29 #include <vector>
30 
31 #include "Utils.hpp"
32 
33 /**
34  * Type used to hold maps of indices.  It needs to allow negative values, too,
35  * since they are used as "indices" of intersection points in the geometry.
36  * Furthermore, -1 is used to denote not-yet-set indices (since NA is not
37  * available as an integer value).
38  */
39 typedef std::vector<int> indexArr;
40 
41 /** Type used for coordinates in space.  */
42 typedef double realT;
43 
44 /* ************************************************************************** */
45 /* Containers for the datas of the mesh.  */
46 
47 /**
48  * Represent a vertex in the list of vertices of a mesh (the mesh.p result).
49  */
50 class Vertex
51 {
52 
53 private:
54 
55   /** This vertex' x coordinate in space.  */
56   realT x;
57   /** This vertex' y coordinate in space.  */
58   realT y;
59 
60 public:
61 
62   /**
63    * Construct with given coordinates.
64    * @param mx x coordinate.
65    * @param my y coordinate.
66    */
Vertex(const realT mx,const realT my)67   inline Vertex (const realT mx, const realT my)
68     : x(mx), y(my)
69   {
70     // Nothing else to do.
71   }
72 
73   /* Default copying.  */
74   Vertex () = default;
75   Vertex (const Vertex& o) = default;
76   Vertex& operator= (const Vertex& o) = default;
77 
78   /**
79    * Convert to row in the vertex matrix to be returned to Octave.
80    * @param i Index for the row to write in.
81    * @param m Matrix to write to.
82    */
83   inline void
toMatrix(const unsigned i,Matrix & m) const84   toMatrix (const unsigned i, Matrix& m) const
85   {
86     m(0, i) = x;
87     m(1, i) = y;
88   }
89 
90 };
91 
92 /**
93  * Represent a triangle in the list of triangles of a mesh (mesh.t field).
94  */
95 class Triangle
96 {
97 
98 private:
99 
100   /** Indices of the vertices.  */
101   unsigned vertices[3];
102 
103 public:
104 
105   /**
106    * Construct with given vertices.
107    * @param a First vertex.
108    * @param b Second vertex.
109    * @param c Third vertex.
110    */
Triangle(const unsigned a,const unsigned b,const unsigned c)111   inline Triangle (const unsigned a, const unsigned b, const unsigned c)
112     : vertices{a, b, c}
113   {
114     // Nothing else to do.
115   }
116 
117   /* Default copying.  */
118   Triangle () = default;
119   Triangle (const Triangle& o) = default;
120   Triangle& operator= (const Triangle& o) = default;
121 
122   /**
123    * Convert to row in the triangle matrix to be returned to Octave.
124    * @param i Index for the row to write in.
125    * @param m Matrix to write to.
126    */
127   inline void
toMatrix(const unsigned i,Matrix & m) const128   toMatrix (const unsigned i, Matrix& m) const
129   {
130     for (unsigned j = 0; j < 3; ++j)
131       m(j, i) = vertices[j];
132   }
133 
134 };
135 
136 /* ************************************************************************** */
137 /* Mesh class itself.  */
138 
139 /**
140  * This class represents the mesh that is being built up.  It keeps track of
141  * the vertices and triangles as they are added.  In addition,
142  * in order to do that, it also keeps a map between original vertex indices
143  * and indices into the already set-up list of vertices.
144  */
145 class Mesh
146 {
147 
148 private:
149 
150   /** The list of vertices.  */
151   std::vector<Vertex> vertices;
152   /** The list of triangles.  */
153   std::vector<Triangle> triangles;
154 
155   /** Map ordinary grid point index to that in list of vertices.  */
156   indexArr innerVertexInd;
157   /** Map intersection point index to that in list of vertices.  */
158   indexArr bdryVertexInd;
159   /**
160    * Map index of vertices on the mesh to the vertex code on the geometry.
161    * I. e., may be positive or negative depending on grid point or ispt.
162    */
163   indexArr meshVertexInd;
164 
165 public:
166 
167   /**
168    * Construct the yet empty mesh.
169    * @param n Number of inner grid points.
170    * @param nBdry Number of intersection points.
171    */
172   Mesh (const unsigned n, const unsigned nBdry);
173 
174   /* No copying.  */
175   Mesh () = delete;
176   Mesh (const Mesh& o) = delete;
177   Mesh& operator= (const Mesh& o) = delete;
178 
179   /**
180    * Get internal vertex index for a given grid point.  Insert it into
181    * the list of vertices if necessary.
182    * @param ind Grid point index.
183    * @param coord Full coordinate array, so we know the coordinates.
184    * @return Index into internal vertex list.
185    */
186   unsigned getInnerVertex (const unsigned ind, const Matrix& coord);
187 
188   /**
189    * Get internal vertex index for a given intersection point.  Insert it into
190    * the list of vertices if necessary.
191    * @param ind Grid point index.
192    * @param coord Array of coordinates of boundary points.
193    * @return Index into internal vertex list.
194    */
195   unsigned getBoundaryVertex (const unsigned ind, const Matrix& coord);
196 
197   /**
198    * Add a triangle with the given vertices.
199    * @param a First vertex.
200    * @param b Second vertex.
201    * @param c Third vertex.
202    */
203   inline void
addTriangle(const unsigned a,const unsigned b,const unsigned c)204   addTriangle (const unsigned a, const unsigned b, const unsigned c)
205   {
206     triangles.push_back (Triangle(a, b, c));
207   }
208 
209   /**
210    * Wrap the mesh's content up into the struct expected by Octave.
211    * @param res Store the msh structure here.
212    * @param vertexMap Store the vertex map here.
213    */
214   void toStruct (octave_scalar_map& res, octave_scalar_map& vertexMap) const;
215 
216 };
217 
Mesh(const unsigned n,const unsigned nBdry)218 Mesh::Mesh (const unsigned n, const unsigned nBdry)
219   : vertices(), triangles(),
220     innerVertexInd(n, -1), bdryVertexInd(nBdry, -1), meshVertexInd()
221 {
222   // Nothing else to do.
223 }
224 
225 unsigned
getInnerVertex(const unsigned ind,const Matrix & coord)226 Mesh::getInnerVertex (const unsigned ind, const Matrix& coord)
227 {
228   if (innerVertexInd[ind] < 0)
229     {
230       const unsigned next = vertices.size ();
231       assert (next == meshVertexInd.size ());
232 
233       vertices.push_back (Vertex(coord(ind, 0), coord(ind, 1)));
234       meshVertexInd.push_back (ind + 1);
235 
236       innerVertexInd[ind] = next + 1;
237     }
238 
239   assert (innerVertexInd[ind] >= 0);
240   return innerVertexInd[ind];
241 }
242 
243 unsigned
getBoundaryVertex(const unsigned ind,const Matrix & coord)244 Mesh::getBoundaryVertex (const unsigned ind, const Matrix& coord)
245 {
246   if (bdryVertexInd[ind] < 0)
247     {
248       const unsigned next = vertices.size ();
249       assert (next == meshVertexInd.size ());
250 
251       vertices.push_back (Vertex(coord(ind, 0), coord(ind, 1)));
252       /* FIXME: Can get rid of cast?  */
253       meshVertexInd.push_back (-static_cast<int> (ind) - 1);
254 
255       bdryVertexInd[ind] = next + 1;
256     }
257 
258   assert (bdryVertexInd[ind] >= 0);
259   return bdryVertexInd[ind];
260 }
261 
262 void
toStruct(octave_scalar_map & res,octave_scalar_map & vertexMap) const263 Mesh::toStruct (octave_scalar_map& res, octave_scalar_map& vertexMap) const
264 {
265   Matrix vert(2, vertices.size ());
266   Matrix tri(3, triangles.size ());
267 
268   for (unsigned i = 0; i < vertices.size (); ++i)
269     vertices[i].toMatrix (i, vert);
270   for (unsigned i = 0; i < triangles.size (); ++i)
271     triangles[i].toMatrix (i, tri);
272 
273   vertexMap = octave_scalar_map ();
274   vertexMap.assign ("grid", convertToColumnVector (innerVertexInd));
275   vertexMap.assign ("ispt", convertToColumnVector (bdryVertexInd));
276   vertexMap.assign ("mesh", convertToColumnVector (meshVertexInd));
277 
278   res = octave_scalar_map ();
279   res.assign ("p", vert);
280   res.assign ("t", tri);
281   /* e is filled out from geom.bedges in the m-file itself.  */
282 }
283 
284 /* ************************************************************************** */
285 
286 /**
287  * Extract the relevant information for building up the triangles from
288  * the boundary element segments information provided by ls_find_geometry.
289  * It basically just extracts everything of interest and reorders the points.
290  * @param segs Boundary element segment info from ls_find_geometry.
291  * @param bdryPoints Return boundary points here.
292  * @param innerPts Add inner points here.
293  */
294 void
getInnerSegment(const octave_scalar_map & segs,unsigned bdryPts[2],indexArr & innerPts)295 getInnerSegment (const octave_scalar_map& segs,
296                  unsigned bdryPts[2], indexArr& innerPts)
297 {
298   bdryPts[0] = segs.contents ("endPt").int_value () - 1;
299   bdryPts[1] = segs.contents ("startPt").int_value () - 1;
300 
301   assert (innerPts.empty ());
302   const ColumnVector inners = segs.contents ("inners").column_vector_value ();
303   const unsigned nInners = inners.numel ();
304   for (unsigned i = 0; i < nInners; ++i)
305     innerPts.push_back (inners(nInners - i - 1) - 1);
306 }
307 
308 /* Octave routine for mesh building.  */
309 DEFUN_DLD (__levelset_internal_mesh, args, nargout,
310   "  [MESH, VMAP] = internal_mesh (L, M, INSIDE, COORDS, ISPTCOORDS,\n"
311   "                                NODELIST, INELEMS, OUTELEMS,\n"
312   "                                BDRYELEMS, BDRYELSEGS, GLOBAL)\n\n"
313   "Internal, fast implementation of mesh-building.  Returned are already\n"
314   "the mesh structure (except the 'e' field) and vertex map returned\n"
315   "from ls_build_mesh, but the arguments are already picked apart by the\n"
316   "wrapper routine.\n\n"
317   "L, M are the size of the grid, COORDS give the coordinates of grid points\n"
318   "in space and INSIDE flags which of those points are inside the domain.\n"
319   "ISPTCOORDS are the coordinates of all intersection points indexed by\n"
320   "intersection point number.  GLOBAL signals that we want a global mesh\n"
321   "instead of a list of meshes for only the inner parts.\n\n"
322   "The remaining arguments contain the relevant information from the\n"
323   "geometry struct picked apart.\n")
324 {
325   try
326     {
327       if (args.length () != 11 || nargout > 2)
328         throw std::runtime_error ("invalid argument counts");
329 
330       /* Get arguments in.  */
331       const unsigned L = args(0).int_value ();
332       const unsigned M = args(1).int_value ();
333       const boolNDArray inside = args(2).bool_array_value ();
334       const Matrix coords = args(3).matrix_value ();
335       const Matrix isptCoords = args(4).matrix_value ();
336       const Matrix nodelist = args(5).matrix_value ();
337       const ColumnVector inElems = args(6).column_vector_value ();
338       const ColumnVector outElems = args(7).column_vector_value ();
339       const ColumnVector bdryElems = args(8).column_vector_value ();
340       const Cell bdryelSegs = args(9).cell_value ();
341       const bool global = args(10).bool_value ();
342 
343       /* Check and extract dimensions.  */
344       getDimension (inside, L * M, 1);
345       getDimension (coords, L * M, 2);
346       const unsigned numIntsec = getDimension (isptCoords, -1, 2);
347       getDimension (nodelist, (L - 1) * (M - 1), 4);
348       const unsigned numInElems = getDimension (inElems, -1, 1);
349       const unsigned numOutElems = getDimension (outElems, -1, 1);
350       const unsigned numBdryElems = getDimension (bdryElems, -1, 1);
351 
352       /* Construct empty mesh.  */
353       Mesh mesh(L * M, numIntsec);
354 
355       /* Construct triangles for inner elements.  */
356       for (unsigned i = 0; i < numInElems; ++i)
357         {
358           const unsigned cur = inElems(i) - 1;
359 
360           unsigned vertInds[4];
361           for (unsigned j = 0; j < 4; ++j)
362             vertInds[j] = mesh.getInnerVertex (nodelist(cur, j) - 1, coords);
363 
364           mesh.addTriangle (vertInds[0], vertInds[1], vertInds[2]);
365           mesh.addTriangle (vertInds[0], vertInds[2], vertInds[3]);
366         }
367 
368       /* In global mode, do that also for outer elements.  */
369       if (global)
370         for (unsigned i = 0; i < numOutElems; ++i)
371           {
372             const unsigned cur = outElems(i) - 1;
373 
374             unsigned vertInds[4];
375             for (unsigned j = 0; j < 4; ++j)
376               vertInds[j] = mesh.getInnerVertex (nodelist(cur, j) - 1, coords);
377 
378             mesh.addTriangle (vertInds[0], vertInds[1], vertInds[2]);
379             mesh.addTriangle (vertInds[0], vertInds[2], vertInds[3]);
380           }
381 
382       /* Go over boundary elements and handle them.  We use the information
383          provided already by ls_find_geometry internally about the segments
384          of each boundary element that belong to the domain.  These need
385          to be split into triangles.  */
386       for (unsigned i = 0; i < numBdryElems; ++i)
387         {
388           const unsigned cur = bdryElems(i) - 1;
389           const Cell cellSegs = bdryelSegs(i).cell_value ();
390           const unsigned nSegs = cellSegs.numel ();
391 
392           std::vector<octave_scalar_map> segs;
393           indexArr endEdges;
394           for (unsigned j = 0; j < nSegs; ++j)
395             {
396               segs.push_back (cellSegs(j).scalar_map_value ());
397               int edge = segs.back ().contents ("endEdge").int_value () - 1;
398               endEdges.push_back (edge);
399             }
400 
401           /* Special case is that of *two* starting points, which
402              indicates that we have a narrow pair.  */
403           if (segs.size () == 2)
404             {
405               assert ((endEdges[0] + 2) % 4 == endEdges[1]);
406 
407               unsigned bdryPts[4];
408               indexArr innerPts[2];
409               for (unsigned j = 0; j < 2; ++j)
410                 {
411                   getInnerSegment (segs[j], bdryPts + 2 * j, innerPts[j]);
412                   assert (innerPts[j].size () == 1);
413 
414                   for (unsigned k = 0; k < 2; ++k)
415                     {
416                       const unsigned ind = 2 * j + k;
417                       bdryPts[ind] = mesh.getBoundaryVertex (bdryPts[ind],
418                                                              isptCoords);
419                     }
420                   for (auto& p : innerPts[j])
421                     p = mesh.getInnerVertex (p, coords);
422 
423                   mesh.addTriangle (bdryPts[2 * j], innerPts[j][0],
424                                     bdryPts[2 * j + 1]);
425                 }
426 
427               if (global)
428                 {
429                   int outerPts[] = {endEdges[0], endEdges[1]};
430                   for (auto& p : outerPts)
431                     {
432                       p = nodelist(cur, p) - 1;
433                       assert (!inside (p));
434                       p = mesh.getInnerVertex (p, coords);
435                     }
436 
437                   mesh.addTriangle (bdryPts[1], outerPts[1], bdryPts[2]);
438                   mesh.addTriangle (bdryPts[0], bdryPts[1], bdryPts[2]);
439                   mesh.addTriangle (bdryPts[0], bdryPts[2], bdryPts[3]);
440                   mesh.addTriangle (bdryPts[0], bdryPts[3], outerPts[0]);
441                 }
442             }
443 
444           /* "Ordinary" case.  */
445           else
446             {
447               assert (segs.size () == 1);
448 
449               /* Find whole list of points forming the segment.  */
450               unsigned bdryPts[2];
451               indexArr innerPts;
452               getInnerSegment (segs[0], bdryPts, innerPts);
453               assert (!innerPts.empty ());
454 
455               indexArr outerPts;
456               for (unsigned p = endEdges[0] % 4; ; p = (p + 3) % 4)
457                 {
458                   const unsigned pInd = nodelist (cur, p) - 1;
459                   if (static_cast<int> (pInd) == innerPts.back ())
460                     break;
461                   assert (!inside (pInd));
462                   outerPts.push_back (pInd);
463                 }
464               assert (innerPts.size () + outerPts.size () == 4);
465 
466               /* Add to list of vertices in the mesh and overwrite the
467                  indices on the way.  */
468               for (auto& p : bdryPts)
469                 p = mesh.getBoundaryVertex (p, isptCoords);
470               for (auto& p : innerPts)
471                 p = mesh.getInnerVertex (p, coords);
472               if (global)
473                 for (auto& p : outerPts)
474                   p = mesh.getInnerVertex (p, coords);
475 
476               /* Build the triangles accordingly and also the list of
477                  boundary edges in this case.  */
478               switch (innerPts.size ())
479                 {
480                   case 1:
481                     mesh.addTriangle (bdryPts[0], innerPts[0], bdryPts[1]);
482                     if (global)
483                       {
484                         mesh.addTriangle (bdryPts[0], outerPts[1], outerPts[0]);
485                         mesh.addTriangle (bdryPts[0], bdryPts[1], outerPts[1]);
486                         mesh.addTriangle (bdryPts[1], outerPts[2], outerPts[1]);
487                       }
488                     break;
489                   case 2:
490                     mesh.addTriangle (bdryPts[0], innerPts[0], innerPts[1]);
491                     mesh.addTriangle (innerPts[1], bdryPts[1], bdryPts[0]);
492                     if (global)
493                       {
494                         mesh.addTriangle (bdryPts[1], outerPts[1], outerPts[0]);
495                         mesh.addTriangle (outerPts[0], bdryPts[0], bdryPts[1]);
496                       }
497                     break;
498                   case 3:
499                     mesh.addTriangle (bdryPts[0], innerPts[0], innerPts[1]);
500                     mesh.addTriangle (innerPts[1], bdryPts[1], bdryPts[0]);
501                     mesh.addTriangle (innerPts[1], innerPts[2], bdryPts[1]);
502                     if (global)
503                       mesh.addTriangle (bdryPts[1], outerPts[0], bdryPts[0]);
504                     break;
505                   default:
506                     assert (false);
507                 }
508             }
509         }
510 
511       /* Build return results.  */
512       octave_scalar_map msh, vertexMap;
513       mesh.toStruct (msh, vertexMap);
514       octave_value_list res;
515       res(0) = msh;
516       res(1) = vertexMap;
517 
518       return res;
519     }
520   catch (const std::runtime_error& exc)
521     {
522       error (exc.what ());
523       return octave_value_list ();
524     }
525 }
526