code cleanup: make bmesh operator names more consistant since python has access to...
[blender.git] / source / blender / bmesh / operators / bmo_symmetrize.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Nicholas Bishop
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 #include "MEM_guardedalloc.h"
24
25 #include "BLI_array.h"
26 #include "BLI_math.h"
27 #include "BLI_utildefines.h"
28
29 #include "bmesh.h"
30 #include "intern/bmesh_operators_private.h"
31
32 enum {
33         SYMM_OUTPUT_GEOM = (1 << 0)
34 };
35
36 /* Note: don't think there's much need to make these user-adjustable? */
37 #define SYMM_AXIS_THRESHOLD 0.00002f
38 #define SYMM_VERT_THRESHOLD 0.00002f
39
40 typedef enum {
41         /* Coordinate lies on the side being copied from */
42         SYMM_SIDE_KEEP,
43         /* Coordinate lies on the side being copied from and within the
44          * axis threshold */
45         SYMM_SIDE_AXIS,
46         /* Coordinate lies on the side being copied to */
47         SYMM_SIDE_KILL
48 } SymmSide;
49
50 typedef struct {
51         BMesh *bm;
52         BMOperator *op;
53
54         int axis;
55         BMO_SymmDirection direction;
56
57         /* Maps from input vertices to their mirrors. If the vertex
58          * doesn't have a mirror, it's not in this map. If the vertex is
59          * within the axis threshold, it's mapped to itself. */
60         GHash *vert_symm_map;
61
62         /* Edges that cross the symmetry plane and are asymmetric get
63          * split. This map goes from input edges to output vertices. If an
64          * edge is not split, it's not in this map. */
65         GHash *edge_split_map;
66 } Symm;
67
68 /* Return which side the coordinate lies on */
69 static SymmSide symm_co_side(const Symm *symm,
70                              const float *co)
71 {
72         float comp = co[symm->axis];
73         if (ELEM3(symm->direction,
74                   BMO_SYMMETRIZE_NEGATIVE_X,
75                   BMO_SYMMETRIZE_NEGATIVE_Y,
76                   BMO_SYMMETRIZE_NEGATIVE_Z))
77         {
78                 comp = -comp;
79         }
80
81         if (comp >= 0) {
82                 if (comp < SYMM_AXIS_THRESHOLD)
83                         return SYMM_SIDE_AXIS;
84                 else
85                         return SYMM_SIDE_KEEP;
86         }
87         else
88                 return SYMM_SIDE_KILL;
89 }
90
91 /* Output vertices and the vert_map array */
92 static void symm_verts_mirror(Symm *symm)
93 {
94         BMOIter oiter;
95         BMVert *src_v, *dst_v;
96
97         symm->vert_symm_map = BLI_ghash_ptr_new(AT);
98
99         BMO_ITER (src_v, &oiter, symm->op->slots_in, "input", BM_VERT) {
100                 SymmSide side = symm_co_side(symm, src_v->co);
101                 float co[3];
102
103                 switch (side) {
104                         case SYMM_SIDE_KEEP:
105                                 /* The vertex is outside the axis area; output its mirror */
106                                 copy_v3_v3(co, src_v->co);
107                                 co[symm->axis] = -co[symm->axis];
108
109                                 dst_v = BM_vert_create(symm->bm, co, src_v);
110                                 BMO_elem_flag_enable(symm->bm, dst_v, SYMM_OUTPUT_GEOM);
111                                 BLI_ghash_insert(symm->vert_symm_map, src_v, dst_v);
112                                 break;
113
114                         case SYMM_SIDE_AXIS:
115                                 /* The vertex is within the axis area, snap to center */
116                                 src_v->co[symm->axis] = 0;
117                                 /* Vertex isn't copied, map to itself */
118                                 BLI_ghash_insert(symm->vert_symm_map, src_v, src_v);
119                                 break;
120
121                         case SYMM_SIDE_KILL:
122                                 /* The vertex does not lie in the half-space being
123                                  * copied from, nothing to do */
124                                 break;
125                 }
126         }
127 }
128
129 static int symm_edge_crosses_axis(const Symm *symm, const BMEdge *e)
130 {
131         const int sides[2] = {symm_co_side(symm, e->v1->co),
132                                   symm_co_side(symm, e->v2->co)};
133
134         return ((sides[0] != SYMM_SIDE_AXIS) &&
135                 (sides[1] != SYMM_SIDE_AXIS) &&
136                 (sides[0] != sides[1]));
137 }
138
139 /* Output edge split vertices for asymmetric edges and the edge_splits
140  * mapping array */
141 static void symm_split_asymmetric_edges(Symm *symm)
142 {
143         BMOIter oiter;
144         BMEdge *e;
145
146         symm->edge_split_map = BLI_ghash_ptr_new(AT);
147
148         BMO_ITER (e, &oiter, symm->op->slots_in, "input", BM_EDGE) {
149                 float flipped[3];
150
151                 copy_v3_v3(flipped, e->v1->co);
152                 flipped[symm->axis] = -flipped[symm->axis];
153
154                 if (symm_edge_crosses_axis(symm, e) &&
155                     (!compare_v3v3(e->v2->co, flipped, SYMM_VERT_THRESHOLD)))
156                 {
157                         /* Endpoints lie on opposite sides and are asymmetric */
158
159                         BMVert *v;
160                         float lambda = 0, edge_dir[3], co[3];
161                         float plane_co[3][3][3] = {
162                                 /* axis == 0 */
163                                 {{0, 0, 0}, {0, 1, 0}, {0, 0, 1}},
164                                 /* axis == 1 */
165                                 {{0, 0, 0}, {1, 0, 0}, {0, 0, 1}},
166                                 /* axis == 2 */
167                                 {{0, 0, 0}, {1, 0, 0}, {0, 1, 0}},
168                         };
169                         int r;
170
171                         /* Find intersection of edge with symmetry plane */
172                         sub_v3_v3v3(edge_dir, e->v2->co, e->v1->co);
173                         normalize_v3(edge_dir);
174                         r = isect_ray_plane_v3(e->v1->co,
175                                                edge_dir,
176                                                plane_co[symm->axis][0],
177                                                plane_co[symm->axis][1],
178                                                plane_co[symm->axis][2],
179                                                &lambda, TRUE);
180                         BLI_assert(r);
181
182                         madd_v3_v3v3fl(co, e->v1->co, edge_dir, lambda);
183                         co[symm->axis] = 0;
184
185                         /* Edge is asymmetric, split it with a new vertex */
186                         v = BM_vert_create(symm->bm, co, e->v1);
187                         BMO_elem_flag_enable(symm->bm, v, SYMM_OUTPUT_GEOM);
188                         BLI_ghash_insert(symm->edge_split_map, e, v);
189                 }
190         }
191 }
192
193 static void symm_mirror_edges(Symm *symm)
194 {
195         BMOIter oiter;
196         BMEdge *e;
197
198         BMO_ITER (e, &oiter, symm->op->slots_in, "input", BM_EDGE) {
199                 BMVert *v1 = NULL, *v2 = NULL;
200                 BMEdge *e_new;
201
202                 v1 = BLI_ghash_lookup(symm->vert_symm_map, e->v1);
203                 v2 = BLI_ghash_lookup(symm->vert_symm_map, e->v2);
204
205                 if (v1 && v2) {
206                         e_new = BM_edge_create(symm->bm, v1, v2, e, TRUE);
207                         BMO_elem_flag_enable(symm->bm, e_new, SYMM_OUTPUT_GEOM);
208                 }
209                 else if (v1 || v2) {
210                         if (BLI_ghash_haskey(symm->edge_split_map, e)) {
211                                 BMVert *v_split = BLI_ghash_lookup(symm->edge_split_map, e);
212
213                                 /* Output the keep side of the split edge */
214                                 if (!v1) {
215                                         e_new = BM_edge_create(symm->bm, v_split, e->v2, e, TRUE);
216                                         BMO_elem_flag_enable(symm->bm, e_new, SYMM_OUTPUT_GEOM);
217                                         v1 = v_split;
218                                 }
219                                 else {
220                                         e_new = BM_edge_create(symm->bm, e->v1, v_split, e, TRUE);
221                                         BMO_elem_flag_enable(symm->bm, e_new, SYMM_OUTPUT_GEOM);
222                                         v2 = v_split;
223                                 }
224
225                                 /* Output the kill side of the split edge */
226                                 e_new = BM_edge_create(symm->bm, v1, v2, e, TRUE);
227                                 BMO_elem_flag_enable(symm->bm, e_new, SYMM_OUTPUT_GEOM);
228                         }
229                 }
230         }
231 }
232
233 /****************************** SymmPoly ******************************/
234
235 typedef struct {
236         /* Indices into the source mvert array (or -1 if not in that array) */
237         BMVert **src_verts;
238         /* Indices into the destination mvert array, these are vertices
239          * created by an edge split (-1 for vertices not created by edge
240          * split) */
241         BMVert **edge_verts;
242
243         /* Number of vertices in the polygon */
244         int len;
245
246         /* True only if none of the polygon's edges were split */
247         int already_symmetric;
248 } SymmPoly;
249
250 static void symm_poly_with_splits(const Symm *symm,
251                                   BMFace *f,
252                                   SymmPoly *out)
253 {
254         BMIter iter;
255         BMLoop *l;
256         int i;
257
258         /* Count vertices and check for edge splits */
259         out->len = f->len;
260         out->already_symmetric = TRUE;
261         BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
262                 if (BLI_ghash_haskey(symm->edge_split_map, l->e)) {
263                         out->len++;
264                         out->already_symmetric = FALSE;
265                 }
266         }
267
268         i = 0;
269         BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
270                 BMVert *split = BLI_ghash_lookup(symm->edge_split_map, l->e);
271
272                 out->src_verts[i] = l->v;
273                 out->edge_verts[i] = NULL;
274                 i++;
275
276                 if (split) {
277                         out->src_verts[i] = NULL;
278                         out->edge_verts[i] = split;
279                         i++;
280                 }
281         }
282 }
283
284 static const float *symm_poly_co(const SymmPoly *sp, int v)
285 {
286         if (sp->src_verts[v])
287                 return sp->src_verts[v]->co;
288         else if (sp->edge_verts[v])
289                 return sp->edge_verts[v]->co;
290         else
291                 return NULL;
292 }
293
294 static SymmSide symm_poly_co_side(const Symm *symm,
295                                   const SymmPoly *sp,
296                                   int v)
297 {
298         return symm_co_side(symm, symm_poly_co(sp, v));
299 }
300
301 /* Return the index of the vertex in the destination array at corner
302  * 'v' of the polygon, or -1 if not in that array */
303 static BMVert *symm_poly_dst(const SymmPoly *sp, int v)
304 {
305         if (sp->edge_verts[v])
306                 return sp->edge_verts[v];
307         else if (sp->src_verts[v])
308                 return sp->src_verts[v];
309         else
310                 return NULL;
311 }
312
313 /* Same as above, but returns the index of the mirror if available, or
314  * the same index if on the axis, or -1 otherwise */
315 static BMVert *symm_poly_mirror_dst(const Symm *symm,
316                                     const SymmPoly *sp,
317                                     int v)
318 {
319         if (sp->edge_verts[v])
320                 return sp->edge_verts[v];
321         else if (sp->src_verts[v]) {
322                 if (BLI_ghash_haskey(symm->vert_symm_map, sp->src_verts[v]))
323                         return BLI_ghash_lookup(symm->vert_symm_map, sp->src_verts[v]);
324                 else
325                         return sp->src_verts[v];
326         }
327         else
328                 return NULL;
329 }
330
331 static int symm_poly_next_crossing(const Symm *symm,
332                                    const SymmPoly *sp,
333                                    int start,
334                                    int *l1,
335                                    int *l2)
336 {
337         int i;
338
339         for (i = 0; i < sp->len; i++) {
340                 (*l1) = (start + i) % sp->len;
341                 (*l2) = ((*l1) + 1) % sp->len;
342
343                 if ((symm_poly_co_side(symm, sp, *l1) == SYMM_SIDE_KILL) ^
344                     (symm_poly_co_side(symm, sp, *l2) == SYMM_SIDE_KILL))
345                 {
346                         return TRUE;
347                 }
348         }
349
350         BLI_assert(!"symm_poly_next_crossing failed");
351         return FALSE;
352 }
353
354 static BMFace *symm_face_create_v(BMesh *bm, BMVert **fv, BMEdge **fe, int len)
355 {
356         BMFace *f_new;
357         int i;
358
359         for (i = 0; i < len; i++) {
360                 int j = (i + 1) % len;
361                 fe[i] = BM_edge_exists(fv[i], fv[j]);
362                 if (!fe[i]) {
363                         fe[i] = BM_edge_create(bm, fv[i], fv[j], NULL, FALSE);
364                         BMO_elem_flag_enable(bm, fe[i], SYMM_OUTPUT_GEOM);
365                 }
366         }
367         f_new = BM_face_create(bm, fv, fe, len, TRUE);
368         BM_face_select_set(bm, f_new, TRUE);
369         BMO_elem_flag_enable(bm, f_new, SYMM_OUTPUT_GEOM);
370         return f_new;
371 }
372
373 static void symm_mesh_output_poly_zero_splits(Symm *symm,
374                                               SymmPoly *sp,
375                                               BMVert **fv,
376                                               BMEdge **fe,
377                                               int segment_len,
378                                               int start)
379 {
380         int i, j;
381
382         j = 0;
383
384         /* Output the keep side of the input polygon */
385         for (i = 0; i < segment_len; i++) {
386                 const int offset = (start + i) % sp->len;
387                 BLI_assert(sp->src_verts[offset]);
388                 fv[j++] = sp->src_verts[offset];
389         }
390
391         /* Output the kill side of the polygon */
392         for (i = segment_len - 1; i >= 0; i--) {
393                 const int offset = (start + i) % sp->len;
394
395                 if (symm_poly_co_side(symm, sp, offset) == SYMM_SIDE_KEEP) {
396                         BLI_assert(sp->src_verts[offset]);
397                         fv[j++] = BLI_ghash_lookup(symm->vert_symm_map,
398                                                    sp->src_verts[offset]);
399                 }
400         }
401
402         symm_face_create_v(symm->bm, fv, fe, j);
403 }
404
405 static void symm_mesh_output_poly_with_splits(Symm *symm,
406                                               SymmPoly *sp,
407                                               BMVert **fv,
408                                               BMEdge **fe,
409                                               int segment_len,
410                                               int start)
411 {
412         int i;
413
414         /* Output the keep side of the input polygon */
415
416         for (i = 0; i < segment_len; i++) {
417                 const int offset = (start + i) % sp->len;
418                 BMVert *v = symm_poly_dst(sp, offset);
419
420                 BLI_assert(v);
421
422                 fv[i] = v;
423         }
424
425         symm_face_create_v(symm->bm, fv, fe, segment_len);
426
427         /* Output the kill side of the input polygon */
428
429         for (i = 0; i < segment_len; i++) {
430                 const int offset = (start + i) % sp->len;
431                 BMVert *v = symm_poly_mirror_dst(symm, sp, offset);
432
433                 fv[segment_len - i - 1] = v;
434
435         }
436
437         symm_face_create_v(symm->bm, fv, fe, segment_len);
438 }
439
440 static void symm_mirror_polygons(Symm *symm)
441 {
442         BMOIter oiter;
443         BMFace *f;
444         BMVert **pv = NULL;
445         BMVert **fv = NULL;
446         BMEdge **fe = NULL;
447         BLI_array_declare(pv);
448         BLI_array_declare(fv);
449         BLI_array_declare(fe);
450
451         BMO_ITER (f, &oiter, symm->op->slots_in, "input", BM_FACE) {
452                 BMIter iter;
453                 BMLoop *l;
454                 int mirror_all = TRUE, ignore_all = TRUE;
455
456                 /* Check if entire polygon can be mirrored or ignored */
457                 BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
458                         const SymmSide side = symm_co_side(symm, l->v->co);
459                         if (side == SYMM_SIDE_KILL)
460                                 mirror_all = FALSE;
461                         else if (side == SYMM_SIDE_KEEP)
462                                 ignore_all = FALSE;
463                 }
464
465                 if (mirror_all) {
466                         int i;
467
468                         /* Make a mirrored copy of the polygon */
469
470                         BLI_array_empty(fv);
471                         BLI_array_empty(fe);
472                         BLI_array_grow_items(fv, f->len);
473                         BLI_array_grow_items(fe, f->len);
474
475                         i = f->len;
476                         BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
477                                 i--;
478
479                                 if (symm_co_side(symm, l->v->co) == SYMM_SIDE_KEEP)
480                                         fv[i] = BLI_ghash_lookup(symm->vert_symm_map, l->v);
481                                 else
482                                         fv[i] = l->v;
483                         }
484
485                         symm_face_create_v(symm->bm, fv, fe, f->len);
486                 }
487                 else if (ignore_all) {
488                         BM_face_kill(symm->bm, f);
489                 }
490                 else {
491                         SymmPoly sp;
492                         int l1, l2, l3, l4;
493                         int double_l2, double_l3;
494                         int segment_len;
495
496                         BLI_array_empty(pv);
497                         BLI_array_grow_items(pv, f->len * 4);
498                         sp.src_verts = pv;
499                         sp.edge_verts = pv + f->len * 2;
500                         symm_poly_with_splits(symm, f, &sp);
501
502                         /* Find first loop edge crossing the axis */
503                         symm_poly_next_crossing(symm, &sp, 0, &l1, &l2);
504
505                         /* If crossing isn't kill to keep, find the next one */
506                         if (symm_poly_co_side(symm, &sp, l1) != SYMM_SIDE_KILL) {
507                                 symm_poly_next_crossing(symm, &sp, l2, &l1, &l2);
508                         }
509
510                         /* Find next crossing (keep to kill) */
511                         symm_poly_next_crossing(symm, &sp, l2, &l3, &l4);
512
513                         if (l2 == l3)
514                                 segment_len = 0;
515                         else if (l2 < l3)
516                                 segment_len = l3 - l2 + 1;
517                         else
518                                 segment_len = (sp.len - l2 + 1) + l3;
519
520                         double_l2 = symm_poly_co_side(symm, &sp, l2) == SYMM_SIDE_KEEP;
521                         double_l3 = symm_poly_co_side(symm, &sp, l3) == SYMM_SIDE_KEEP;
522
523                         /* Calculate number of new polygons/loops */
524                         if (segment_len == 0) {
525                         }
526                         else if (sp.already_symmetric) {
527                                 int new_loops;
528
529                                 if (double_l2 && double_l3)
530                                         new_loops = segment_len * 2;
531                                 else if (!double_l2 && !double_l3)
532                                         new_loops = segment_len * 2 - 2;
533                                 else
534                                         new_loops = segment_len * 2 - 1;
535
536                                 BLI_array_empty(fv);
537                                 BLI_array_empty(fe);
538                                 BLI_array_grow_items(fv, new_loops);
539                                 BLI_array_grow_items(fe, new_loops);
540
541                                 symm_mesh_output_poly_zero_splits(symm, &sp,
542                                                                   fv, fe,
543                                                                   segment_len, l2);
544                                 BM_face_kill(symm->bm, f);
545                         }
546                         else if (!double_l2 && !double_l3) {
547                                 BLI_array_empty(fv);
548                                 BLI_array_empty(fe);
549                                 BLI_array_grow_items(fv, segment_len);
550                                 BLI_array_grow_items(fe, segment_len);
551
552                                 symm_mesh_output_poly_with_splits(symm, &sp,
553                                                                   fv, fe,
554                                                                   segment_len,
555                                                                   l2);
556
557                                 BM_face_kill(symm->bm, f);
558                         }
559                         else {
560                                 BLI_array_empty(fv);
561                                 BLI_array_empty(fe);
562                                 BLI_array_grow_items(fv, segment_len);
563                                 BLI_array_grow_items(fe, segment_len);
564
565                                 symm_mesh_output_poly_with_splits(symm, &sp,
566                                                                   fv, fe,
567                                                                   segment_len,
568                                                                   l2);
569
570                                 BM_face_kill(symm->bm, f);
571
572                                 /* Output bridge triangle */
573
574                                 BLI_array_empty(fv);
575                                 BLI_array_empty(fe);
576                                 BLI_array_grow_items(fv, 3);
577                                 BLI_array_grow_items(fe, 3);
578
579                                 if (double_l2) {
580                                         fv[0] = symm_poly_dst(&sp, l2);
581                                         fv[1] = symm_poly_mirror_dst(symm, &sp, l2);
582                                         fv[2] = symm_poly_dst(&sp, l3);
583                                 }
584                                 else if (double_l3) {
585                                         fv[0] = symm_poly_dst(&sp, l3);
586                                         fv[1] = symm_poly_mirror_dst(symm, &sp, l3);
587                                         fv[2] = symm_poly_dst(&sp, l2);
588                                 }
589
590                                 BLI_assert(fv[0] && fv[1] && fv[2]);
591
592                                 symm_face_create_v(symm->bm, fv, fe, 3);
593                         }
594                 }
595         }
596
597         BLI_array_free(pv);
598         BLI_array_free(fv);
599         BLI_array_free(fe);
600 }
601
602 /* Remove unused edges and vertices from the side being copied to */
603 static void symm_kill_unused(Symm *symm)
604 {
605         BMOIter oiter;
606         BMEdge *e;
607         BMVert *v;
608
609         /* Kill unused edges */
610         BMO_ITER (e, &oiter, symm->op->slots_in, "input", BM_EDGE) {
611                 const int crosses = symm_edge_crosses_axis(symm, e);
612                 const int symmetric = (crosses &&
613                                        (!BLI_ghash_haskey(symm->edge_split_map, e)));
614
615                 if (((symm_co_side(symm, e->v1->co) == SYMM_SIDE_KILL) ||
616                      (symm_co_side(symm, e->v2->co) == SYMM_SIDE_KILL)) &&
617                     !symmetric)
618                 {
619                         /* The edge might be used by a face outside the input set */
620                         if (BM_edge_is_wire(e))
621                                 BM_edge_kill(symm->bm, e);
622                 }
623         }
624
625         /* Kill unused vertices */
626         BMO_ITER (v, &oiter, symm->op->slots_in, "input", BM_VERT) {
627                 if (symm_co_side(symm, v->co) == SYMM_SIDE_KILL) {
628                         if (BM_vert_edge_count(v) == 0)
629                                 BM_vert_kill(symm->bm, v);
630                 }
631         }
632 }
633
634 void bmo_symmetrize_exec(BMesh *bm, BMOperator *op)
635 {
636         Symm symm;
637         BMO_SymmDirection direction = BMO_slot_int_get(op->slots_in, "direction");
638
639         symm.bm = bm;
640         symm.op = op;
641         symm.axis = (ELEM(direction,
642                           BMO_SYMMETRIZE_NEGATIVE_X,
643                           BMO_SYMMETRIZE_POSITIVE_X) ? 0 :
644                      ELEM(direction,
645                           BMO_SYMMETRIZE_NEGATIVE_Y,
646                           BMO_SYMMETRIZE_POSITIVE_Y) ? 1 :
647                      ELEM(direction,
648                           BMO_SYMMETRIZE_NEGATIVE_Z,
649                           BMO_SYMMETRIZE_POSITIVE_Z) ? 2 : 0);
650         symm.direction = direction;
651
652         symm_verts_mirror(&symm);
653         symm_split_asymmetric_edges(&symm);
654         symm_mirror_edges(&symm);
655         symm_mirror_polygons(&symm);
656         symm_kill_unused(&symm);
657
658         BLI_ghash_free(symm.vert_symm_map, NULL, NULL);
659         BLI_ghash_free(symm.edge_split_map, NULL, NULL);
660
661         BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "geom.out",
662                                           BM_ALL, SYMM_OUTPUT_GEOM);
663 }