Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
558 views
in Technique[技术] by (71.8m points)

c# - How can I connect two parallel 2d polygons to create a seamless 3d mesh?

Suppose I have two polygons, one just above the other like this:

Two polygons

I'd like to connect up their vertices to create a 3d mesh out of triangles around their perimeters. This picture shows one way you might do this (orange lines represent triangle edges):

Two polygons, connected with triangles

This sort of thing can be done intuitively by a human, but I'm having real trouble translating it into a working algorithm.

The polygons are stored as a List<Vector2>. They will always be simple, and may be concave.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I would do it like this:

  1. find two closest points in each polygons

    this will be used as start point

  2. reorder vertexes from this two start points

    with the same winding rule for example CW like on image

  3. find 'center' points for each polygon

    for example average of all vertexes or mid point of bounding box or whatever. It does not need to be precise but on complex concave shapes wrongly selected center position will cause errors in output mesh.

  4. add angular info for each vertex

    angle is easy just use

    a=atan2(vertex-center)
    

After all this it should looks like this:

polygons

for angle angle [deg]

index: 0   1   2   3   4
green: 355 085 135 170 230 
 blue: 020 135 255

Now you just match 2 closest vertexes from one polygon to the other

do not forget that angle difference is max +/-180 deg and also if you use radians then convert constants 180,360 to PI,2.0*PI

da=ang1-ang0;
while (da<-180.0) da+=360.0;
while (da>+180.0) da-=360.0;
if (da<0.0) da=-da;

for next text line(i,j) will mean i-th vertex from green and j-th vertex from blue polygon

Now the joining:

  1. join closest vertexes

    just process all green vertexes and join them to closest blue vertex (black lines on image)

    line(0,0)
    line(1,1)
    line(2,1)
    line(3,1)
    line(4,2)
    
  2. fill the holes

    mesh triangulation need at least 2 joints per vertex so process points that have less connections:

                index: 0 1 2 3 4
    green connections: 1 1 1 1 1
     blue connections: 1 3 1
    

    so now process less then 2 times used blue vertexes (0,2) and join them to closest green vertex (yellow lines on image) ignoring already used connection (chose next one in that case)

    line(1,0)
    line(3,2)
    

    so:

                index: 0 1 2 3 4
    green connections: 1 2 1 2 1
     blue connections: 2 3 2
    

    now process the rest of green less then 2 times used vertexes joined to less then 3 times used blue vertex. Chose just the next point to already used connection if it has less then 3 connections and if there are more then 1 option use the closest one (red lines on image).


line(0,2) green 0 is connected to blue 0 so choose from blue 1,2 (1 is too used so 2)
line(2,-) green 2 is connected to blue 1 which is 3 times used so ignore this vertex
line(4,-) green 4 is connected to blue 2 which is 3 times used so ignore this vertex

                index: 0 1 2 3 4
    green connections: 2 2 1 2 1
     blue connections: 2 3 3

and that is it all. Now you should have the tesselation done like this:

mesh

[notes]

  • you can also use perimeter position / perimeter length instead of angle (sometimes it have better results)
  • concave polygons can mess this up a lot so there should be added some triangle intersection checks to avoid problems.
  • also the number of vertexes should not differ too much between polygons in that case you can still divide some lines to add missing points otherwise that 3 times usage conditions will be wrong (if the vertex count differs 2 or more times)
  • if you want to be safe you should cut polygons to convex parts and tesselate them separately and only after that join the meshes back together

[Edit1] new approach less susceptible to concavity and points density

Its slower but looks its working for more complex shapes. As example I took the modified star and plus sign from the comments.

  1. find two closest points in each polygons

    this will be used as start point

  2. reorder vertexes from this two start points

    with the same winding rule for example CW like on image

  3. prepare edge lists

    we will need a structure holding all the connections between polygons. I decided to something like this:

    List< List<int> > edg0,edg1;
    

    where edg0[point0][i] is holding i-th point1 connected to point0. Beware in the code array index is the point index and the actual value n the array is point index in a global point table pnt where I can transform between the two like this:

    point0 = (pnt_index - ix0)/3 
    point1 = (pnt_index - ix1)/3 
    pnt_index = ix0 + 3*point0
    pnt_index = ix1 + 3*point1
    

    where ix0,ix1 are start indexes in pnt for each polygon.

  4. join close points

    Instead of joining to the closest points I added thresholding so the point distance must be smaller than threshold minll. This threshold is a multiply of the distance of the 2 start points (min distance). In code:

    minll=2.5*l0;
    

    but beware I am comparing distance^2 for speed so if you got distance it would be

    minl=sqrt(2.5)*l0;
    

    The multiply coefficient can be tweaked ...

    join close points

    as you can see the distant point on star is not connected due thresholding.

  5. fill unused holes in points0

    simply find sequence of unused points0 and then interpolate the join from start to end with first neighbors that are connected. so if points0 i0 .. i1 are unconnected and their closest neighbors are connected take their closest connected points1 j0 .. j1 and simply linearly connect point i with j where:

    i = i0 + t*(i1-i0)
    j = j0 + t*(j1-j0)
    

    where t is parameter t=<0.0,1.0> going through all "integer" point indexes. (use DDA).

    holes0

  6. fill unused holes in points1

    its the same as #5 but look for the unconnected points1 in the other polygon.

    holes1

  7. find&connect singular connection lines

    both endpoints are used just once. So simply find such edge and connect it to neighboring points.

    singular lines

  8. find singular poinst0 that form QUAD hole and triangulate it

    so find point0 that is connected just once and then test its neighbors if the are connected back to the point1 the first one was connected to. If no you found QUAD hole so triangulate it.

    Quad holes

  9. now just extract triangles and lines from the edg0,edg1

    line are simple as they are already encoded directly, for triangles simply search neighboring point0 for connection to the same point1. If found form a triangle. the triangles should be formed also in the other edge list so search neighboring point1 that are connected to the same point0 too.

Here GL/C++ example

List<double> pnt;
List<int> lin,tri;
int iy0=0,iy1=0,iy2=0,iy3=0;// pol0<iy0,iy1),pol1<iy1,iy2),merge<iy2,iy3)
int ix0=0,ix1=0,ix2=0;      // points1<ix0,ix1), points2<ix1,ix2)
//---------------------------------------------------------------------------
void obj_init()
    {
    // input data
    const double d=0.5;             // distance between polygons
    const double pol0[]={0.0,2.0, 1.0,2.0, 1.0,3.0, 2.00,3.0, 2.0,2.0, 3.00,2.0, 3.0,1.0, 2.0,1.0, 2.0,0.0, 1.0,0.0, 1.0,1.0, 0.0,1.0, 0.0,2.0};
//  const double pol1[]={0.0,0.0, 1.0,1.0, 0.0,2.0, 1.25,1.5, 1.5,2.5, 1.75,1.5, 3.0,2.0, 2.0,1.0, 3.0,0.0, 1.5,0.7};
    const double pol1[]={0.0,0.0, 1.0,1.0, 0.0,2.0, 1.25,1.5, 1.5,5.0, 1.75,1.5, 3.0,2.0, 2.0,1.0, 3.0,0.0, 1.5,0.7};
//                                                                ***
    // variables
    List<double> tmp;
    List< List<int> > edg0,edg1;    // edge table

    double minll;                   // max distance to points that will join automatically
    double p[3],l0,l;
    int i,i0,i1,ii,ii0,ii1,di;
    int j,j0,j1,jj,jj0,jj1,dj;
    int k,n0,n1,e,de;
    pnt.num=0;
    lin.num=0;

    // copy pol0 to point list pnt[]
    ix0=pnt.num;
    n0=sizeof(pol0)/sizeof(pol0[0]);
    for (j=pnt.num,i=0;i<n0;)
        {
        pnt.add(pol0[i]); i++;
        pnt.add(pol0[i]); i++;
        pnt.add(-d);
        } ix1=pnt.num; n0/=2;

    // copy pol1 to point list pnt[]
    n1=sizeof(pol1)/sizeof(pol1[0]);
    for (j=pnt.num,i=0;i<n1;)
        {
        pnt.add(pol1[i]); i++;
        pnt.add(pol1[i]); i++;
        pnt.add(+d);
        } ix2=pnt.num; n1/=2;

    // add polygon edges
    iy0=lin.num;
    for (j=ix1-3,i=ix0;i<ix1;j=i,i+=3){ lin.add(j); lin.add(i); } iy1=lin.num;
    for (j=ix2-3,i=ix1;i<ix2;j=i,i+=3){ lin.add(j); lin.add(i); } iy2=lin.num;

    // find closest points -> start points i0,j0
    i0=-1; j0=-1; l0=0.0; minll=0.0;
    for (i=ix0;i<ix1;i+=3)
     for (j=ix1;j<ix2;j+=3)
        {
        vector_sub(p,pnt.dat+i,pnt.dat+j);
        l=vector_len2(p);
        if ((i0<0)||(l<l0)){ l0=l; i0=i; j0=j; }
        } minll=2.5*l0;
    // reorder points so closest ones are first
    if (i0!=ix0)
        {
        tmp.num=0;  for (i=ix0;i<ix1;i++) tmp.add(pnt.dat[i]);                          // copy to temp
        for (j=i0,i=ix0;i<ix1;i++,j++,(j>=ix1)?j=ix0:j=j) pnt.dat[i]=tmp.dat[j-ix0];    // reorder
        }
    if (j0!=ix1)
        {
        tmp.num=0;  for (i=ix1;i<ix2;i++) tmp.add(pnt.dat[i]);                          // copy to temp
        for (j=j0,i=ix1;i<ix2;i++,j++,(j>=ix2)?j=ix1:j=j) pnt.dat[i]=tmp.dat[j-ix1];    // reorder
        }

    // init edge lists
    edg0.allocate(n0); edg0.num=n0; for (i=0;i<n0;i++) edg0[i].num=0;
    edg1.allocate(n1); edg1.num=n1; for (i=0;i<n1;i++) edg1[i].num=0;

    // join close points
    for (ii=0,i=ix0;i<ix1;i+=3,ii++)
        {
        j0=-1; jj0=-1; l0=0.0;
        for (jj=0,j=ix1;j<ix2;j+=3,jj++)
            {
            vector_sub(p,pnt.dat+i,pnt.dat+j);
            l=vector_len2(p);
            if ((j0<0)||(l<l0)){ l0=l; j0=j; jj0=jj; }
            }
        if ((j0>=0)&&(l0<min

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...