📄 sweep.c
字号:
/*
** License Applicability. Except to the extent portions of this file are
** made subject to an alternative license as permitted in the SGI Free
** Software License B, Version 1.1 (the "License"), the contents of this
** file are subject only to the provisions of the License. You may not use
** this file except in compliance with the License. You may obtain a copy
** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
**
** http://oss.sgi.com/projects/FreeB
**
** Note that, as provided in the License, the Software is distributed on an
** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
**
** Original Code. The Original Code is: OpenGL Sample Implementation,
** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
** Copyright in any portions created by third parties is as indicated
** elsewhere herein. All Rights Reserved.
**
** Additional Notice Provisions: The application programming interfaces
** established by SGI in conjunction with the Original Code are The
** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
** Window System(R) (Version 1.3), released October 19, 1998. This software
** was created using the OpenGL(R) version 1.2.1 Sample Implementation
** published by SGI, but has not been independently verified as being
** compliant with the OpenGL(R) version 1.2.1 Specification.
**
*/
/*
** 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_PROGRAM
extern 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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -