Creating a 2D meshing algorithm in Mathematica

Update

The code from this post is part of the FEMAddOns and can be installed by evaluating

ResourceFunction["FEMAddOnsInstall"][]

End Update

Hm, I am late for the party but anyway here is my entry. This distmesh port works in 2D and 3D (though this has issues I should say) and does not need external code like qhull. It also has a quality control / max steps termination and boundary points to be included can be given. Note, however, this is a prototype code and it has issues. A word about distmesh before the code: distmesh is probably the simplest mesh generator (well if you have a Delaunay code) and produces high quality meshes. The downside, unfortunately, is that the algorithm is slow due to it's repeated re-delaunay-zation.

To understand the code you most likely will need some familiarity with the Person's Distmesh paper.

Preliminary Code:

Some computational geometry stuff:

ddiff[d1_, d2_] := Max[d1, -d2];
dunion[d1_, d2_] := Min[d1, d2];
dintersection[d1_, d2_] := Max[d1, d2];
dcircle[{x_, y_}, {cx_, cy_, r_}] := (x - cx)^2 + (y - cy)^2 - r^2
drectangle[{x_, y_}, {{x1_, y1_}, {x2_, y2_}}] := -Min[Min[Min[-y1 + y, y2 - y], -x1 + x], x2 - x]
dline[{x_,y_}, {{x1_, y1_}, {x2_,y2_}}] := ((x2 - x1)*(y2 - y) - (y2 - y1)*(x2 - x)) / Sqrt[(x2 - x1)^2 + (y2 - y1)^2 ]
dtriangle[{x_, y_}, {a : {x1_, y1_}, b : {x2_, y2_}, c : {x3_, y3_}}] := 
 Max[ dline[{x, y}, {a, b}], dline[{x, y}, {b, c}],dline[{x, y}, {c, a}]]
dpoly[{x_, y_}, a : {{_, _} ..}] := 
 Max @@ (dline[{x, y}, #] & /@ Partition[ a, 2, 1, {1, 1}])

Distmesh helper functions:

(* this is not quite correct, the coords in the mesh may not be the \
same as the ones origianly given as pts; e.g. when pts contain \
duplicate points *)
myDelaunay[ pts_, dim_] := Block[{tmp},
   Switch[ dim,
    2, 
    tmp = Graphics`Region`DelaunayMesh[ pts];
    Developer`ToPackedArray[ tmp["MeshObject"]["MeshElements"]],
    3,
    TetGenDelaunay[ pts][[2]]
    ]
   ];

thresholdPosition[ p_, th_ ] := 
 Flatten[ SparseArray[UnitStep[th], Automatic, 1] /. 
   HoldPattern[SparseArray[c___]] :> {c}[[4, 2, 2]] ]

getElementCoordinates = 
  Compile[{{coords, _Real, 2}, {part, _Integer, 1}}, 
   Compile`GetElement[coords, #] & /@ part, 
   RuntimeAttributes -> Listable];

mkCompileCode[vars_, code_, idx_] := 
 With[{fVars = Flatten[vars]}, Compile[{{in, _Real, idx}},
   Block[fVars,
    fVars = Flatten[ in];
    code]
   , RuntimeAttributes -> Listable]
  ]

mkcForces[ chCode_] := Compile[{{p, _Real, 2}, {bars, _Integer, 2}, {fScale, _Real, 0}},
  Block[{barVec, len, hBars, l0, force, forceVec},
   barVec = p[[ bars[[ All, 1]] ]] - p[[ bars[[ All, 2 ]] ]];
   len = Sqrt[ Plus @@@ Power[ barVec, 2]];
   hBars = chCode /@ ((( Plus @@ p[[#]] ) & /@ bars )/2 );
   l0 = hBars*fScale*
     Sqrt[ (Plus @@ Power[ len, 2 ]) /( Plus @@ Power[ hBars, 2 ]) ];
   force  = Max[ #, 0. ] & /@ (l0 - len);
   forceVec = Transpose[ { #, -# } ] &[ (force / len ) * barVec ]
   ]
  ]

moveMeshPoints[ p_, bars_, cForces_, fScale_: 1.2 ] := Module[
  { totForce, forceVec},
  forceVec = cForces[ p, bars, fScale];
  totForce = 
   NDSolve`FEM`AssembleMatrix[ {bars, 
     ConstantArray[ Range[ Last[ Dimensions[p]]], {Length[ bars]}]}, 
    Flatten[ forceVec, {{1}, {2, 3}} ], Dimensions[p]];
  totForce
  ]

meshbarPlot[p_, bar_List] := 
 Print[Show[{gr, 
    Graphics[Line /@ (Part[p, #] & /@ bar), AspectRatio -> Automatic, 
     PlotRange -> All]}]]

initialMeshPoints[ {{x1_, y1_}, {x2_, y2_} }, h0_ ] := Module[
   { xRow, yColumn, tmp },
   xRow = Range[ x1, x2, h0 ];
   yColumn = Range[ y1, y2, h0 *Sqrt[3]/2 ];
   tmp = Transpose[ {
      Flatten[ 
       Transpose[ 
        Table[ If[ OddQ[ i ], xRow, xRow + h0/2], { i, 
          Length[ yColumn ] } ] ] ], 
      Flatten[ Table[ yColumn, { Length[ xRow ] } ] ]
      } ];
   Developer`ToPackedArray@tmp
   ];

initialMeshPoints[ {{x1_, y1_, z1_}, {x2_, y2_, z2_} }, h0_ ] := 
  Module[
   { xP, yP, zP },
   xP = Range[ x1, x2, h0 ];
   yP = Range[ y1, y2, h0 ];
   zP = Range[ z1, z2, h0 ];
   Flatten[ Outer[List, xP, yP, zP], 2]
   ];

Quality control functions [What is a good linear finite element]:

edgeLength[c : {{_, _} ..}] := EuclideanDistance @@@ Partition[c, 2, 1, 1];

TriangleAreaSymbolic[{c1_, c2_, c3_}] := 1/2*Det[Transpose[{c1, c2}] - c3];
triangleEdgeLength := edgeLength;
tri = Array[Compile`GetElement[X, ##] &, {3, 2}];
source = 4*Sqrt[3]* TriangleAreaSymbolic[tri] / Total[triangleEdgeLength[tri]^2];

meshQuality[triangle] = 
  With[{code = source}, 
   Compile[{{in, _Real, 2}}, Block[{X}, X = in; code], 
    RuntimeAttributes -> Listable]];

(*Shewchuk vertex ordering is a_,b_,c_,d_*)
(*this is to acomodate the mesh element ordering*)
TetrahedronVolumeSymbolic[{a_, c_, b_, d_}] := 
  1/6*Det[Transpose[{a, b, c}] - d];
tetEdgeLength[{c1_, c2_, c3_, c4_}] := 
 Norm[#, 2] & /@ {c1 - c2, c2 - c3, c3 - c1, c1 - c4, c2 - c4, c3 - c4}
tet = Array[Compile`GetElement[X, ##] &, {4, 3}];
source = 6*Sqrt[2]*
   TetrahedronVolumeSymbolic[tet]/
    Sqrt[(1/6*Total[tetEdgeLength[tet]^2])]^3;

meshQuality[tetrahedra] = 
  With[{code = source}, 
   Compile[{{in, _Real, 2}}, Block[{X}, X = in; code], 
    RuntimeAttributes -> Listable]];

Distmesh:

Options[distmesh] = {MaxIterations -> 150, 
   MeshElementQualityFactor -> 1/2, 
   MeshElementQualityFunction -> Min, ReturnBestMeshSofar -> True};

distmesh[ df_, rdf_, h0_, vars_, bbox_, pfix_, opts___ ] := Module[
  {fopts, maxIterations, iterationNumber, meshQualityFactor, 
   meshQualityFunction,
   dim,
   p, ix, deps, cdfs, d,
   dgradDim, dgradN, depsIM, dgrad,
   pMid, t, bars, geps,
   crdf, cForces,
   mq, mqMax, elementType,
   symElementCoords, qualityCode, cMeshQuality,
   divisor, barSelectors,
   saveBestMeshQ,
   tBest, pBest,
   dptol, ttol, fScale, deltat, r0, n, pOld, tmp, scaledR0, totForce, 
   dgradx, dgrady, dgrad2},

  dim = Length[ vars];

  fopts = FilterRules[{opts}, Options[distmesh]];

  {maxIterations, meshQualityFactor, meshQualityFunction, 
    saveBestMeshQ} =
   {MaxIterations, MeshElementQualityFactor, 
      MeshElementQualityFunction, ReturnBestMeshSofar} /. 
     fopts /. Options[distmesh];

  meshQualityFactor = Min[1, Max[0, meshQualityFactor]];
  iterationNumber = 0;
  mqMax = 0;

  dptol = .001;
  ttol = .1;
  fScale = 1.2;
  deltat = .2;
  geps = .001*h0;

  deps = Sqrt[10^-$MachinePrecision]*h0;

  (* compile the distance set fuction *)

  cdfs = mkCompileCode[vars, df, 1];

  (* compile raltive mesh coarsens function *)

  crdf = mkCompileCode[vars, rdf, 1];
  cForces = mkcForces[crdf];

  (* select the mesh quality code *)
  Switch[ dim,
   2,
   elementType = Global`triangle;
   divisor = 3;
   barSelectors = {{1, 2}, {2, 3}, {3, 1}};
   ,
   3,
   elementType = Global`tetrahedra;
   divisor = 4;
   barSelectors = {{1, 2}, {2, 3}, {3, 1}, {1, 4}, {2, 4}, {3, 4}}
   ];
  cMeshQuality = meshQuality[elementType];

  p = initialMeshPoints[bbox, h0];
  (* p = Select[ p, ( fd @@ #<geps)& ]; *)

  p = p[[thresholdPosition[p, cdfs[p] - geps]]];

  r0 = 1/crdf[p]^2;
  scaledR0 = r0/Max[r0];
  p = Join[pfix,
    p[[thresholdPosition[p, 
       RandomReal[{0, 1}, {Length[p]}] - scaledR0 ]]]];
  n = Length[p];

  pOld = Infinity;
  While[True,
   iterationNumber++;

   If[Max[Sqrt[Total[(p - pOld)^2, {2}]]/h0 ] > ttol,
    pOld = p;
    t = myDelaunay[p, dim];

    pMid = Total[getElementCoordinates[ p, t ], {2}]/divisor;
    (* t = t[[ Flatten[ Position[ fd @@@ 
    pMid, _?(# < -geps &) ] ] ]]; *)

    t = t[[thresholdPosition[ p, cdfs[ pMid] - geps] ]];
    bars = Union[Sort /@ Flatten[t[[All, # ]] & /@ barSelectors, 1]];
    ,
    Null;
    ];

   totForce = moveMeshPoints[p, bars, cForces];
   totForce[[Range[Length[pfix ]]]] = 
    ConstantArray[0., {Length[pfix], dim}];
   p = p + deltat*totForce;
   d = cdfs[p];
   (*ix = Flatten[ Position[ d, _?(#>0.&) ] ];*)

   ix = thresholdPosition[d, -(d + 0.)];

   dgradDim = Dimensions[p[[ix]]];
   dgradN = ConstantArray[ 0., dgradDim ];
   depsIM = deps*IdentityMatrix[dim];
   Do[
    dgradN[[All, i]] = 
     (cdfs[ 
         p[[ix]] + ConstantArray[ depsIM[[i]], {Length[p[[ix]]]}]] - 
        d[[ix]])/deps
    , {i, Last[dgradDim]}];
   dgrad = Total[ dgradN^2, {2} ];
   p[[ix]] = p[[ix]] - (d[[ix]]*dgradN)/dgrad;

   If[ dim == 2 && meshBarPlotQ,  meshbarPlot[ p, bars ]; ];
   mq = meshQualityFunction[ 
     cMeshQuality[ getElementCoordinates[ p, t ] ]];
   If[ (mq >= meshQualityFactor) || iterationNumber >= maxIterations,
    If[ mq <= mqMax && saveBestMeshQ, mq = mqMax; p = pBest; 
     t = tBest; ];
    Break[],
    If[ mq > mqMax && saveBestMeshQ, mqMax = mq; pBest = p; 
      tBest = t; ];
    ];

   ];
  Print["it num: ", iterationNumber, "  mq: ", mq];
  Return[ { p, t } ];
  ]

Examples:

Example 1: Square with hole and different refinement on boundaries:

di = Function[ {x, y}, 
   ddiff[ drectangle[ {x, y}, {{-1, -1}, {1, 1}} ], 
    dcircle[ {x, y}, {0, 0, 0.4} ] ] ];

h = Function[ {x, y}, Min[ 4*Sqrt[Plus @@ ({x, y}^2) ] - 1, 2 ] ];
h0 = 0.05;

bbox = {{-1, -1}, {1, 1}};
pfix = {{-1, -1}, {-1, 1}, {1, -1}, {1, 1}} // N;

AbsoluteTiming[ {coords, inci} = 
   distmesh[ di[x, y], h[x, y], h0, {x, y}, bbox, pfix ];]
(* it num: 87  mq: 0.66096 *)
(* {0.849295, Null} *)

Graphics[{EdgeForm[Black], FaceForm[], Polygon[(coords[[#]] & /@ inci)]}]

dm1

Example 2: Same example, finer base grid and animated:

di = Function[ {x, y}, 
   ddiff[ drectangle[ {x, y}, {{-1, -1}, {1, 1}} ], 
    dcircle[ {x, y}, {0, 0, 0.4} ] ] ];
h = Function[ {x, y}, 1. ];
h0 = 0.1;
bbox = {{ -1, -1}, {1, 1} } // N;
pfix = {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}} // N;
meshBarPlotQ = True;
gr = RegionPlot[ 
   di[x, y] < 0, {x, bbox[[1, 1]], bbox[[2, 1]]}, {y, bbox[[1, 2]], 
    bbox[[2, 2]]}, AspectRatio -> Automatic, Frame -> False, 
   Axes -> False, Mesh -> False ];
distmesh[ di[x, y], h[x, y], h0, {x, y}, bbox, pfix ];
meshBarPlotQ = False;
ClearAll[gr]

dmani1

Example 3: Same example, anisotropic refinement:

h = Function[ {x, y}, 1. + Abs[x*y]];
h0 = 0.03;
bbox = {{ -1, -1}, {1, 1} } // N;
pfix = {{-1, -1}, {1, -1}, {1, 1}, {-1, 1}} // N;
AbsoluteTiming[ {coords, inci} = 
   distmesh[ di[x, y], h[x, y], h0, {x, y}, bbox, pfix ]; ]

enter image description here

Example 4: A somewhat more complicated geometry:

di = Function[{x, y}, 
   Evaluate[
    Simplify[
     dintersection[ 
      ddiff[ ddiff[ dcircle[{x, y}, {0, 0, 2}], 
        dcircle[{x, y}, {3/2, 0, 1/5}]], dcircle[{x, y}, {0, 0, 1}]], 
      dtriangle[{x, y}, {{0, 0}, {2, 0}, {2, 1}}]]]]];

ci[{{x1_, y1_}, r_}, {m_, c_}] := Block[{tmpX, tmpH, tmpY},
  tmpX = {(-(c*m) + x1 + m*y1 - 
       Sqrt[-c^2 + r^2 + m^2*r^2 - 2*c*m*x1 - m^2*x1^2 + 2*c*y1 + 
         2*m*x1*y1 - y1^2])/(1 + m^2), (-(c*m) + x1 + m*y1 + 
       Sqrt[-c^2 + r^2 + m^2*r^2 - 2*c*m*x1 - m^2*x1^2 + 2*c*y1 + 
         2*m*x1*y1 - y1^2])/(1 + m^2)};
  tmpH = Function[{x}, m*x + c];
  tmpY = tmpH /@ tmpX;
  Transpose[{tmpX, tmpY}]
  ]

h = Function[ {x, y}, 
   Min[ 5*Sqrt[Plus @@ ({x - 1.5, y}^2) ] - 0.2, 2 ] ];
h0 = 0.01;
bbox = { {0.8, 0}, {2, 1} } // N;
pfix = Join[ {{1., 0.}, {1.3, 0.}, {1.5, 0.2}, {1.7, 0.}, {2., 0.}}, 
   ci[{{0, 0}, 2}, {1/2, 0}][[{2}]], 
   ci[{{0, 0}, 1}, {1/2, 0}][[{2}]] ] // N

AbsoluteTiming[ {coords, inci} = 
   distmesh[ di[x, y], h[x, y], h0, {x, y}, bbox, pfix, 
    MaxIterations -> 400 ];]
(*it num: 164  mq: 0.742647*)
(* {14.795667, Null} *)
Graphics[ {EdgeForm[Black], FaceForm[], 
  Polygon[ (coords[[#]] & /@ inci) ]} ]

enter image description here

Example 5: 3D:

di = Function[{x, y, z}, Sqrt[x^2 + y^2 + z^2] - 1];
h = Function[ {x, y, z}, 1. ];
h0 = 0.25;
bbox = {{ -1, -1, -1}, {1, 1, 1} } // N;
pfix = {} // N;
AbsoluteTiming[ {coords, inci} = 
   distmesh[ di[x, y, z], h[x, y, z], h0, {x, y, z}, bbox, pfix, 
    MaxIterations -> 20]; ]

TetrahedraWireframe[i_] := 
 Line[ Flatten[ 
   i[[All, #]] & /@ {{1, 2}, {2, 3}, {3, 1}, {1, 4}, {2, 4}, {3, 4}}, 
   1]]
Graphics3D[GraphicsComplex[coords, TetrahedraWireframe[inci]]]

enter image description here

Example 6: Converting a black and white image into a mesh:

shape

enter image description here;

Convert the shape into a distance function:

dist = DistanceTransform[Image[1 - ImageData[EdgeDetect[shape]]]];
ImageAdjust@dist

enter image description here

Create an inteprolation function from it:

data = Transpose[Reverse[-ImageData[dist]*(2*ImageData[shape] - 1)]];
dataRange = Transpose[{{x, y}, {1, 1}, Dimensions[data]}];
if = ListInterpolation[ data,  dataRange[[All, {2, 3}]], 
   InterpolationOrder -> 1];

Call distmesh:

di = if;
h = Function[ {x, y}, 1];
h0 = 5;

bbox = Transpose[if["Domain"]];
pfix = {} // N;

AbsoluteTiming[ {coords, inci} = 
   distmesh[ di[x, y], h[x, y], h0, {x, y}, bbox, pfix]; ]
(* it num: 150  mq: 0.535491 *)
(* {3.215599, Null} *)

enter image description here


first part..i had lying around..

poly = Random[Real, {1, 2}] {Cos[#], Sin[#]} & /@  Sort[Table[Random[Real, {0, 2 Pi}], {5}]]
isLeft[P2_, {P0_, P1_}] := -Sign@Det@{P2 - P0, P1 - P0};
pinpoly[p_, poly_] := Module[{ed},(*winding rule*)
    ed = Partition[Append[poly, poly[[1]]], {2}, 1];
    Count[ed,pr_ /; (pr[[1, 2]] <= p[[2]] < pr[[2, 2]] &&isLeft[p, pr] == 1)]
  - Count[ed, pr_ /; (pr[[2, 2]] <= p[[2]] < pr[[1, 2]] && isLeft[p, pr] == -1)] == 1];
seed = Select[ Table[RandomReal[{-2, 2}, 2], {2000}] ,  pinpoly[#, poly] &];
Show[{Graphics[Line[Append[poly, poly[[1]]]]],         Graphics[{Red, Point /@ seed}]}]
allp = Join[seed, poly];

enter image description here

Show[{Graphics[(Line /@ (allp[[#]] & /@ Thread[#])) & /@  DelaunayTriangulation[allp]], 
          Graphics[{Red, PointSize[.02], Point /@ seed}], 
          Graphics[{Blue, PointSize[.02], Point /@ poly}]}]

enter image description here

EDIT--step 2: relaxing seed node positions, note I'm not doing anythng to keep in bounds..so you see a few go out where the boundary is not convex.

lastmf = 0;
dmf = 1;
Monitor[While[dmf > 10^-3,
  allp = Join[seed, poly];
  tri = DelaunayTriangulation[allp][[;; Length[seed]]];
  alledges = Union@(Sort /@ Flatten[ Thread /@ tri , 1]);
    (*unit vector associated with each edge*)
  alledgesunitv = ((allp[[#[[2]]]] - allp[[#[[1]]]]) // Normal) & /@  alledges;
    (* edgemap is a list of positions on elledges at each seed node,
          signed to indicate if the unit vector points toward or away from the node *)
  edgemap =  Map[Function[t, (If[Length[(p = Position[alledges, #])] == 1, 
          p[[1, 1]], -Position[alledges, Reverse[#]][[1, 1]]]) & /@ Thread@ t], tri];
  elen = Norm[(allp[[#[[1]]]] - allp[[#[[2]]]])] & /@ alledges;
  targetlen = .8 Mean[elen]; 
  (*note the mean does not include the bounding polygon  edge lengths*)
  eforce =  1 - targetlen/elen; (*nonlinear spring.. need to play with this..*)
  {mf, dmf, lastmf} = {#, Abs[# - lastmf], mf} &@ Max@Abs@eforce;
  seed += (Total /@
       (Map[Sign[#] .01  eforce[[Abs[#]]]  alledgesunitv[[Abs[#]]] & ,   edgemap , {2}]));
  (* add code here to keep nodes in bounds..*)
    g = Show[{Graphics[{Thick, Blue, Line[Append[poly, poly[[1]]]]}], 
        Graphics[Line /@ Map[allp[[#]] &, alledges, {2}]], 
        Graphics[{Red, PointSize[.02], Point /@ seed}], 
        Graphics[{Blue, PointSize[.02], Point /@ poly}]}]], {g, mf, dmf}]

enter image description here

I expect at the boundary you want to not only keep nodes inside, but look for nodes close inside and snap those to the edge.

Edit 3 ..

force the bounary polygon to have a minimum edge length:

   seedlen = .25
   poly = Flatten[(kk = Ceiling[Norm[Subtract @@ # ]/(1.2 seedlen )] ; 
              Table[#[[1]] (  1 + ( 1 - ici )/kk)  + (ici - 1)/ kk #[[2]] , {ici, kk}]) &
                 /@  Partition[Append[poly, poly[[1]]], {2}, 1], 1]

and simply discard nodes that wander out of bounds , where i had "add code here" above..

   seed = Select[seed, pinpoly[#, poly] &];

works pretty good.. note when you drop nodes you need to render the plot after recomputing the triangulation.

enter image description here


Table[drawtriangulation[mesh @@ example, First@example,
   AspectRatio -> Automatic],
  {example, {circle, circle34, ellipseeye}}] // GraphicsRow

Mesh 1: Three examples

Calculating specifications for these examples:

(* distance function, bounding box, fixed points,
number of initial points, max iterations, min triangle quality *)

circle = {Sqrt[#1^2 + #2^2] - 1. &,
   {{-1, -1}, {1, 1}}, {}, 100, 200, .6};

circle34 = {Max[
     Sqrt[#1^2 + #2^2] - 1.,
     Min[Min[Min[1. + #2, -#2], #1], 1. - #1]] &,
   {{-1, -1}, {1, 1}}, {{0, -1}, {0, 0}, {1, 0}}, 100, 200, .6};

ellipseeye = {Max[
     Sqrt[#1^2 + 1.6 #2^2] - 1.,
     -(Sqrt[#1^2 + #2^2] - .6)] &,
   {{-1, -.8}, {1, .8}}, {}, 300, 200, .6};

Following OP and the paper he's following (A simple mesh generator in MATLAB), I wrote the main code in mesh below which iteratively relaxes and moves the mesh points, and, as it is, stops at iterationmax or already when quality of the worst triangle goes over qmin; region defines geometry and can be specified as a distance function (see above; negative inside geometry only and zero at boundary) or as a list of polygon points (see update), box are two corner points of a rectangle enclosing the geometry, and npts is the number of initial points inside box.

mesh[region_List | region_Function, box_List, pfix_List,
  npts_Integer, iterationmax_Integer, qmin_Real] :=
 With[{ttol = .1, fscale = 1.2, deltat = .2, geps = .001},
  Module[{p, dp, pold = {}, t, bars, barvec,
    l, l0, fvec, ftot, x1, y1, x2, y2, outlined,
    iteration = 0, nf, grad, test},
   {{x1, y1}, {x2, y2}} = box;
   Switch[Head[region],
    List, nf = Nearest[region -> Automatic,
      DistanceFunction -> EuclideanDistance];
    outlined = outline[region, geps (x2 - x1)],
    Function, grad[x_, y_] := Grad[region[x, y], {x, y}] // Evaluate];
   test = If[Head[region] === List,
     Graphics`Mesh`InPolygonQ[outlined, #] &, region @@ # < geps &];
   p = pfix~Join~Select[Transpose[{
        RandomReal[{x1, x2}, npts],
        RandomReal[{y1, y2}, npts]}], test@# &];
   While[iteration++ < iterationmax,
    If[pold == {} || Max[Divide[
         Norm /@ (p - pold), (x2 - x1)/Sqrt[npts]]] > ttol,
     pold = p;
     t = qDelaunayTriangulation[p];
     t = Select[t, test@Mean[p[[#]]] &];
     bars = DeleteDuplicates[Sort /@ Flatten[
         Partition[#, 2, 1, 1] & /@ t, 1]]];
    If[Min[quality @@@ (p[[#]] & /@ t)] > qmin, Break[]];
    barvec = Subtract @@ p[[#]] & /@ bars;
    l = Norm /@ barvec;
    l0 = fscale Sqrt[Total[l^2]/Length@bars];
    fvec = Map[Max[#, 0] &, l0 - l]/l barvec;
    ftot = SparseArray[MapThread[
        {#1 -> Hold[#2], Reverse[#1] -> Hold[-#2]} &,
        {bars, fvec}]~Flatten~1];
    If[pfix =!= {}, ftot[[Range@Length@pfix, All]] = 0];
    dp = deltat Total /@ Map[ReleaseHold, ftot, {2}];
    p += dp;
    p = MapAt[If[Head[region] === List,
        backtoedgelist[#, region, nf],
        backtoedgefunction[#, region, grad]] &, p,
      Position[p, {x_Real, y_Real} /; ! test[{x, y}]]];
    ]; p]]

You can see mesh calls qDelaunayTriangulation which is an external program, so the first thing is to mathlink Qhull which supplies this relatively speedy triangulator (@halirutan discusses it here, compiled and ready for Windows via @Oleksandr R.). It's about 3,000 times faster than DelaunayTriangulation in package ComputationalGeometry.

Providing Qhull is in the directory following the one where notebook is saved:

lnk = Install[NotebookDirectory[] <> "\\qhull64\\qh-math.exe"]

This gives access to qDelaunayTriangulation. There is also a call to InPolygonQ which is an undocumented function that determines if a point is inside a polygon or not.

Finally, the functions below: quality for testing triangle quality (1 for equilateral, 0 for a degenerate triangle with zero area), backtoedgefunction for bringing a wentover point back to boundary defined with distance function, backtoedgelist for polygon boundary, and drawtriangulation for drawing result.

quality[p1_, p2_, p3_] := With[{
   a = Norm[-p1 + p2],
   b = Norm[-p2 + p3],
   c = Norm[-p1 + p3]},
  (b + c - a) (c + a - b) (a + b - c)/(a b c)]

(* point to boundary region = 0, preevaluated gradient *)
backtoedgefunction[point_, region_Function, gradient_] :=
 With[{r = point + t gradient @@ point},
  r /. FindRoot[region @@ r,
    {t, 0, .1}, Method -> "Secant"]]

(* point to polygon boundary, precalculated nf *)
backtoedgelist[point_, region_List, nf_NearestFunction] :=
 With[{i = nf[point][[1]], d = Length@region},
  Module[{a, b, c, test},
   {a, b, c} = Extract[region, List /@ Which[
       1 < i < d, {i - 1, i, i + 1},
       i == 1, {-1, 1, 2},
       i == d, {d - 1, d, 1}]];
   test = And @@ Thread[{
         VectorAngle[#3 - #1, #2 - #1],
         VectorAngle[#3 - #2, #1 - #2]} < .5 Pi] &;
   Which[
    test[a, b, point], a + Projection[point - a, b - a],
    test[b, c, point], b + Projection[point - b, c - b],
    True, b]]]

drawtriangulation[pts_List,
  region_List | region_Function, opt___] :=
 With[{geps = .001},
  Module[{
    dt = qDelaunayTriangulation@pts,
    outlined, test},
   test = If[Head[region] === List,
     outlined = outline[region,
       geps {-1, 1}.Through[{Min, Max}[First /@ region]]];
     Graphics`Mesh`InPolygonQ[outlined, #] &, region @@ # < geps &];
   dt = Select[dt, test@Mean@pts[[#]] &];
   Graphics[GraphicsComplex[pts,
     {EdgeForm[Opacity[.2]], Map[{
         ColorData["AlpineColors"][quality @@ pts[[#]]],
         Polygon[#]} &, dt]}], opt]]]

Update: Polygons

I extended the code to meshing polygons. (Testing whether a point is inside the region changes, also bringing the point back to the boundary changes.) Two examples:

heart = {
   With[{n = 30, fx = 16 Sin[#]^3 &,
     fy = 13 Cos[#] - 5 Cos[2 #] - 2 Cos[3 #] - Cos[4 #] &},
    Table[{fx@t, fy@t}, {t, 0., 2 Pi (1 - 1/n), 2 Pi/n}]],
   {{-16, -17}, {16, 12}}, {{0, 5}, {0, -17}}, 200, 200, .7};

slo = Module[{edge,
    boundary = CountryData["Slovenia", "Polygon"][[1, 1]]},
   edge = Take[boundary, {1, -1, 5}];
   edge = # - Mean[edge] & /@ edge;
   {edge, Transpose[{
      Through[{Min, Max}[First /@ edge]],
      Through[{Min, Max}[Last /@ edge]]}], {}, 200, 200, .7}];

Table[Graphics[Line@First@poly],
  {poly, {heart, slo}}] // GraphicsRow

Two polygons

Table[drawtriangulation[mesh @@ example, First@example],
  {example, {heart}}] // GraphicsRow

Mesh 2: Two polygon examples

In both cases of region, whether it's a polygon or distance function, a geometric tolerance geps is used in testing whether a point lies inside the region. For this I "inflated" a polygon a tiny bit with these two functions:

moveout[{r1_, r2_, r3_}, d_] := Module[{
   t = VectorAngle[r1 - r2, r3 - r2],
   s = Normalize[r1 - r2], sign},
  sign = If[Negative@Last@Cross[
       (r2 - r1)~Append~0,
       (r3 - r2)~Append~0], -1, 1];
  r2 + Plus[d  (s /. {x_, y_} :> {y, -x}), sign d Tan[.5 (Pi - t)] s]]

outline[poly_, d_] :=
 Map[Function[tri, moveout[tri, d]], Partition[poly, 3, 1, 1]]