📄 sweep.c
字号:
/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. *//*** Author: Eric Veach, July 1994.***/#include "gluos.h"#include <assert.h>#include <stddef.h>#include <setjmp.h> /* longjmp */#include <limits.h> /* LONG_MAX */#include "mesh.h"#include "geom.h"#include "tess.h"#include "dict.h"#include "priorityq.h"#include "memalloc.h"#include "sweep.h"#define TRUE 1#define FALSE 0#ifdef FOR_TRITE_TEST_PROGRAMextern void DebugEvent( GLUtesselator *tess );#else#define DebugEvent( tess )#endif/* * Invariants for the Edge Dictionary. * - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2) * at any valid location of the sweep event * - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2 * share a common endpoint * - for each e, e->Dst has been processed, but not e->Org * - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org) * where "event" is the current sweep line event. * - no edge e has zero length * * Invariants for the Mesh (the processed portion). * - the portion of the mesh left of the sweep line is a planar graph, * ie. there is *some* way to embed it in the plane * - no processed edge has zero length * - no two processed vertices have identical coordinates * - each "inside" region is monotone, ie. can be broken into two chains * of monotonically increasing vertices according to VertLeq(v1,v2) * - a non-invariant: these chains may intersect (very slightly) * * Invariants for the Sweep. * - if none of the edges incident to the event vertex have an activeRegion * (ie. none of these edges are in the edge dictionary), then the vertex * has only right-going edges. * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced * by ConnectRightVertex), then it is the only right-going edge from * its associated vertex. (This says that these edges exist only * when it is necessary.) */#undef MAX#undef MIN#define MAX(x,y) ((x) >= (y) ? (x) : (y))#define MIN(x,y) ((x) <= (y) ? (x) : (y))/* When we merge two edges into one, we need to compute the combined * winding of the new edge. */#define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \ eDst->Sym->winding += eSrc->Sym->winding)static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent );static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp );static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp );static int EdgeLeq( GLUtesselator *tess, ActiveRegion *reg1, ActiveRegion *reg2 )/* * Both edges must be directed from right to left (this is the canonical * direction for the upper edge of each region). * * The strategy is to evaluate a "t" value for each edge at the * current sweep line position, given by tess->event. The calculations * are designed to be very stable, but of course they are not perfect. * * Special case: if both edge destinations are at the sweep event, * we sort the edges by slope (they would otherwise compare equally). */{ GLUvertex *event = tess->event; GLUhalfEdge *e1, *e2; GLdouble t1, t2; e1 = reg1->eUp; e2 = reg2->eUp; if( e1->Dst == event ) { if( e2->Dst == event ) { /* Two edges right of the sweep line which meet at the sweep event. * Sort them by slope. */ if( VertLeq( e1->Org, e2->Org )) { return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0; } return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0; } return EdgeSign( e2->Dst, event, e2->Org ) <= 0; } if( e2->Dst == event ) { return EdgeSign( e1->Dst, event, e1->Org ) >= 0; } /* General case - compute signed distance *from* e1, e2 to event */ t1 = EdgeEval( e1->Dst, event, e1->Org ); t2 = EdgeEval( e2->Dst, event, e2->Org ); return (t1 >= t2);}static void DeleteRegion( GLUtesselator *tess, ActiveRegion *reg ){ if( reg->fixUpperEdge ) { /* It was created with zero winding number, so it better be * deleted with zero winding number (ie. it better not get merged * with a real edge). */ assert( reg->eUp->winding == 0 ); } reg->eUp->activeRegion = NULL; dictDelete( tess->dict, reg->nodeUp ); /* __gl_dictListDelete */ memFree( reg );}static int FixUpperEdge( ActiveRegion *reg, GLUhalfEdge *newEdge )/* * Replace an upper edge which needs fixing (see ConnectRightVertex). */{ assert( reg->fixUpperEdge ); if ( !__gl_meshDelete( reg->eUp ) ) return 0; reg->fixUpperEdge = FALSE; reg->eUp = newEdge; newEdge->activeRegion = reg; return 1;}static ActiveRegion *TopLeftRegion( ActiveRegion *reg ){ GLUvertex *org = reg->eUp->Org; GLUhalfEdge *e; /* Find the region above the uppermost edge with the same origin */ do { reg = RegionAbove( reg ); } while( reg->eUp->Org == org ); /* If the edge above was a temporary edge introduced by ConnectRightVertex, * now is the time to fix it. */ if( reg->fixUpperEdge ) { e = __gl_meshConnect( RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext ); if (e == NULL) return NULL; if ( !FixUpperEdge( reg, e ) ) return NULL; reg = RegionAbove( reg ); } return reg;}static ActiveRegion *TopRightRegion( ActiveRegion *reg ){ GLUvertex *dst = reg->eUp->Dst; /* Find the region above the uppermost edge with the same destination */ do { reg = RegionAbove( reg ); } while( reg->eUp->Dst == dst ); return reg;}static ActiveRegion *AddRegionBelow( GLUtesselator *tess, ActiveRegion *regAbove, GLUhalfEdge *eNewUp )/* * Add a new active region to the sweep line, *somewhere* below "regAbove" * (according to where the new edge belongs in the sweep-line dictionary). * The upper edge of the new region will be "eNewUp". * Winding number and "inside" flag are not updated. */{ ActiveRegion *regNew = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); if (regNew == NULL) longjmp(tess->env,1); regNew->eUp = eNewUp; /* __gl_dictListInsertBefore */ regNew->nodeUp = dictInsertBefore( tess->dict, regAbove->nodeUp, regNew ); if (regNew->nodeUp == NULL) longjmp(tess->env,1); regNew->fixUpperEdge = FALSE; regNew->sentinel = FALSE; regNew->dirty = FALSE; eNewUp->activeRegion = regNew; return regNew;}static GLboolean IsWindingInside( GLUtesselator *tess, int n ){ switch( tess->windingRule ) { case GLU_TESS_WINDING_ODD: return (n & 1); case GLU_TESS_WINDING_NONZERO: return (n != 0); case GLU_TESS_WINDING_POSITIVE: return (n > 0); case GLU_TESS_WINDING_NEGATIVE: return (n < 0); case GLU_TESS_WINDING_ABS_GEQ_TWO: return (n >= 2) || (n <= -2); } /*LINTED*/ assert( FALSE ); /*NOTREACHED*/ return GL_FALSE; /* avoid compiler complaints */}static void ComputeWinding( GLUtesselator *tess, ActiveRegion *reg ){ reg->windingNumber = RegionAbove(reg)->windingNumber + reg->eUp->winding; reg->inside = IsWindingInside( tess, reg->windingNumber );}static void FinishRegion( GLUtesselator *tess, ActiveRegion *reg )/* * Delete a region from the sweep line. This happens when the upper * and lower chains of a region meet (at a vertex on the sweep line). * The "inside" flag is copied to the appropriate mesh face (we could * not do this before -- since the structure of the mesh is always * changing, this face may not have even existed until now). */{ GLUhalfEdge *e = reg->eUp; GLUface *f = e->Lface; f->inside = reg->inside; f->anEdge = e; /* optimization for __gl_meshTessellateMonoRegion() */ DeleteRegion( tess, reg );}static GLUhalfEdge *FinishLeftRegions( GLUtesselator *tess, ActiveRegion *regFirst, ActiveRegion *regLast )/* * We are given a vertex with one or more left-going edges. All affected * edges should be in the edge dictionary. Starting at regFirst->eUp, * we walk down deleting all regions where both edges have the same * origin vOrg. At the same time we copy the "inside" flag from the * active region to the face, since at this point each face will belong * to at most one region (this was not necessarily true until this point * in the sweep). The walk stops at the region above regLast; if regLast * is NULL we walk as far as possible. At the same time we relink the * mesh if necessary, so that the ordering of edges around vOrg is the * same as in the dictionary. */{ ActiveRegion *reg, *regPrev; GLUhalfEdge *e, *ePrev; regPrev = regFirst; ePrev = regFirst->eUp; while( regPrev != regLast ) { regPrev->fixUpperEdge = FALSE; /* placement was OK */ reg = RegionBelow( regPrev ); e = reg->eUp; if( e->Org != ePrev->Org ) { if( ! reg->fixUpperEdge ) { /* Remove the last left-going edge. Even though there are no further * edges in the dictionary with this origin, there may be further * such edges in the mesh (if we are adding left edges to a vertex * that has already been processed). Thus it is important to call * FinishRegion rather than just DeleteRegion. */ FinishRegion( tess, regPrev ); break; } /* If the edge below was a temporary edge introduced by * ConnectRightVertex, now is the time to fix it. */ e = __gl_meshConnect( ePrev->Lprev, e->Sym ); if (e == NULL) longjmp(tess->env,1); if ( !FixUpperEdge( reg, e ) ) longjmp(tess->env,1); } /* Relink edges so that ePrev->Onext == e */ if( ePrev->Onext != e ) { if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); if ( !__gl_meshSplice( ePrev, e ) ) longjmp(tess->env,1); } FinishRegion( tess, regPrev ); /* may change reg->eUp */ ePrev = reg->eUp; regPrev = reg; } return ePrev;}static void AddRightEdges( GLUtesselator *tess, ActiveRegion *regUp, GLUhalfEdge *eFirst, GLUhalfEdge *eLast, GLUhalfEdge *eTopLeft, GLboolean cleanUp )/* * Purpose: insert right-going edges into the edge dictionary, and update
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -