1
0
forked from 0ad/0ad
0ad/source/dcdt/se/se_dcdt.cpp
janwas 5bf9bca9ef fix/disable warnings.
there are too many W4 and "potentially uninitialized", so those are
disabled by 0ad_warning_disable.h.

the silly "int x = strlen" and very dangerous "int x = (void*)p" (and
vice versa) problems are fixed.

This was SVN commit r5526.
2007-12-23 12:18:57 +00:00

711 lines
22 KiB
C++

#include "precompiled.h"
#include "0ad_warning_disable.h"
# include <math.h>
# include <stdlib.h>
# include <string.h>
# include "sr_box.h"
# include "sr_geo2.h"
# include "se_dcdt.h"
//# define SR_USE_TRACE1 // insert polygon
//# define SR_USE_TRACE2 // init
//# define SR_USE_TRACE3 // insert polygon
//# define SR_USE_TRACE4 // remove polygon
//# define SR_USE_TRACE5 // funnel
//# define SR_USE_TRACE6 // translate
//# define SR_USE_TRACE7 // ray
//# define SR_USE_TRACE8 // tri manager
//# define SR_USE_TRACE9 // extract contours
# include "sr_trace.h"
//DJD: declaring static member variables {
template <class T>
SrArray<SeLinkFace<T> *> SeLinkFace<T>::processing;
template <class T>
int SeLinkFace<T>::current = 0;
//declaring static member variables }
//=============================== SeDcdt ==================================
SeDcdt::SeDcdt ()
{
_mesh = new SeDcdtMesh;
_triangulator = new SeTriangulator
( SeTriangulator::ModeConstrained,
_mesh,
new SeDcdtTriManager,
0.000001 /*epsilon*/ );
_first_symedge = 0;
_cur_search_face = 0;
_xmin = _xmax = _ymin = _ymax = 0;
_radius = -1;
}
SeDcdt::~SeDcdt ()
{
delete _triangulator;
delete _mesh;
}
void SeDcdt::get_mesh_edges ( SrArray<SrPnt2>* constr, SrArray<SrPnt2>* unconstr )
{
SeDcdtEdge *e, *ei;
SeDcdtSymEdge *s;
SrArray<SrPnt2>* pa;
if ( constr ) constr->size(0);
if ( unconstr ) unconstr->size(0);
e = ei = _mesh->first()->edg();
do { if ( e->is_constrained() ) pa=constr; else pa=unconstr;
if ( pa )
{ s = e->se(); pa->push() = s->vtx()->p;
s = s->nxt(); pa->push() = s->vtx()->p;
}
e = e->nxt();
} while ( e!=ei );
}
//================================================================================
//================================ save ==========================================
//================================================================================
bool SeDcdt::save ( SrOutput& out )
{
int elems = _polygons.elements();
out << "SeDcdt\n\n";
out << "# domain:" << (elems>0?1:0) << " polygons:" << (elems-1) << srnl << srnl;
out << "epsilon " << _triangulator->epsilon() << srnl << srnl;
if ( _radius!=-1 )
out << "init_radius " << _radius << srnl << srnl;
if ( elems==0 ) return true;
int i, id, psize;
int maxid = _polygons.maxid();
for ( id=0; id<=maxid; id++ )
{ if ( !_polygons[id] ) continue;
SeDcdtInsPol& p = *_polygons[id];
if ( id==0 )
{ out << "domain\n"; }
else
{ out << "polygon " << id;
if ( p.open ) out << " open";
out << srnl;
}
psize = p.size();
for ( i=0; i<psize; i++ )
{ out << p[i]->p;
if ( i==psize-1 ) out<<';'; else out<<srspc;
}
out << srnl << srnl;
}
return true;
}
//================================================================================
//================================ load ==========================================
//================================================================================
static void _read_pol ( SrInput& inp, SrPolygon& pol )
{
pol.size ( 0 );
while ( 1 )
{ inp.get_token();
if ( inp.last_token()==';' ) break;
inp.unget_token ();
inp >> pol.push();
}
}
bool SeDcdt::load ( SrInput& inp )
{
SrPolygon pol;
pol.capacity ( 64 );
SrArray<int> invids;
float epsilon = 0.00001f;
float radius = -1.0f;
int id, nextid=1;
// signature:
inp.comment_style ( '#' );
inp.get_token ();
if ( inp.last_token()!="SeDcdt") return false;
while ( 1 )
{ inp.get_token ();
if ( inp.finished() ) break;
SrString& s = inp.last_token();
if ( s=="epsilon" )
{ inp >> epsilon;
}
if ( s=="init_radius" )
{ inp >> radius;
}
else if ( s=="domain" )
{ _read_pol ( inp, pol );
pol.open ( false );
init ( pol, epsilon, radius );
}
else if ( s=="polygon" )
{ if ( num_polygons()==0 ) return false;
inp >> id;
while ( id>nextid ) { invids.push()=_polygons.insert(); nextid++; }
inp.get_token();
if ( inp.last_token()=="open" )
{ pol.open(true); }
else
{ inp.unget_token();
pol.open(false);
}
_read_pol ( inp, pol );
insert_polygon ( pol );
nextid++;
}
}
while ( invids.size() ) _polygons.remove(invids.pop());
return true;
}
//================================================================================
//================================ init ==========================================
//================================================================================
void SeDcdt::init ( const SrPolygon& domain , float epsilon, float radius )
{
SR_TRACE2 ( "Starting init()..." );
// Reset cur searched face:
_cur_search_face = 0;
// Clear structures if needed:
if ( _first_symedge )
{ _mesh->destroy(); _first_symedge=0; _polygons.init(); }
// Checks parameters:
if ( domain.size()<3 || domain.open() )
sr_out.fatal_error ( "SeDcdt::init(): domain polygon must be simple and closed!");
// Calculates an external polygon (the border) :
SR_TRACE2 ( "Calculating External Polygon for domain with "<<domain.size()<<" points..." );
SrBox b;
domain.get_bounding_box ( b );
_xmin=b.a.x; _xmax=b.b.x; _ymin=b.a.y; _ymax=b.b.y;
float dx = (_xmax-_xmin)/5.0f;
float dy = (_ymax-_ymin)/5.0f;
if ( radius>0 )
{ if ( dx<radius ) dx=radius;
if ( dy<radius ) dy=radius;
}
if ( radius!=0 )
{ _xmax+=dx; _xmin-=dx; _ymax+=dy; _ymin-=dy; }
// Creates the triangulated external square (the border):
SR_TRACE2 ( "Creating External Polygon..." );
_triangulator->epsilon ( epsilon );
_first_symedge = (SeDcdtSymEdge*)_triangulator->init_as_triangulated_square
( _xmin, _ymin, _xmax, _ymin, _xmax, _ymax, _xmin, _ymax );
_radius = radius;
// Inserts the vertices of the domain according to radius:
if ( radius==0 )
{ _using_domain = false;
}
else
{ _using_domain = true;
insert_polygon ( domain );
}
}
void SeDcdt::get_bounds ( float& xmin, float& xmax, float& ymin, float& ymax ) const
{
xmin=_xmin; xmax=_xmax; ymin=_ymin; ymax=_ymax;
}
//================================================================================
//=========================== insert polygon ====================================
//================================================================================
int SeDcdt::insert_polygon ( const SrPolygon& pol )
{
int i, i1, id;
SeVertex* v;
SeFace* sface;
SR_TRACE1 ( "Inserting entry in the polygon set..." ); // put in _polygons
id = _polygons.insert();
SeDcdtInsPol& ip = *_polygons[id];
ip.open = pol.open();
SR_TRACE1 ( "Inserting polygon points..." ); // insert vertices
sface = _search_face();
for ( i=0; i<pol.size(); i++ )
{ v = _triangulator->insert_point ( pol[i].x, pol[i].y, sface );
if ( !v ) sr_out.fatal_error ( "Search failure in _insert_polygon()." );
ip.push() = (SeDcdtVertex*)v;
sface = v->se()->fac();
}
// should we keep here collinear vertices (which are not corners)?
// they can be removed as Steiner vertices when removing another intersecting polygon
_cur_search_face=0; // Needed because edge constraint may call kef
SR_TRACE1 ( "Inserting polygon edges constraints..." ); // insert edges
for ( i=0; i<ip.size(); i++ )
{ i1 = (i+1)%ip.size();
if ( i1==0 && ip.open ) break; // do not close the polygon
if ( !_triangulator->insert_line_constraint ( ip[i], ip[i1], id ) )
sr_out.fatal_error ( "Unable to insert constraint in _insert_polygon()." );
}
return id;
}
int SeDcdt::polygon_max_id () const
{
return _polygons.maxid();
}
int SeDcdt::num_polygons () const
{
return _polygons.elements();
}
//================================================================================
//=========================== remove polygon ====================================
//================================================================================
void SeDcdt::_find_intermediate_vertices_and_edges ( SeDcdtVertex* vini, int oid )
{
SeDcdtEdge *e;
SeDcdtVertex *v;
SeDcdtSymEdge *s, *si;
_mesh->begin_marking ();
_varray.size(0); _earray.size(0); _stack.size(0);
// initialize stack with all constrained edges from v:
_varray.push() = vini;
_mesh->mark ( vini );
s = si = vini->se();
do { e = s->edg();
if ( e->has_id(oid) )
{ _stack.push() = s;
_earray.push() = s;
_mesh->mark ( e );
}
s = s->rot();
} while ( s!=si );
// advances untill all edges are reached:
while ( _stack.size() )
{ s = si = _stack.pop()->nxt();
v = s->vtx();
if ( !_mesh->marked(v) )
{ _varray.push() = v;
_mesh->mark ( v );
}
do { e = s->edg();
if ( !_mesh->marked(e) && e->has_id(oid) )
{ _stack.push() = s;
_earray.push() = s;
_mesh->mark ( e );
}
s = s->rot();
} while ( s!=si );
}
_mesh->end_marking ();
}
bool SeDcdt::_is_intersection_vertex
( SeDcdtVertex* v, int id, SeDcdtVertex*& v1, SeDcdtVertex*& v2 )
{
SeDcdtSymEdge *si, *s;
v1 = v2 = 0;
// at this point, it is guaranteed that only two constraints with index id
// are adjacent to v, because there is a test that ensures that only two
// constrained edges are incident to v in _remove_vertex_if_possible().
// (however these two constrained edges may have more than one id)
si = s = v->se();
do { if ( s->edg()->has_id(id) )
{ if ( v1==0 ) v1=s->nxt()->vtx();
else if ( v2==0 ) { v2=s->nxt()->vtx(); break; }
}
s = s->rot();
} while ( s!=si );
if ( v1==0 || v2==0 ) return false; // v is an extremity of an open constraint
double d = sr_point_segment_dist ( v->p.x, v->p.y, v1->p.x, v1->p.y, v2->p.x, v2->p.y );
return d<=_triangulator->epsilon()? true:false;
}
void SeDcdt::_remove_vertex_if_possible ( SeDcdtVertex* v, const SrArray<int>& vids )
{
SR_TRACE4 ( "Try to remove vertex with ref="<<vids.size()<<"..." );
// Easier case, vertex is no more used:
if ( vids.size()==0 ) // dangling vertex
{ SR_TRACE4 ( "Removing dangling vertex" );
_triangulator->remove_vertex(v);
return;
}
int i;
int num_of_incident_constrained_edges=0;
SeDcdtSymEdge *s;
s = v->se();
do { if ( s->edg()->is_constrained() ) num_of_incident_constrained_edges++;
s = s->rot();
} while ( s!=v->se() );
if ( num_of_incident_constrained_edges!=2 ) return;
// test if all polygons using the vertex no more need it:
SeDcdtVertex *v1, *v2;
SrArray<SeDcdtVertex*>& va = _varray2; // internal buffer
va.size ( 0 );
// (note that we can have vids.size()>1 at this point)
for ( i=0; i<vids.size(); i++ )
{ if ( !_is_intersection_vertex(v,vids[i],v1,v2) )
{ SR_TRACE4 ( "Not an intersection vertex!" );
return;
}
va.push()=v1; va.push()=v2;
}
SR_TRACE4 ( "Removing one intersection vertex..." );
_triangulator->remove_vertex(v);
// and recover all deleted constraints:
for ( i=0; i<vids.size(); i++ )
{ _triangulator->insert_line_constraint ( va[i*2], va[i*2+1], vids[i] );
}
}
void SeDcdt::remove_polygon ( int polygonid )
{
int i;
SR_TRACE4 ( "Entering remove_polygon..." );
if ( polygonid==0 )
sr_out.fatal_error("Domain cannot be removed by SeDcdt::remove_polygon()");
if ( polygonid<0 || polygonid>_polygons.maxid() )
sr_out.fatal_error("Invalid id sent to SeDcdt::remove_polygon()");
if ( !_polygons[polygonid] )
sr_out.fatal_error("SeDcdt::remove_polygon(): polygon already removed");
// _search_face can be invalidated so make it unavailable:
_cur_search_face = 0;
// Recuperate intermediate vertices inserted as Steiner points:
SR_TRACE4 ( "Recuperating full polygon..." );
_find_intermediate_vertices_and_edges ( _polygons[polygonid]->get(0), polygonid );
SR_TRACE4 ( "polygon has "<<_varray.size()<<" vertices " << "and "<<_earray.size()<<" edges." );
// Remove all ids which are equal to polygonid in the edge constraints
SR_TRACE4 ( "Updating edge constraints..." );
for ( i=0; i<_earray.size(); i++ )
{ _earray[i]->edg()->remove_id ( polygonid ); // remove all occurences
}
SR_TRACE4 ( "Removing free vertices..." );
for ( i=0; i<_varray.size(); i++ )
{ _varray[i]->get_references ( _ibuffer ); //vobs
SR_TRACE4 ( "Vertex "<<i<<": (" << _varray[i]->p << ")" );
_remove_vertex_if_possible ( _varray[i], _ibuffer );
}
_polygons.remove ( polygonid );
SR_TRACE4 ( "Finished." );
}
//================================================================================
//============================= get polygon ======================================
//================================================================================
void SeDcdt::get_polygon ( int polygonid, SrPolygon& polygon )
{
polygon.size(0);
if ( polygonid<0 || polygonid>_polygons.maxid() ) return;
if ( !_polygons[polygonid] ) return;
SeDcdtInsPol& p = *_polygons[polygonid];
int i;
polygon.size ( p.size() );
polygon.open ( p.open? true:false );
for ( i=0; i<p.size(); i++ )
polygon[i] = p[i]->p;
}
void SeDcdt::get_triangulated_polygon ( int polygonid, SrArray<SeDcdtVertex*>* vtxs, SrArray<SeDcdtEdge*>* edgs )
{
if ( polygonid<0 || polygonid>_polygons.maxid() ) return;
if ( !_polygons[polygonid] ) return;
_find_intermediate_vertices_and_edges ( _polygons[polygonid]->get(0), polygonid );
if ( vtxs ) *vtxs = _varray;
if ( edgs ) // need to convert symedges to edges
{ int i;
edgs->size ( _earray.size() );
for ( i=0; i<edgs->size(); i++ ) (*edgs)[i] = _earray[i]->edg();
}
}
//================================================================================
//=========================== ray intersection ===================================
//================================================================================
static bool _intersects ( double p1x, double p1y, double p2x, double p2y, SeDcdtSymEdge* s )
{
SeDcdtVertex* v1 = s->vtx();
SeDcdtVertex* v2 = s->nxt()->vtx();
return sr_segments_intersect ( p1x, p1y, p2x, p2y,
v1->p.x, v1->p.y, v2->p.x, v2->p.y );
}
void SeDcdt::ray_intersection ( float x1, float y1, float x2, float y2,
SrArray<int>& polygons, int depth )
{
SeTriangulator::LocateResult res;
SeBase *ses;
SeDcdtSymEdge *s;
polygons.size(0);
if ( depth==0 ) return;
double p1x=x1, p1y=y1, p2x=x2, p2y=y2;
res = _triangulator->locate_point ( _search_face(), p1x, p1y, ses );
if ( res==SeTriangulator::NotFound )
{ SR_TRACE7 ( "First point not found!" );
return;
}
s = (SeDcdtSymEdge*)ses;
_cur_search_face = s->fac();
// Find first intersection with the first triangle:
int i=0;
while ( !_intersects(p1x,p1y,p2x,p2y,s) )
{ s=s->nxt();
if ( i++==3 )
{ SR_TRACE7 ( "None intersections, ray in triangle!" );
return;
}
}
// Continues until end:
while ( true )
{ if ( s->edg()->is_constrained() )
{ //int r = rand() % e->ids_size;
SR_TRACE7 ( "found one!" );
SrArray<int>& a = s->edg()->ids;
for ( i=0; i<a.size(); i++ )
if ( polygons.size()<depth ) polygons.push()=a[i];
if ( depth>0 && polygons.size()>=depth )
{ SR_TRACE7 ( "depth reached!" ); return; }
}
s = s->sym();
if ( _intersects(p1x,p1y,p2x,p2y,s->nxt()) ) s=s->nxt();
else
if ( _intersects(p1x,p1y,p2x,p2y,s->pri()) ) s=s->pri();
else
{ SR_TRACE7 ( "Finished Ray!" ); return; }
}
}
void SeDcdt::_add_contour ( SeDcdtSymEdge* s, SrPolygon& vertices, SrArray<int>& pindices )
{
SeDcdtSymEdge* si=s;
pindices.push() = vertices.size();
SR_TRACE9 ( "Begin contour: "<<pindices.top() );
do { vertices.push() = s->vtx()->p;
_mesh->mark ( s->edg() );
do { s=s->rot(); } while ( !s->edg()->is_constrained() );
s = s->sym();
} while ( s!=si );
pindices.push() = vertices.size()-1;
SR_TRACE9 ( "End contour: "<<pindices.top() );
}
SeDcdtSymEdge* SeDcdt::_find_one_obstacle ()
{
SeDcdtSymEdge *s;
while ( 1 )
{ if ( _earray.empty() ) break;
s = _earray.pop();
if ( _mesh->marked(s->edg()) ) continue;
if ( s->edg()->is_constrained() ) return s;
_mesh->mark(s->edg());
s = s->sym()->nxt();
if ( !_mesh->marked(s->edg()) ) { _earray.push()=s; }
s = s->nxt();
if ( !_mesh->marked(s->edg()) ) { _earray.push()=s; }
}
return 0;
}
void SeDcdt::extract_contours ( SrPolygon& vertices, SrArray<int>& pindices, float x, float y )
{
SeTriangulator::LocateResult res;
SeBase *ses;
vertices.size(0);
pindices.size(0);
SR_TRACE9 ( "Begin Extract" );
res = _triangulator->locate_point ( _search_face(), x, y, ses );
if ( res==SeTriangulator::NotFound ) return;
_mesh->begin_marking();
_earray.size(0);
SeDcdtSymEdge *s, *si;
s = si = (SeDcdtSymEdge*)ses;
do { _earray.push() = s;
s = s->nxt();
} while ( s!=si );
while (1)
{ s = _find_one_obstacle ();
if ( !s ) break;
_add_contour ( s, vertices, pindices );
}
_mesh->end_marking();
SR_TRACE9 ( "End Extract" );
}
//================================================================================
//=========================== inside polygon ====================================
//================================================================================
static int interhoriz ( float px, float py, float p1x, float p1y, float p2x, float p2y )
{
if ( p1y>p2y ) { float tmp; SR_SWAP(p1x,p2x); SR_SWAP(p1y,p2y); } // swap
if ( p1y>=py ) return false; // not intercepting
if ( p2y<py ) return false; // not intercepting or = max
float x2 = p1x + (py-p1y) * (p2x-p1x) / (p2y-p1y);
return (px<x2)? true:false;
}
int SeDcdt::inside_polygon ( float x, float y , SrArray<int>* allpolys )
{
if ( _polygons.elements()<=1 ) return -1; // if there is only the domain, return.
int cont=0, i, size;
SeDcdtInsPol* pol;
SeDcdtVertex *v1, *v2;
if ( allpolys ) allpolys->size(0);
int polid=0;
if ( _using_domain ) polid++;
for ( ; polid<=_polygons.maxid(); polid++ )
{ pol = _polygons[polid];
if ( !pol ) continue;
size = pol->size();
for ( i=0; i<size; i++ )
{ v1 = pol->get(i);
v2 = pol->get( (i+1)%size );
cont ^= interhoriz ( x, y, v1->p.x, v1->p.y, v2->p.x, v2->p.y );
}
if (cont)
{ if ( allpolys ) allpolys->push()=polid;
else return polid;
}
}
if ( allpolys ) if ( allpolys->size()>0 ) return allpolys->get(0);
return -1;
}
int SeDcdt::pick_polygon ( float x, float y )
{
if ( _polygons.elements()==0 ) return -1;
if ( _using_domain && _polygons.elements()==1 ) return -1; // if there is only the domain, return.
SeTriangulator::LocateResult res;
SeBase *sse;
res = _triangulator->locate_point ( _search_face(), x, y, sse );
if ( res==SeTriangulator::NotFound ) return -1;
SeDcdtSymEdge *s = (SeDcdtSymEdge*)sse;
SrPnt2 pt(x,y);
float d1 = dist2 ( pt, s->vtx()->p );
float d2 = dist2 ( pt, s->nxt()->vtx()->p );
float d3 = dist2 ( pt, s->nxt()->nxt()->vtx()->p );
if ( d2<=d1 && d2<=d3 ) s=s->nxt();
if ( d3<=d1 && d3<=d2 ) s=s->nxt()->nxt();
SeDcdtSymEdge* k=s;
do { SrArray<int>& ids = k->edg()->ids;
if ( ids.size()>0 ) { if ( !_using_domain || ids[0]>0 ) return ids[0]; } // 0 is the domain
k=k->rot();
} while ( k!=s );
return -1;
}
//================================================================================
//=============================== search path ======================================
//================================================================================
bool SeDcdt::search_path ( float x1, float y1, float x2, float y2,
const SeDcdtFace* iniface, bool vistest )
{
// fast security test to ensure at least that points are not outside the border limits:
if ( x1<_xmin || x1>_xmax || x2<_xmin || x2>_xmax ) return false;
if ( !iniface ) iniface = _search_face();
bool found = _triangulator->search_path ( x1, y1, x2, y2, iniface, vistest );
// to optimize searching for next queries around the same point,
// set the next starting search face to the first channel face:
if ( _triangulator->get_channel_interior_edges().size()>0 )
_cur_search_face = (SeDcdtFace*)_triangulator->get_channel_interior_edges()[0]->fac();
return found;
}
//============================ End of File ===============================