star-cpr

Clip 2D meshes with 2D polygons
git clone git://git.meso-star.fr/star-cpr.git
Log | Files | Refs | README | LICENSE

commit e81c4569e9b09f3499df965c7819d60bae8589ac
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Fri, 19 Aug 2016 18:07:02 +0200

First draft of the Weiler Atherton clipping algorithm

Diffstat:
Amain.c | 361+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 361 insertions(+), 0 deletions(-)

diff --git a/main.c b/main.c @@ -0,0 +1,361 @@ +#include <rsys/double2.h> +#include <rsys/stretchy_array.h> + +#include <stdio.h> +#include <math.h> + +#define NDISKS 10 +#define N 50 +#define LEN 5.0 +#define DELTA (LEN / NDISKS) + +struct mesh { + double* coords; + size_t npoints; + int* tris; + size_t ntris; +}; + +struct hit { + double pos[2]; /* Position */ + double t0; /* Parametric intersection on the polygon */ + double t1; /* Parametric intersection on the clipping polygon */ +}; + +struct node { + int prev; + int next; + int link; + double pos[2]; + double t; +}; + +#define MESH_NULL { NULL, 0, NULL, 0 } + +static void +dump(FILE* stream, const struct mesh* poly) +{ + size_t i; + for(i = 0; i < poly->npoints; ++i) { + fprintf(stream, "v %g %g %g\n", + poly->coords[i*3+0], + poly->coords[i*3+1], + poly->coords[i*3+2]); + } + + for(i = 0; i < poly->ntris; ++i) { + fprintf(stream, "f %d %d %d\n", + poly->tris[i*3+0] + 1, + poly->tris[i*3+1] + 1, + poly->tris[i*3+2] + 1); + } +} + +static int +intersect + (const double a0[2], const double a1[2], /* 1st segment */ + const double b0[2], const double b1[2], /* 2nd segment */ + struct hit* hit) +{ + double p0[2], p1[2]; + double d0[2], d1[2]; + double tmp[2]; + double a, b, t; + + d2_set(p0, a0); + d2_set(p1, b0); + d2_sub(d0, a1, a0); + d2_sub(d1, b1, b0); + + d2_sub(tmp, p1, p0); + a = d2_cross(tmp, d1); + b = d2_cross(d0, d1); + if(b == 0) return 0; + + hit->t0 = a / b; + d2_add(hit->pos, p0, d2_muld(hit->pos, d0, hit->t0)); + if(d1[0] != 0) { + hit->t1 = (hit->pos[0] - p1[0]) / d1[0]; + } else { + hit->t1 = (hit->pos[1] - p1[1]) / d1[1]; + } + + if(hit->t0 < 0 || hit->t0 > 1 + || hit->t1 < 0 || hit->t1 > 1) + return 0; /* No intersection */ + +#ifndef NDEBUG + { + double pt1[2]; + d2_add(pt1, p1, d2_muld(pt1, d1, hit->t1)); + ASSERT(d2_eq_eps(hit->pos, pt1, 1.e-6)); + } +#endif + return 1; +} + +static void +clip_weiler_atherton + (double v[3][2], /* CW order */ + const double* clip, /* CCW order */ + const size_t nclips) +{ + struct node* poly_nodes = NULL; + struct node* clip_nodes = NULL; + double* output = NULL; + int* is_inside = NULL; + struct node* list[2]; + int prev, next; + int i, j, n; + + /* Register the polygon nodes */ + for(i=0, prev=2, next=1, n=3; i < n; ++i) { + struct node* node = sa_add(poly_nodes, 1); + node->prev = prev; + node->next = next; + node->link = -1; + node->pos[0] = v[i][0]; + node->pos[1] = v[i][1]; + + prev = (prev + 1) % n; + next = (next + 1) % n; + } + + /* Register the clipping nodes */ + for(i=0, prev=nclips-1, next=1, n=nclips; i < n; ++i) { + struct node* node = sa_add(clip_nodes, 1); + node->prev = prev; + node->next = next; + node->link = -1; + node->pos[0] = clip[i*2 + 0]; + node->pos[1] = clip[i*2 + 1]; + + prev = (prev + 1) % n; + next = (next + 1) % n; + } + + /* Add the intersection nodes into the polygon and clipping nodes */ + for(i=0; i < 3; ++i) { + double poly_edge[2][2]; + + /* Fetch the edge vertices of the triangle */ + d2_set(poly_edge[0], v[i]); + d2_set(poly_edge[1], v[(i+1) % 3]); + + for(j=0; j < nclips; ++j) { + struct node* node; + struct node* clip_node; + struct node* poly_node; + struct hit hit; + size_t ipoly_node; + size_t iclip_node; + size_t inode; + double clip_edge[2][2]; + int has_intersection; + + /* Fetch the vertices of the clip polygon edge */ + d2_set(clip_edge[0], clip + j*2); + d2_set(clip_edge[1], clip + ((j+1)%nclips)*2); + + /* Find the intersection between the triangle and the clip polygon edges */ + has_intersection = intersect + (poly_edge[0], poly_edge[1], clip_edge[0], clip_edge[1], &hit); + if(!has_intersection) continue; /* No edge intersection */ + + /* Register the intersection into the list of poly/clip nodes */ + poly_node = sa_add(poly_nodes, 1); + clip_node = sa_add(clip_nodes, 1); + ipoly_node = poly_node - poly_nodes; + iclip_node = clip_node - clip_nodes; + + /* Insert the new poly node into the list of triangle nodes */ + node = poly_nodes + poly_nodes[i].next; + while(node->link >= 0) { + if(node->t > hit.t0) break; + node = poly_nodes + node->next; + } + inode = node - poly_nodes; + poly_node->next = inode; + poly_node->prev = node->prev; + poly_node->link = iclip_node; /* Id of the corresponding clip node */ + poly_node->pos[0] = hit.pos[0]; + poly_node->pos[1] = hit.pos[1]; + poly_node->t = hit.t0; + poly_nodes[node->prev].next = ipoly_node; + node->prev = ipoly_node; + + /* Insert the new clip node into the list of clip nodes */ + node = clip_nodes + clip_nodes[j].next; + while(node->link >= 0) { + if(node->t > hit.t1) break; + node = clip_nodes + node->next; + } + inode = node - clip_nodes; + clip_node->next = inode; + clip_node->prev = node->prev; + clip_node->link = ipoly_node; /* Id of the corresponding poly node */ + clip_node->pos[0] = hit.pos[0]; + clip_node->pos[1] = hit.pos[1]; + clip_node->t = hit.t1; + clip_nodes[node->prev].next = iclip_node; + node->prev = iclip_node; + } + } + + /* Define which triangle vertices are inside the clip polygon */ + for(i=0; i < 3; ++i) { + int inside = 1; + for(j=0; inside && j < nclips; ++j) { + const double* c0 = clip + j*2/*#coords per vertex*/; + const double* c1 = clip + ((j+1)%nclips)*2/*#coords per vertex*/; + double e0[2], e1[2]; + + d2_sub(e0, c0, v[i]); + d2_sub(e1, c1, v[i]); + inside = d2_cross(e1, e0) < 0; + } + sa_push(is_inside, inside); + } + + list[0] = poly_nodes; + list[1] = clip_nodes; + + /* Note that hit nodes are registered *after* the nodes of the polygon */ + if(sa_size(poly_nodes) == 3 && !is_inside[0]) { + for(i=0; i < 3; ++i) { + double x = v[i][0]; + double y = v[i][1]; + double z = x*x + y*y; + printf("%g %g %g\n", x, y, z); + } + } else { + for(i=3, n=sa_size(poly_nodes); i < n; ++i) { + struct node* node_start = poly_nodes + poly_nodes[i].next; + struct node* node = node_start; + int ilist = 0; + + if(poly_nodes[poly_nodes[i].next].link >= 0 || is_inside[poly_nodes[i].next]) + continue; + + sa_clear(output); + node_start = poly_nodes + poly_nodes[i].next; + node = node_start; + ilist = 0; + + do { + d2_set(sa_add(output, 2), node->pos); + node = list[ilist] + node->next; + + if(node->link >= 0) { + ilist = !ilist; + node = list[ilist] + node->link; + } + } while(node != node_start); + + /* Print the output polygon */ + for(j = 0; j < sa_size(output)/2; ++j) { + double x = output[j*2+0]; + double y = output[j*2+1]; + double z = x*x + y*y; + printf("%g %g %g\n", x, y, z); + } + printf("\n"); + } + } + + sa_release(is_inside); + sa_release(output); + sa_release(poly_nodes); + sa_release(clip_nodes); +} + +static void +test_paraboloid(void) +{ + struct mesh paraboloid = MESH_NULL; + const double xlen; + double* clip = NULL; + int i, j; + + for(i = 0; i < NDISKS+1; ++i) { + const double r = (i+1/*FIXME*/)*DELTA; + for(j = 0; j < N; ++j) { + const double theta = (double)j / (double)N * 2 * M_PI; + const double x = r * cos(theta); + const double y = r * sin(theta); + const double z = x*x + y*y; + sa_push(paraboloid.coords, x); + sa_push(paraboloid.coords, y); + sa_push(paraboloid.coords, z); + } + } + /* Polar point */ + sa_push(paraboloid.coords, 0); + sa_push(paraboloid.coords, 0); + sa_push(paraboloid.coords, 0); + paraboloid.npoints = sa_size(paraboloid.coords) / 3 /* #coords per point */; + + for(i = 0; i < NDISKS; ++i) { + const int offset = (i+1)*N; + for(j = 0; j < N; ++j) { + const int id0 = j + offset; + const int id1 = ((j + 1) % N) + offset; + const int id2 = id0 - N; + const int id3 = id1 - N; + + sa_push(paraboloid.tris, id0); + sa_push(paraboloid.tris, id1); + sa_push(paraboloid.tris, id2); + + sa_push(paraboloid.tris, id2); + sa_push(paraboloid.tris, id1); + sa_push(paraboloid.tris, id3); + } + } + + /* Polar triangles */ + for(j = 0; j < N; ++j) { + const int id0 = j; + const int id1 = (j + 1) % N; + const int id2 = sa_size(paraboloid.coords)/3 - 1; + + sa_push(paraboloid.tris, id0); + sa_push(paraboloid.tris, id1); + sa_push(paraboloid.tris, id2); + } + paraboloid.ntris = sa_size(paraboloid.tris) / 3 /* #ids per triangle */; + /*dump(stdout, &paraboloid);*/ + + /* Build the clip polygon */ + d2(sa_add(clip, 2), 0, -2.5); + d2(sa_add(clip, 2), 4, 2); + d2(sa_add(clip, 2), -3.5, 0); + + for(i = 0; i < paraboloid.ntris; ++i) { + double tri[3][2]; + d2_set(tri[0], paraboloid.coords + paraboloid.tris[i*3+0] * 3); + d2_set(tri[1], paraboloid.coords + paraboloid.tris[i*3+1] * 3); + d2_set(tri[2], paraboloid.coords + paraboloid.tris[i*3+2] * 3); + clip_weiler_atherton(tri, clip, sa_size(clip)/2); + } + + sa_release(paraboloid.coords); + sa_release(paraboloid.tris); +} + +static void +test_simple(void) +{ + double triangle[3][2] = { {0.0, 0.0}, {0.0, 1.0}, {1.0, 0.0} }; + double clip[6] = { -1.0, 0.25, 1.0, 0.25, 1, 0.75 }; + clip_weiler_atherton(triangle, clip, 3); +} + +int +main(int argc, char** argv) +{ + test_paraboloid(); + /*test_simple();*/ + return 0; +} +