wo80 / Triangle.NET

C# / .NET version of Jonathan Shewchuk's Triangle mesh generator.
457 stars 83 forks source link

Mesh refinement around line segment and points #48

Open mustbau opened 3 weeks ago

mustbau commented 3 weeks ago

Hi, i am struggling to achieve mesh refinement around a line segment and around some points inside a polygon. i used this code below to define the geometry for the PSLG for meshing, i have a n Ellipse and a line segment inside the Polygon and some points. i want to refine the mesh around all of them, here is my code:

`
var p = new TriangleNet.Geometry.Polygon();

        //first add the outer boundry points (taken from the modellines) to the polygon p
        p.Add(new Contour(boundary.Select(px => new Vertex(px.X, px.Y, 1)), 1));

        //Add borehole points
       p.Points.AddRange(boreholepts.Select(qy => new Vertex(qy.X, qy.Y, 1)));

        // Add ellipse segments to the main geometry PSLG
        List<XYZ> ellipse = AddEllipse(boreholepts[4], 1, 1);
        p.Add(new Contour(ellipse.Select(px => new Vertex(px.X, px.Y, 1)), 1));

        // Add line segment, where mesh is to be refined to the main geometry PSLG
        List<XYZ> linesegment = new List<XYZ> { boreholepts[0], boreholepts[1] };
        p.Add(new Contour(linesegment.Select(px => new Vertex(px.X, px.Y, 1)), 1));
        p.Regions.Add(new RegionPointer(linesegment[0].X, linesegment[0].Y, 11, 0.5));

        // Define regions in polygon where mesh must be refined.
        for (int i =0;i<boreholepts.Count;i++) 
        {
            //Add refinement region for each borehole point
            p.Regions.Add(new RegionPointer(boreholepts[i].X, boreholepts[i].Y,i,0.5)) ;
        }

        var options = new ConstraintOptions() { ConformingDelaunay = true };

        var triQuality = new QualityOptions()
        {
            MinimumAngle = 30,

            MaximumArea = MaxArea
        };

        // The aCute refinement algorithm might fail when used with variable
        // area constraints, so we use Ruppert's refinement algorithm here.
        triQuality.UseLegacyRefinement = true;

        //var mesh = p.Triangulate(new ConstraintOptions() { Convex = true });
        var mesh = p.Triangulate(options, triQuality);//,triQuality`

the mesh does refine around the ellipse but not around the borehole points and the line segment. Any idea whats missing in the geometry description. Thanks in Advance.

wo80 commented 3 weeks ago

I'm afraid there is no easy way to directly refine a mesh around points or a line. Region pointers are exactly what the name says: a pointer to a region which is enclosed by a contour (i.e. a region has an area > 0). A simple point doesn't fit into that category, neither does a line.

A possible solution might be to post-process the mesh, setting the triangle area for all triangles connected to the borehole points (using the VertexCirculator class) and then refining with triQuality.VariableArea = true.

wo80 commented 3 weeks ago

Here's an example:


using System.Collections.Generic;
using TriangleNet;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Meshing.Iterators;

public class Issue48
{
    public static void Test()
    {
        var poly = new Polygon(6);

        // Outer contour
        poly.Add(new Contour([
            new Vertex(0.0, 0.0, 1),
            new Vertex(1.0, 0.0, 1),
            new Vertex(1.0, 1.0, 1),
            new Vertex(0.0, 1.0, 1)],
        1, true));

        var boreholepoints = new List<Vertex>()
        {
            new Vertex(0.4, 0.4, 2),
            new Vertex(0.9, 0.9, 2)
        };

        poly.Points.AddRange(boreholepoints);

        var options = new QualityOptions()
        {
            MinimumAngle = 30,
            MaximumArea = 0.05
        };

        var m = poly.Triangulate(options);

        var c = new VertexCirculator((Mesh)m);

        // Set area constraint around borehole points
        foreach (var p in boreholepoints)
        {
            foreach (var tri in c.EnumerateTriangles(p))
            {
                tri.Area = 0.002;
            }
        }

        options.VariableArea = true;

        m.Refine(options, true);
    }
}

mesh-1

wo80 commented 3 weeks ago

Another option: add extra points around borehole points before triangulation. Of course you must make sure that those extra points do not introduce any unwanted features. Additionally, you could subdivide the line segments before triangulation.

Example:

public static void Test()
{
    var poly = new Polygon(6);

    // Outer contour
    poly.Add(new Contour([
        new Vertex(0.0, 0.0, 1),
        new Vertex(1.0, 0.0, 1),
        new Vertex(1.0, 1.0, 1),
        new Vertex(0.0, 1.0, 1)],
    1, true));

    var boreholepoints = new List<Vertex>()
    {
        new Vertex(0.4, 0.4, 2),
        new Vertex(0.9, 0.9, 2)
    };

    poly.Points.AddRange(boreholepoints);

    var options = new QualityOptions()
    {
        MinimumAngle = 30,
        MaximumArea = 0.05
    };

    // Add extra points in vicinity of borehole points
    foreach (var p in boreholepoints)
    {
        // Using negative of original label to identify extra points.
        poly.Points.AddRange(Circle(6, p, 0.01, -p.Label));
    }

    var m = poly.Triangulate(options);
}

static IEnumerable<Vertex> Circle(int n, Vertex center, double radius, int label = 0)
{
    double x = center.X;
    double y = center.Y;

    double dphi = 2 * Math.PI / n;

    for (int i = 0; i < n; i++)
    {
        yield return new Vertex(x + radius * Math.Cos(i * dphi), y + radius * Math.Sin(i * dphi), label);
    }
}

mesh-0

mustbau commented 3 weeks ago

Hi Christian, Thanks for the examples, i found the first example to be suitable, i have tried the 2nd example before. it works when i add an ellipse or circle beforehand, but the refinement is only bound inside/adjecent to the ellipse/circle contours.

I would like to continue with the post refinement technique in your first example. i tried it but somehow the function "c.EnumerateTriangles(pt) " throws null exception. Although the mesh is full of triangles.

here the full post refinement code:

` var mesh = p.Triangulate(options, triQuality);//,triQuality TriangleNet.Mesh m = (TriangleNet.Mesh)mesh; var c = new TriangleNet.Meshing.Iterators.VertexCirculator(m);

        // Set area constraint around borehole points
        int indexer = 0;
        foreach (var pt in boreholepts)
        {
            var vert = new Vertex(pt.X, pt.Y, indexer);

            foreach (var tri in c.EnumerateTriangles(vert))
            {
                tri.Area = 0.5;
            }
            indexer++;  
        }

        triQuality.VariableArea = true;

        mesh.Refine(triQuality, true);`
wo80 commented 3 weeks ago

Are there by any chance duplicate vertices in your input? Please check with PolygonValidator and also take a look at the log.

mustbau commented 3 weeks ago

hi, thanks for fast reply. No, i tested it with a single internal point, still throwing: System.NullReferenceException: “The object reference was not set to an object instance.", ill try to check with Validator.

wo80 commented 3 weeks ago

That's strange. Can you post the full stack trace and maybe a small, but complete (code and input data) test case that reproduces the error?

wo80 commented 3 weeks ago

Ok, I see the problem. Take a look at the VertexCirculator. It relies on the internal Vertex.tri. This means you cannot do

var vert = new Vertex(pt.X, pt.Y, indexer);
foreach (var tri in c.EnumerateTriangles(vert))
    //...

since that newly created vertex won't have the tri field set (that's an implementation detail of the mesh topology you don't have access to).

To make this work with your original code example, you have to have access to the borehole vertices:

//Add borehole points
var boreholevertices = boreholepts.Select(qy => new Vertex(qy.X, qy.Y, 1));
p.Points.AddRange(boreholevertices);
//...
foreach (var p in boreholevertices)
{
    foreach (var tri in c.EnumerateTriangles(p))
    //...
mustbau commented 3 weeks ago

Hi, i now tried to use the older vertices which were added to the polygon itself for post refinement, but i get the same System.NullReferenceException: “The object reference was not set to an object instance.",

Here the new try code (ill add the data together with error log after another try at it. ill try to use some other points first): ` var p = new TriangleNet.Geometry.Polygon();

        //creat vertices from boreholpts
        var bvert = new List<Vertex>();

        foreach (XYZ pt in boreholepts) 
        {
            Vertex vert = new Vertex(pt.X, pt.Y, 1);
            bvert.Add(vert);
        }

        //first add the outer boundry points (taken from the modellines) to the polygon p
        p.Add(new Contour(boundary.Select(px => new Vertex(px.X, px.Y, 1)), 1));

        // now add internal vertices 
        p.Points.AddRange(bvert);

        var options = new ConstraintOptions() { ConformingDelaunay = true };

        var triQuality = new QualityOptions()
        {
            MinimumAngle = 30,
            //VariableArea = true,
            MaximumArea = MaxArea
        };

        var mesh = p.Triangulate(options, triQuality);//,triQuality
        TriangleNet.Mesh m = (TriangleNet.Mesh)mesh;
        var c = new TriangleNet.Meshing.Iterators.VertexCirculator(m);

        // Set area constraint around borehole points

        foreach (var pt in bvert)
        {

            foreach (var tri in c.EnumerateTriangles(pt))
            {
                tri.Area = 0.5;
            }

        }

        triQuality.VariableArea = true;

        mesh.Refine(triQuality, true);

`