/* * Mesa 3-D graphics library * Version: 3.3 * Copyright (C) 1995-2000 Brian Paul * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * This file is part of the polygon tesselation code contributed by * Bogdan Sikorski */ #ifdef PC_HEADER #include "all.h" #else #include #include #include "tess.h" #endif static GLboolean edge_flag; static void emit_triangle(GLUtriangulatorObj *, tess_vertex *, tess_vertex *, tess_vertex *); static void emit_triangle_with_edge_flag(GLUtriangulatorObj *, tess_vertex *, GLboolean, tess_vertex *, GLboolean, tess_vertex *, GLboolean); static GLdouble twice_the_triangle_area(tess_vertex * va, tess_vertex * vb, tess_vertex * vc) { return (vb->x - va->x) * (vc->y - va->y) - (vb->y - va->y) * (vc->x - va->x); } static GLboolean left(GLdouble A, GLdouble B, GLdouble C, GLdouble x, GLdouble y) { if (A * x + B * y + C > -EPSILON) return GL_TRUE; else return GL_FALSE; } static GLboolean right(GLdouble A, GLdouble B, GLdouble C, GLdouble x, GLdouble y) { if (A * x + B * y + C < EPSILON) return GL_TRUE; else return GL_FALSE; } static GLint convex_ccw(tess_vertex * va, tess_vertex * vb, tess_vertex * vc, GLUtriangulatorObj * tobj) { GLdouble d; d = twice_the_triangle_area(va, vb, vc); if (d > EPSILON) { return 1; } else if (d < -EPSILON) { return 0; } else { return -1; } } static GLint convex_cw(tess_vertex * va, tess_vertex * vb, tess_vertex * vc, GLUtriangulatorObj * tobj) { GLdouble d; d = twice_the_triangle_area(va, vb, vc); if (d < -EPSILON) { return 1; } else if (d > EPSILON) { return 0; } else { return -1; } } static GLboolean diagonal_ccw(tess_vertex * va, tess_vertex * vb, GLUtriangulatorObj * tobj, tess_contour * contour) { tess_vertex *vc = va->next, *vertex, *shadow_vertex; struct { GLdouble A, B, C; } ac, cb, ba; GLdouble x, y; GLint res = convex_ccw(va, vc, vb, tobj); if (res == 0) return GL_FALSE; if (res == -1) return GL_TRUE; ba.A = vb->y - va->y; ba.B = va->x - vb->x; ba.C = -ba.A * va->x - ba.B * va->y; ac.A = va->y - vc->y; ac.B = vc->x - va->x; ac.C = -ac.A * vc->x - ac.B * vc->y; cb.A = vc->y - vb->y; cb.B = vb->x - vc->x; cb.C = -cb.A * vb->x - cb.B * vb->y; for (vertex = vb->next; vertex != va; vertex = vertex->next) { shadow_vertex = vertex->shadow_vertex; if (shadow_vertex != NULL && (shadow_vertex == va || shadow_vertex == vb || shadow_vertex == vc)) continue; x = vertex->x; y = vertex->y; if (left(ba.A, ba.B, ba.C, x, y) && left(ac.A, ac.B, ac.C, x, y) && left(cb.A, cb.B, cb.C, x, y)) return GL_FALSE; } return GL_TRUE; } static GLboolean diagonal_cw(tess_vertex * va, tess_vertex * vb, GLUtriangulatorObj * tobj, tess_contour * contour) { tess_vertex *vc = va->next, *vertex, *shadow_vertex; struct { GLdouble A, B, C; } ac, cb, ba; GLdouble x, y; GLint res = convex_cw(va, vc, vb, tobj); if (res == 0) return GL_FALSE; if (res == -1) return GL_TRUE; ba.A = vb->y - va->y; ba.B = va->x - vb->x; ba.C = -ba.A * va->x - ba.B * va->y; ac.A = va->y - vc->y; ac.B = vc->x - va->x; ac.C = -ac.A * vc->x - ac.B * vc->y; cb.A = vc->y - vb->y; cb.B = vb->x - vc->x; cb.C = -cb.A * vb->x - cb.B * vb->y; for (vertex = vb->next; vertex != va; vertex = vertex->next) { shadow_vertex = vertex->shadow_vertex; if (shadow_vertex != NULL && (shadow_vertex == va || shadow_vertex == vb || shadow_vertex == vc)) continue; x = vertex->x; y = vertex->y; if (right(ba.A, ba.B, ba.C, x, y) && right(ac.A, ac.B, ac.C, x, y) && right(cb.A, cb.B, cb.C, x, y)) return GL_FALSE; } return GL_TRUE; } static void clip_ear(GLUtriangulatorObj * tobj, tess_vertex * v, tess_contour * contour) { emit_triangle(tobj, v->previous, v, v->next); /* the first in the list */ if (contour->vertices == v) { contour->vertices = v->next; contour->last_vertex->next = v->next; v->next->previous = contour->last_vertex; } else /* the last ? */ if (contour->last_vertex == v) { contour->vertices->previous = v->previous; v->previous->next = v->next; contour->last_vertex = v->previous; } else { v->next->previous = v->previous; v->previous->next = v->next; } free(v); --(contour->vertex_cnt); } static void clip_ear_with_edge_flag(GLUtriangulatorObj * tobj, tess_vertex * v, tess_contour * contour) { emit_triangle_with_edge_flag(tobj, v->previous, v->previous->edge_flag, v, v->edge_flag, v->next, GL_FALSE); v->previous->edge_flag = GL_FALSE; /* the first in the list */ if (contour->vertices == v) { contour->vertices = v->next; contour->last_vertex->next = v->next; v->next->previous = contour->last_vertex; } else /* the last ? */ if (contour->last_vertex == v) { contour->vertices->previous = v->previous; v->previous->next = v->next; contour->last_vertex = v->previous; } else { v->next->previous = v->previous; v->previous->next = v->next; } free(v); --(contour->vertex_cnt); } static void triangulate_ccw(GLUtriangulatorObj * tobj, tess_contour * contour) { tess_vertex *vertex; GLuint vertex_cnt = contour->vertex_cnt; while (vertex_cnt > 3) { vertex = contour->vertices; while (diagonal_ccw(vertex, vertex->next->next, tobj, contour) == GL_FALSE && tobj->error == GLU_NO_ERROR) vertex = vertex->next; if (tobj->error != GLU_NO_ERROR) return; clip_ear(tobj, vertex->next, contour); --vertex_cnt; } } static void triangulate_cw(GLUtriangulatorObj * tobj, tess_contour * contour) { tess_vertex *vertex; GLuint vertex_cnt = contour->vertex_cnt; while (vertex_cnt > 3) { vertex = contour->vertices; while (diagonal_cw(vertex, vertex->next->next, tobj, contour) == GL_FALSE && tobj->error == GLU_NO_ERROR) vertex = vertex->next; if (tobj->error != GLU_NO_ERROR) return; clip_ear(tobj, vertex->next, contour); --vertex_cnt; } } static void triangulate_ccw_with_edge_flag(GLUtriangulatorObj * tobj, tess_contour * contour) { tess_vertex *vertex; GLuint vertex_cnt = contour->vertex_cnt; while (vertex_cnt > 3) { vertex = contour->vertices; while (diagonal_ccw(vertex, vertex->next->next, tobj, contour) == GL_FALSE && tobj->error == GLU_NO_ERROR) vertex = vertex->next; if (tobj->error != GLU_NO_ERROR) return; clip_ear_with_edge_flag(tobj, vertex->next, contour); --vertex_cnt; } } static void triangulate_cw_with_edge_flag(GLUtriangulatorObj * tobj, tess_contour * contour) { tess_vertex *vertex; GLuint vertex_cnt = contour->vertex_cnt; while (vertex_cnt > 3) { vertex = contour->vertices; while (diagonal_cw(vertex, vertex->next->next, tobj, contour) == GL_FALSE && tobj->error == GLU_NO_ERROR) vertex = vertex->next; if (tobj->error != GLU_NO_ERROR) return; clip_ear_with_edge_flag(tobj, vertex->next, contour); --vertex_cnt; } } void tess_tesselate(GLUtriangulatorObj * tobj) { tess_contour *contour; for (contour = tobj->contours; contour != NULL; contour = contour->next) { if (contour->orientation == GLU_CCW) { triangulate_ccw(tobj, contour); } else { triangulate_cw(tobj, contour); } if (tobj->error != GLU_NO_ERROR) return; /* emit the last triangle */ emit_triangle(tobj, contour->vertices, contour->vertices->next, contour->vertices->next->next); } } void tess_tesselate_with_edge_flag(GLUtriangulatorObj * tobj) { tess_contour *contour; edge_flag = GL_TRUE; /* first callback with edgeFlag set to GL_TRUE */ (tobj->callbacks.edgeFlag) (GL_TRUE); for (contour = tobj->contours; contour != NULL; contour = contour->next) { if (contour->orientation == GLU_CCW) triangulate_ccw_with_edge_flag(tobj, contour); else triangulate_cw_with_edge_flag(tobj, contour); if (tobj->error != GLU_NO_ERROR) return; /* emit the last triangle */ emit_triangle_with_edge_flag(tobj, contour->vertices, contour->vertices->edge_flag, contour->vertices->next, contour->vertices->next->edge_flag, contour->vertices->next->next, contour->vertices->next->next->edge_flag); } } static void emit_triangle(GLUtriangulatorObj * tobj, tess_vertex * v1, tess_vertex * v2, tess_vertex * v3) { (tobj->callbacks.begin) (GL_TRIANGLES); (tobj->callbacks.vertex) (v1->data); (tobj->callbacks.vertex) (v2->data); (tobj->callbacks.vertex) (v3->data); (tobj->callbacks.end) (); } static void emit_triangle_with_edge_flag(GLUtriangulatorObj * tobj, tess_vertex * v1, GLboolean edge_flag1, tess_vertex * v2, GLboolean edge_flag2, tess_vertex * v3, GLboolean edge_flag3) { (tobj->callbacks.begin) (GL_TRIANGLES); if (edge_flag1 != edge_flag) { edge_flag = (edge_flag == GL_TRUE ? GL_FALSE : GL_TRUE); (tobj->callbacks.edgeFlag) (edge_flag); } (tobj->callbacks.vertex) (v1->data); if (edge_flag2 != edge_flag) { edge_flag = (edge_flag == GL_TRUE ? GL_FALSE : GL_TRUE); (tobj->callbacks.edgeFlag) (edge_flag); } (tobj->callbacks.vertex) (v2->data); if (edge_flag3 != edge_flag) { edge_flag = (edge_flag == GL_TRUE ? GL_FALSE : GL_TRUE); (tobj->callbacks.edgeFlag) (edge_flag); } (tobj->callbacks.vertex) (v3->data); (tobj->callbacks.end) (); }