diff --git a/Common/Context.h b/Common/Context.h index c9b11a9280659b6f83a21e00319f4ba41f15e810..a14ed6b68b6f90c16405ac3f793258c2ff61a6ff 100644 --- a/Common/Context.h +++ b/Common/Context.h @@ -154,7 +154,7 @@ class Context_T { int lc_from_points, lc_from_curvature, lc_extend_from_boundary; int dual, voronoi, draw_skin_only; int light, light_two_side, light_lines; - int format, nb_smoothing, algo2d, algo3d, algo_recombine; + int format, nb_smoothing, algo2d, algo3d, algo_subdivide; int order, second_order_linear, second_order_incomplete; int second_order_experimental, mesh_only_visible; int smooth_internal_edges, c1_continuity; diff --git a/Common/DefaultOptions.h b/Common/DefaultOptions.h index 174e2ba3c1a4b58c54040043b1ce3f8c9d02cdcf..a403e9fe63a00648632b354107efcf6a224f9435 100644 --- a/Common/DefaultOptions.h +++ b/Common/DefaultOptions.h @@ -1079,8 +1079,6 @@ StringXNumber MeshOptions_Number[] = { "Random factor used in 2D and 3D meshing algorithm (test other values when the algorithm fails)" }, { F|O, "RefineSteps" , opt_mesh_refine_steps , 10 , "Number of refinement steps in the MeshAdapt-based 2D algorithms" }, - { F|O, "RecombineAlgo" , opt_mesh_recombine_algo , 1 , - "Recombine algorithm (1=mixed triangles-quadrangles, 2=all quadrangles)" }, { F|O, "ReverseAllNormals" , opt_mesh_reverse_all_normals , 0. , "Reverse all the mesh normals (for display)" }, @@ -1104,6 +1102,8 @@ StringXNumber MeshOptions_Number[] = { "Number of smoothing steps of internal edges for high order meshes" }, { F|O, "SmoothNormals" , opt_mesh_smooth_normals , 0. , "Smooth the mesh normals?" }, + { F|O, "SubdivisionAlgorithm" , opt_mesh_algo_subdivide , 0 , + "Mesh subdivision algorithm (0=none, 1=all quadrangles, 2=all hexahedra)" }, { F|O, "SurfaceEdges" , opt_mesh_surfaces_edges , 1. , "Display edges of surface mesh?" }, { F|O, "SurfaceFaces" , opt_mesh_surfaces_faces , 0. , diff --git a/Common/Gmsh.cpp b/Common/Gmsh.cpp index 7f645c1a6170c315fe9cd1d9696e32276d55bd79..85988d59feef5c618c15ad3f09df58dcaefe99ee 100644 --- a/Common/Gmsh.cpp +++ b/Common/Gmsh.cpp @@ -124,7 +124,7 @@ int GmshBatch() else if(CTX.batch == 4) AdaptMesh(GModel::current()); else if(CTX.batch == 5) - RefineMesh(GModel::current()); + RefineMesh(GModel::current(), CTX.mesh.second_order_linear); #if defined(HAVE_CHACO) || defined(HAVE_METIS) if(CTX.batch_after_mesh == 1) PartitionMesh(GModel::current(), CTX.mesh.partition_options); diff --git a/Common/Options.cpp b/Common/Options.cpp index d5b8bf69ed9eab677b50d9202913683f68de78f9..b3b9d42ee7162de511b1428ae1ec4646ba832a3c 100644 --- a/Common/Options.cpp +++ b/Common/Options.cpp @@ -5313,19 +5313,19 @@ double opt_mesh_algo2d(OPT_ARGS_NUM) return CTX.mesh.algo2d; } -double opt_mesh_recombine_algo(OPT_ARGS_NUM) +double opt_mesh_algo_subdivide(OPT_ARGS_NUM) { if(action & GMSH_SET){ - CTX.mesh.algo_recombine = (int)val; - if(CTX.mesh.algo_recombine < 1 && CTX.mesh.algo_recombine > 2) - CTX.mesh.algo_recombine = 1; + CTX.mesh.algo_subdivide = (int)val; + if(CTX.mesh.algo_subdivide < 0 && CTX.mesh.algo_subdivide > 2) + CTX.mesh.algo_subdivide = 0; } #if defined(HAVE_FLTK) if(GUI::available() && (action & GMSH_GUI)) { - GUI::instance()->options->mesh.choice[5]->value(CTX.mesh.algo_recombine - 1); + GUI::instance()->options->mesh.choice[5]->value(CTX.mesh.algo_subdivide); } #endif - return CTX.mesh.algo_recombine; + return CTX.mesh.algo_subdivide; } double opt_mesh_algo3d(OPT_ARGS_NUM) diff --git a/Common/Options.h b/Common/Options.h index da3105052cd0534b554e0e52f0c89d169c27e599..5fba73b448cf9786fc18d859d5ca26b593c67c7e 100644 --- a/Common/Options.h +++ b/Common/Options.h @@ -484,7 +484,7 @@ double opt_mesh_bdf_field_format(OPT_ARGS_NUM); double opt_mesh_nb_smoothing(OPT_ARGS_NUM); double opt_mesh_algo2d(OPT_ARGS_NUM); double opt_mesh_algo3d(OPT_ARGS_NUM); -double opt_mesh_recombine_algo(OPT_ARGS_NUM); +double opt_mesh_algo_subdivide(OPT_ARGS_NUM); double opt_mesh_mesh_only_visible(OPT_ARGS_NUM); double opt_mesh_min_circ_points(OPT_ARGS_NUM); double opt_mesh_allow_swap_edge_angle(OPT_ARGS_NUM); diff --git a/Fltk/optionWindow.cpp b/Fltk/optionWindow.cpp index de02bbc4effd2576be55dcd3f2e33b3aa5d95af4..026df7addea25a94ed7fb87cee9cf3caa592f57f 100644 --- a/Fltk/optionWindow.cpp +++ b/Fltk/optionWindow.cpp @@ -447,8 +447,7 @@ static void mesh_options_ok_cb(Fl_Widget *w, void *data) opt_mesh_algo3d(0, GMSH_SET, (o->mesh.choice[3]->value() == 0) ? ALGO_3D_TETGEN_DELAUNAY : ALGO_3D_NETGEN); - opt_mesh_recombine_algo(0, GMSH_SET, - (o->mesh.choice[5]->value() == 0) ? 1 : 2); + opt_mesh_algo_subdivide(0, GMSH_SET, o->mesh.choice[5]->value()); opt_mesh_color_carousel(0, GMSH_SET, o->mesh.choice[4]->value()); opt_mesh_quality_type(0, GMSH_SET, o->mesh.choice[6]->value()); opt_mesh_label_type(0, GMSH_SET, o->mesh.choice[7]->value()); @@ -1889,9 +1888,10 @@ optionWindow::optionWindow(int deltaFontSize) {"Netgen", 0, 0, 0}, {0} }; - static Fl_Menu_Item menu_recombine_algo[] = { - {"Mixed Tri-Quads", 0, 0, 0}, + static Fl_Menu_Item menu_subdivision_algo[] = { + {"None", 0, 0, 0}, {"All Quads", 0, 0, 0}, + {"All Hexas", 0, 0, 0}, {0} }; @@ -1908,8 +1908,8 @@ optionWindow::optionWindow(int deltaFontSize) mesh.choice[3]->callback(mesh_options_ok_cb); mesh.choice[5] = new Fl_Choice - (L + 2 * WB, 2 * WB + 3 * BH, IW, BH, "Recombine algorithm"); - mesh.choice[5]->menu(menu_recombine_algo); + (L + 2 * WB, 2 * WB + 3 * BH, IW, BH, "Subdivision algorithm"); + mesh.choice[5]->menu(menu_subdivision_algo); mesh.choice[5]->align(FL_ALIGN_RIGHT); mesh.choice[5]->callback(mesh_options_ok_cb); diff --git a/Geo/GRegion.h b/Geo/GRegion.h index f3e5d352428b0e9fb63908a2cfc41e5994984e56..3bdf4203c91e49d77cd7a068d4178304f456ac60 100644 --- a/Geo/GRegion.h +++ b/Geo/GRegion.h @@ -67,6 +67,7 @@ class GRegion : public GEntity { virtual void resetMeshAttributes(); struct { + // is this surface meshed using a transfinite interpolation char Method; // the extrusion parameters (if any) ExtrudeParams *extrude; diff --git a/Mesh/Generator.cpp b/Mesh/Generator.cpp index b2a39ab0101a6c43b039a5d52a6d9eaf0c7b1545..aacb75f4686d5572759ea9fc666e3997bc3a029d 100644 --- a/Mesh/Generator.cpp +++ b/Mesh/Generator.cpp @@ -408,13 +408,8 @@ static void Mesh2D(GModel *m) } } - // look if there are model faces for which the "full quad" algo is - // set ON - bool fullQuad = false; - for(GModel::fiter it = m->firstFace() ; it!=m->lastFace(); ++it) - if(CTX.mesh.algo_recombine == 2 && (*it)->quadrangles.size()) - fullQuad = true; - if(fullQuad) RefineMesh(m, false, true); + // use "full quad" subdivision + if(CTX.mesh.algo_subdivide == 1) RefineMesh(m, false, true); // gmshCollapseSmallEdges (*m); @@ -461,6 +456,9 @@ static void Mesh3D(GModel *m) for(unsigned int i = 0; i < connected.size(); i++) MeshDelaunayVolume(connected[i]); + // Use "full hexa" subdivision? + if(CTX.mesh.algo_subdivide == 2) RefineMesh(m, false, false, true); + double t2 = Cpu(); CTX.mesh_timer[2] = t2 - t1; Msg::Info("Mesh 3D complete (%g s)", CTX.mesh_timer[2]); diff --git a/Mesh/Generator.h b/Mesh/Generator.h index 35dabb758258b25356632ff42a61271d67680e2a..b0c7fa6d83c5a8f8677e52af1cf900378120ca15 100644 --- a/Mesh/Generator.h +++ b/Mesh/Generator.h @@ -13,6 +13,7 @@ void AdaptMesh(GModel *m); void GenerateMesh(GModel *m, int dimension); void OptimizeMesh(GModel *m); void OptimizeMeshNetgen(GModel *m); -void RefineMesh(GModel *m, bool linear=true, bool splitTrianglesIntoQuads = false); +void RefineMesh(GModel *m, bool linear, bool splitIntoQuads=false, + bool splitIntoHexas=false); #endif diff --git a/Mesh/HighOrder.h b/Mesh/HighOrder.h index f0fd3cf456e34c1ae9a8595d33dfd845929c6861..fbdaf071e967537c3cb72ebea95e7fdacfe4d85f 100644 --- a/Mesh/HighOrder.h +++ b/Mesh/HighOrder.h @@ -17,7 +17,6 @@ typedef std::map<std::pair<MVertex*, MVertex*>, std::vector<MVertex*> > edgeCont // for each face (a list of vertices) we build a list of vertices that // are the high order representation of the face -// typedef std::map<std::vector<MVertex*>, std::vector<MVertex*>> faceContainer; typedef std::map<MFace, std::vector<MVertex*>, Less_Face> faceContainer; void SetOrder1(GModel *m); diff --git a/Mesh/meshRefine.cpp b/Mesh/meshRefine.cpp index 3240ba1da291b0ab2da659b15c2c029262950b93..899613cbf43a04461f89ec425d6858845052c3af 100644 --- a/Mesh/meshRefine.cpp +++ b/Mesh/meshRefine.cpp @@ -44,10 +44,11 @@ static void Subdivide(GEdge *ge) ge->deleteVertexArrays(); } -static void Subdivide(GFace *gf, bool splitTrianglesIntoQuads) +static void Subdivide(GFace *gf, bool splitIntoQuads, bool splitIntoHexas, + faceContainer &faceVertices) { - if(!splitTrianglesIntoQuads){ + if(!splitIntoQuads && !splitIntoHexas){ std::vector<MTriangle*> triangles2; for(unsigned int i = 0; i < gf->triangles.size(); i++){ MTriangle *t = gf->triangles[i]; @@ -81,27 +82,29 @@ static void Subdivide(GFace *gf, bool splitTrianglesIntoQuads) } delete q; } - if(splitTrianglesIntoQuads){ + if(splitIntoQuads || splitIntoHexas){ for(unsigned int i = 0; i < gf->triangles.size(); i++){ MTriangle *t = gf->triangles[i]; if(t->getNumVertices() == 6){ - SPoint2 pt, temp; - SPoint3 ptx; t->pnt(0.5,0.5,0,ptx); + SPoint2 pt; + SPoint3 ptx; t->pnt(0.5, 0.5, 0, ptx); bool reparamOK = true; - for(int k = 0; k<6; k++){ + for(int k = 0; k < 6; k++){ + SPoint2 temp; reparamOK &= reparamMeshVertexOnFace(t->getVertex(k), gf, temp); - pt[0] += temp[0]/6.; - pt[1] += temp[1]/6.; + pt[0] += temp[0] / 6.; + pt[1] += temp[1] / 6.; } MVertex *newv; if (reparamOK){ GPoint gp = gf->point(pt); - newv = new MFaceVertex (gp.x(),gp.y(),gp.z(),gf,pt[0],pt[1]); + newv = new MFaceVertex(gp.x(), gp.y(), gp.z(), gf, pt[0], pt[1]); } else { - newv = new MVertex (ptx.x(),ptx.y(),ptx.z(),gf); + newv = new MVertex(ptx.x(), ptx.y(), ptx.z(), gf); } gf->mesh_vertices.push_back(newv); + if(splitIntoHexas) faceVertices[t->getFace(0)].push_back(newv); quadrangles2.push_back (new MQuadrangle(t->getVertex(0), t->getVertex(3), newv, t->getVertex(5))); quadrangles2.push_back @@ -120,32 +123,34 @@ static void Subdivide(GFace *gf, bool splitTrianglesIntoQuads) gf->deleteVertexArrays(); } -static void Subdivide(GRegion *gr) +static void Subdivide(GRegion *gr, bool splitIntoHexas, faceContainer &faceVertices) { - std::vector<MTetrahedron*> tetrahedra2; - for(unsigned int i = 0; i < gr->tetrahedra.size(); i++){ - MTetrahedron *t = gr->tetrahedra[i]; - if(t->getNumVertices() == 10){ - tetrahedra2.push_back - (new MTetrahedron(t->getVertex(0), t->getVertex(4), t->getVertex(7), t->getVertex(6))); - tetrahedra2.push_back - (new MTetrahedron(t->getVertex(1), t->getVertex(4), t->getVertex(5), t->getVertex(9))); - tetrahedra2.push_back - (new MTetrahedron(t->getVertex(2), t->getVertex(5), t->getVertex(6), t->getVertex(8))); - tetrahedra2.push_back - (new MTetrahedron(t->getVertex(3), t->getVertex(7), t->getVertex(9), t->getVertex(8))); - tetrahedra2.push_back - (new MTetrahedron(t->getVertex(5), t->getVertex(8), t->getVertex(7), t->getVertex(9))); - tetrahedra2.push_back - (new MTetrahedron(t->getVertex(5), t->getVertex(7), t->getVertex(4), t->getVertex(9))); - tetrahedra2.push_back - (new MTetrahedron(t->getVertex(7), t->getVertex(8), t->getVertex(5), t->getVertex(6))); - tetrahedra2.push_back - (new MTetrahedron(t->getVertex(4), t->getVertex(7), t->getVertex(5), t->getVertex(6))); + if(!splitIntoHexas){ + std::vector<MTetrahedron*> tetrahedra2; + for(unsigned int i = 0; i < gr->tetrahedra.size(); i++){ + MTetrahedron *t = gr->tetrahedra[i]; + if(t->getNumVertices() == 10){ + tetrahedra2.push_back + (new MTetrahedron(t->getVertex(0), t->getVertex(4), t->getVertex(7), t->getVertex(6))); + tetrahedra2.push_back + (new MTetrahedron(t->getVertex(1), t->getVertex(4), t->getVertex(5), t->getVertex(9))); + tetrahedra2.push_back + (new MTetrahedron(t->getVertex(2), t->getVertex(5), t->getVertex(6), t->getVertex(8))); + tetrahedra2.push_back + (new MTetrahedron(t->getVertex(3), t->getVertex(7), t->getVertex(9), t->getVertex(8))); + tetrahedra2.push_back + (new MTetrahedron(t->getVertex(5), t->getVertex(8), t->getVertex(7), t->getVertex(9))); + tetrahedra2.push_back + (new MTetrahedron(t->getVertex(5), t->getVertex(7), t->getVertex(4), t->getVertex(9))); + tetrahedra2.push_back + (new MTetrahedron(t->getVertex(7), t->getVertex(8), t->getVertex(5), t->getVertex(6))); + tetrahedra2.push_back + (new MTetrahedron(t->getVertex(4), t->getVertex(7), t->getVertex(5), t->getVertex(6))); + } + delete t; } - delete t; + gr->tetrahedra = tetrahedra2; } - gr->tetrahedra = tetrahedra2; std::vector<MHexahedron*> hexahedra2; for(unsigned int i = 0; i < gr->hexahedra.size(); i++){ @@ -178,6 +183,85 @@ static void Subdivide(GRegion *gr) } delete h; } + if(splitIntoHexas){ + for(unsigned int i = 0; i < gr->tetrahedra.size(); i++){ + MTetrahedron *t = gr->tetrahedra[i]; + if(t->getNumVertices() == 10){ + std::vector<MVertex*> newv; + for(int j = 0; j < t->getNumFaces(); j++){ + MFace face = t->getFace(j); + faceContainer::iterator fIter = faceVertices.find(face); + if (fIter != faceVertices.end()){ + newv.push_back(fIter->second[0]); + } + else{ + SPoint3 pc = face.barycenter(); + newv.push_back(new MVertex(pc.x(), pc.y(), pc.z(), gr)); + gr->mesh_vertices.push_back(newv.back()); + } + } + SPoint3 pc = t->barycenter(); + newv.push_back(new MVertex(pc.x(), pc.y(), pc.z(), gr)); + gr->mesh_vertices.push_back(newv.back()); + hexahedra2.push_back + (new MHexahedron(t->getVertex(0), t->getVertex(4), newv[0], t->getVertex(6), + t->getVertex(7), newv[1], newv[4], newv[2])); + hexahedra2.push_back + (new MHexahedron(t->getVertex(4), t->getVertex(1), t->getVertex(5), newv[0], + newv[1], t->getVertex(9), newv[3], newv[4])); + hexahedra2.push_back + (new MHexahedron(t->getVertex(6), newv[0], t->getVertex(5), t->getVertex(2), + newv[2], newv[4], newv[3], t->getVertex(8))); + hexahedra2.push_back + (new MHexahedron(t->getVertex(3), t->getVertex(9), newv[1], t->getVertex(7), + t->getVertex(8), newv[3], newv[4], newv[2])); + delete t; + } + } + gr->tetrahedra.clear(); + + for(unsigned int i = 0; i < gr->prisms.size(); i++){ + MPrism *p = gr->prisms[i]; + if(p->getNumVertices() == 18){ + std::vector<MVertex*> newv; + for(int j = 0; j < 2; j++){ + MFace face = p->getFace(j); + faceContainer::iterator fIter = faceVertices.find(face); + if (fIter != faceVertices.end()){ + newv.push_back(fIter->second[0]); + } + else{ + SPoint3 pc = face.barycenter(); + newv.push_back(new MVertex(pc.x(), pc.y(), pc.z(), gr)); + gr->mesh_vertices.push_back(newv.back()); + } + } + SPoint3 pc = p->barycenter(); + newv.push_back(new MVertex(pc.x(), pc.y(), pc.z(), gr)); + gr->mesh_vertices.push_back(newv.back()); + hexahedra2.push_back + (new MHexahedron(p->getVertex(0), p->getVertex(6), newv[0], p->getVertex(7), + p->getVertex(8), p->getVertex(15), newv[2], p->getVertex(16))); + hexahedra2.push_back + (new MHexahedron(p->getVertex(1), p->getVertex(9), newv[0], p->getVertex(6), + p->getVertex(10), p->getVertex(17), newv[2], p->getVertex(15))); + hexahedra2.push_back + (new MHexahedron(p->getVertex(2), p->getVertex(7), newv[0], p->getVertex(9), + p->getVertex(11), p->getVertex(16), newv[2], p->getVertex(17))); + hexahedra2.push_back + (new MHexahedron(p->getVertex(8), p->getVertex(15), newv[2], p->getVertex(16), + p->getVertex(3), p->getVertex(12), newv[1], p->getVertex(13))); + hexahedra2.push_back + (new MHexahedron(p->getVertex(10), p->getVertex(17), newv[2], p->getVertex(15), + p->getVertex(4), p->getVertex(14), newv[1], p->getVertex(12))); + hexahedra2.push_back + (new MHexahedron(p->getVertex(11), p->getVertex(16), newv[2], p->getVertex(17), + p->getVertex(5), p->getVertex(13), newv[1], p->getVertex(14))); + } + } + gr->prisms.clear(); + + } gr->hexahedra = hexahedra2; std::vector<MPrism*> prisms2; @@ -215,9 +299,13 @@ static void Subdivide(GRegion *gr) std::vector<MPyramid*> pyramids2; for(unsigned int i = 0; i < gr->pyramids.size(); i++){ + if(splitIntoHexas){ + Msg::Error("Full hexahedron subdivision is not implemented for pyramids"); + return; + } MPyramid *p = gr->pyramids[i]; if(p->getNumVertices() == 14){ - // BASE + // Base pyramids2.push_back (new MPyramid(p->getVertex(0), p->getVertex(5), p->getVertex(13), p->getVertex(6), p->getVertex(7))); @@ -263,7 +351,7 @@ static void Subdivide(GRegion *gr) gr->deleteVertexArrays(); } -void RefineMesh(GModel *m, bool linear, bool splitTrianglesIntoQuads) +void RefineMesh(GModel *m, bool linear, bool splitIntoQuads, bool splitIntoHexas) { Msg::StatusBar(1, true, "Refining mesh..."); double t1 = Cpu(); @@ -271,15 +359,18 @@ void RefineMesh(GModel *m, bool linear, bool splitTrianglesIntoQuads) // Create 2nd order mesh (using "2nd order complete" elements) to // generate vertex positions SetOrderN(m, 2, linear, false); - + + // only used when splitting tets into hexes + faceContainer faceVertices; + // Subdivide the second order elements to create the refined linear // mesh for(GModel::eiter it = m->firstEdge(); it != m->lastEdge(); ++it) Subdivide(*it); for(GModel::fiter it = m->firstFace(); it != m->lastFace(); ++it) - Subdivide(*it,splitTrianglesIntoQuads); + Subdivide(*it, splitIntoQuads, splitIntoHexas, faceVertices); for(GModel::riter it = m->firstRegion(); it != m->lastRegion(); ++it) - Subdivide(*it); + Subdivide(*it, splitIntoHexas, faceVertices); double t2 = Cpu(); Msg::Info("Mesh refinement complete (%g s)", t2 - t1); diff --git a/benchmarks/2d/embedded_recombine.geo b/benchmarks/2d/embedded_recombine.geo index 2b29fdf0b9eff01feab38f4e4530e002507a9246..be390fab8f5a930c352d8585bec54ccbc75370be 100644 --- a/benchmarks/2d/embedded_recombine.geo +++ b/benchmarks/2d/embedded_recombine.geo @@ -1,5 +1,5 @@ // Options -Mesh.RecombineAlgo = 2 ; +Mesh.SubdivisionAlgorithm = 1; Mesh.MshFileVersion = 1; // Variables diff --git a/doc/TODO.txt b/doc/TODO.txt index 19b8719105c7692cfc092ad98c5ad777f4b2c3b0..cf143278a3978b9c1619791473d723f12b75dd50 100644 --- a/doc/TODO.txt +++ b/doc/TODO.txt @@ -1,4 +1,4 @@ -$Id: TODO.txt,v 1.13 2009-01-04 10:21:55 geuzaine Exp $ +$Id: TODO.txt,v 1.14 2009-01-07 10:59:20 geuzaine Exp $ ******************************************************************** @@ -72,11 +72,6 @@ introduce Right/Left/Alternate for extruded meshes ******************************************************************** -add full-hexa recombine by simply doing barycentric subdivision on -tets - -******************************************************************** - re-implement STL remeshing ******************************************************************** diff --git a/doc/VERSIONS.txt b/doc/VERSIONS.txt index 0cda2c7ca73ab6f2eaaef6f9e7b0eb8e4c8cbc5f..42ad33bd470060406152e903f54b7ce1841849f5 100644 --- a/doc/VERSIONS.txt +++ b/doc/VERSIONS.txt @@ -1,11 +1,11 @@ -$Id: VERSIONS.txt,v 1.29 2009-01-03 08:56:45 geuzaine Exp $ - -2.3.0 (?): restored full-quad recombine algorithm; fixed clipping -planes when more than 32 views are present ({Geometry,Mesh,View}.Clip -replaces General.Clip); modified arrow size and transform options; -improved visibility browser; major graphics and GUI code refactoring; -improved automatic transfinite corner selection (now also for -volumes); many small improvements and small bug fixes. +$Id: VERSIONS.txt,v 1.30 2009-01-07 10:59:20 geuzaine Exp $ + +2.3.0 (?): major graphics and GUI code refactoring; new full-quad/hexa +subdivision algorithm (new option Mesh.SubdivisionAlgorithm; removed +Mesh.RecombineAlgo); improved automatic transfinite corner selection +(now also for volumes); improved visibility browser; modified arrow +size, clipping planes and transform options; many small improvements +and bug fixes all over the place. 2.2.6 (Nov 21, 2008): better transfinite smoothing and automatic corner selection; fixed high order meshing crashes on Windows and