kicad/polygon/kbool/src/scanbeam.cpp

1365 lines
45 KiB
C++

/*! \file src/scanbeam.cpp
\author Klaas Holwerda or Julian Smart
Copyright: 2001-2004 (C) Klaas Holwerda
Licence: see kboollicense.txt
RCS-ID: $Id: scanbeam.cpp,v 1.4 2009/09/07 19:23:28 titato Exp $
*/
// class scanbeam
// this class represents de space between two scanlines
#include "kbool/scanbeam.h"
#include <math.h>
#include <assert.h>
#include "kbool/booleng.h"
#include "kbool/graph.h"
#include "kbool/node.h"
//this here is to initialize the static iterator of scanbeam
//with NOLIST constructor
int recordsorter( kbRecord* , kbRecord* );
int recordsorter_ysp_angle( kbRecord* , kbRecord* );
int recordsorter_ysp_angle_back( kbRecord* rec1, kbRecord* rec2 );
ScanBeam::ScanBeam( Bool_Engine* GC ): DL_List<kbRecord*>()
{
_GC = GC;
_type = NORMAL;
_BI.Attach( this );
}
ScanBeam::~ScanBeam()
{
//first delete all record still in the beam
_BI.Detach();
remove_all( true );
//DeleteRecordPool();
}
void ScanBeam::SetType( kbNode* low, kbNode* high )
{
if ( low->GetX() < high->GetX() )
_type = NORMAL;
else
_type = FLAT;
}
/*
//catch node to link crossings
// must be sorted on ysp
int ScanBeam::FindCloseLinksAndCross(TDLI<kbLink>* _I,kbNode* _lowf)
{
int merges = 0;
kbRecord* record;
TDLI<kbRecord> _BBI=TDLI<kbRecord>(this);
if (_BI.count() > 1)
{
//first search a link towards this node
for(_BI.tohead(); !_BI.hitroot(); _BI++)
{
record=_BI.item();
if( (record->GetLink()->GetBeginNode()==_lowf) ||
(record->GetLink()->GetEndNode() ==_lowf)
)
break;
}
//NOTICE if the node "a_node" is not inside a record
//for instance to connected flat links (flatlinks not in beam)
//then IL will be at end (those will be catched at 90 degrees rotation)
if (_BI.hitroot())
{
return(merges);
}
//from IL search back for close links
_BBI.toiter(&_BI);
_BBI--;
while(!_BBI.hitroot())
{
record=_BBI.item();
if (record->Ysp() != _lowf->GetY())
break;
// the distance to the low node is smaller then the MARGE
if( (record->GetLink()->GetBeginNode()!=_lowf) &&
(record->GetLink()->GetEndNode() !=_lowf)
)
{ // the link is not towards the low node
record->GetLink()->AddCrossing(_lowf);
record->SetNewLink(record->GetLink()->ProcessCrossingsSmart(_I));
merges++;
}
_BBI--;
}
//from IL search forward for close links
_BBI.toiter(&_BI);
_BBI++;
while(!_BBI.hitroot())
{
record=_BBI.item();
if (record->Ysp() != _lowf->GetY())
// if (record->Ysp() < _lowf->GetY()-MARGE)
break;
// the distance to the low node is smaller then the MARGE
if( (record->GetLink()->GetBeginNode()!=_lowf) &&
(record->GetLink()->GetEndNode() !=_lowf)
)
{ // the link is not towards the low node
record->GetLink()->AddCrossing(_lowf);
record->SetNewLink(record->GetLink()->ProcessCrossingsSmart(_I));
merges++;
}
_BBI++;
}
}
return merges;
}
*/
/*
bool ScanBeam::Update(TDLI<kbLink>* _I,kbNode* _lowf)
{
bool found=false;
kbLink* link;
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record=_BI.item();
record->Calc_Ysp(_type,_low);
_BI++;
}
FindCloseLinksAndCross(_I,_lowf);
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record=_BI.item();
//records containing links towards the new low node
//are links to be marked for removal
if ((record->GetLink()->GetEndNode() == _lowf) ||
(record->GetLink()->GetBeginNode() == _lowf)
)
{
//cross here the links that meat eachother now
delete _BI.item();
_BI.remove();
//cross here the links that meat eachother now
_BI--;
if (!_BI.hitroot() && (_BI.count() > 1))
{
kbRecord* prev=_BI.item();
_BI++;
if (!_BI.hitroot())
{
if (!_BI.item()->Equal(prev)) // records NOT parallel
if (_BI.item()->GetLine()->Intersect(prev->GetLine(),MARGE))
{
//they did cross, integrate the crossings in the graph
//this may modify the links already part of the record
//this is why they are returned in set for the record
_BI.item()->SetNewLink(_BI.item()->GetLink()->ProcessCrossingsSmart(_I));
prev->SetNewLink(prev->GetLink()->ProcessCrossingsSmart(_I));
}
}
}
else
_BI++;
}
else
_BI++;
}
//writebeam();
//ONLY links towards the low node are possible to be added
//the bin flag will be set if it fits in the beam
//so for following beams it will not be checked again
while ( bool(link=_lowf->GetBinHighest(false)) )
{
kbRecord* record=new kbRecord(link);
// yp_new will always be the y of low node since all new links are
// from this node
record->SetYsp(_lowf->GetY());
record->Set_Flags(_type);
//need to calculate ysn to be able to sort this record in the right order
//this is only used when the insert node is equal for both records
// ins_smart and cross neighbour directly
// if empty then just insert
if (empty())
insend(record);
else
{
// put new item left of the one that is bigger
_BI.tohead();
while(!_BI.hitroot())
{
if (recordsorter_ysp_angle(record,_BI.item())==1)
break;
_BI++;
}
_BI.insbefore(record);
_BI--;_BI--; //just before the new record inserted
if (!_BI.hitroot())
{
kbRecord* prev=_BI.item();
_BI++; //goto the new record inserted
if (!_BI.item()->Equal(prev)) // records NOT parallel
{
if (_BI.item()->GetLine()->Intersect(prev->GetLine(),MARGE))
{
//this may modify the links already part of the record
//this is why they are returned in set for the record
_BI.item()->SetNewLink(_BI.item()->GetLink()->ProcessCrossingsSmart(_I));
prev->SetNewLink(prev->GetLink()->ProcessCrossingsSmart(_I));
}
}
}
else
_BI++;
kbRecord* prev=_BI.item(); //the new record
_BI++;
if (!_BI.hitroot() && !_BI.item()->Equal(prev)) // records NOT parallel
{
kbRecord* cur=_BI.item();
if (cur->GetLine()->Intersect(prev->GetLine(),MARGE))
{
//this may modify the links already part of the record
//this is why they are returned in set for the record
cur->SetNewLink(cur->GetLink()->ProcessCrossingsSmart(_I));
prev->SetNewLink(prev->GetLink()->ProcessCrossingsSmart(_I));
}
}
}
//remember this to calculate in/out values for each new link its polygon again.
GNI->insend(record->GetLink()->GetGraphNum());
found=true;
record->GetLink()->SetBeenHere();
}
FindCloseLinksAndCross(_I,_lowf);
//writebeam();
return(found);
}
*/
bool ScanBeam::FindNew( SCANTYPE scantype, TDLI<kbLink>* _I, bool& holes )
{
bool foundnew = false;
_low = _I->item()->GetBeginNode();
kbLink* link;
//if (!checksort())
// SortTheBeam();
lastinserted = 0;
//ONLY links towards the low node are possible to be added
//the bin flag will be set if it fits in the beam
//so for following beams it will not be checked again
while ( ( link = _low->GetBinHighest( false ) ) != NULL )
{
if ( ( link->GetEndNode()->GetX() == link->GetBeginNode()->GetX() ) //flatlink in flatbeam
&& ( ( scantype == NODELINK ) || ( scantype == LINKLINK ) || ( scantype == LINKHOLES ) )
)
{
switch( scantype )
{
case NODELINK:
{
//all vertical links in flatbeam are ignored
//normal link in beam
kbRecord * record = new kbRecord( link, _GC );
// yp_new will always be the y of low node since all new links are
// from this node
record->SetYsp( _low->GetY() );
record->Set_Flags();
// put new item left of the one that is lower in the beam
// The last one inserted in this loop, is already left of the current
// iterator position. So the new links are inerted in proper order.
link->SetRecordNode( _BI.insbefore( record ) );
_BI--;
foundnew = Process_PointToLink_Crossings() != 0 || foundnew;
delete record;
_BI.remove();
break;
}
case LINKLINK:
//is the new record a flat link
{
kbLine flatline = kbLine( link, _GC );
foundnew = Process_LinkToLink_Flat( &flatline ) || foundnew;
//flatlinks are not part of the beams, still they are used to find new beams
//they can be processed now if the beginnode does not change, since this is used to
//to find new beams. and its position does not change
//ProcessCrossings does take care of this
flatline.ProcessCrossings( _I );
break;
}
case LINKHOLES : //holes are never to flatlinks
assert( true );
default:
break;
}
}
else
{
//normal link in beam
kbRecord* record = new kbRecord( link, _GC );
// yp_new will always be the y of low node since all new links are
// from this node
record->SetYsp( _low->GetY() );
record->Set_Flags();
// put new item left of the one that is lower in the beam
// The last one inserted in this loop, is already left of the current
// iterator position. So the new links are inserted in proper order.
link->SetRecordNode( _BI.insbefore( record ) );
lastinserted++;
//_GC->Write_Log( "after insert" );
writebeam();
switch( scantype )
{
case NODELINK:
_BI--;
foundnew = Process_PointToLink_Crossings() != 0 || foundnew;
_BI++;
break;
case INOUT:
{
_BI--;
//now we can set the _inc flag
Generate_INOUT( record->GetLink()->GetGraphNum() );
_BI++;
}
break;
case GENLR:
{
//now we can set the a/b group flags based on the above link
_BI--;
_BI--;
kbRecord* above = 0;
if ( !_BI.hitroot() )
above = _BI.item();
_BI++;
//something to do for winding rule
if ( record->Calc_Left_Right( above ) )
{
delete record;
_BI.remove();
lastinserted--;
}
else
_BI++;
}
break;
case LINKHOLES:
_BI--;
holes = ProcessHoles( true, _I ) || holes;
_BI++;
break;
default:
break;
}
}
link->SetBeenHere();
}
writebeam();
return foundnew;
}
bool ScanBeam::RemoveOld( SCANTYPE scantype, TDLI<kbLink>* _I, bool& holes )
{
bool found = false;
bool foundnew = false;
DL_Iter<kbRecord*> _BBI = DL_Iter<kbRecord*>();
bool attached = false;
_low = _I->item()->GetBeginNode();
switch( scantype )
{
case INOUT:
case GENLR:
case LINKHOLES:
if ( _type == NORMAL )
{
if ( _low->GetBinHighest( true ) ) //is there something to remove
{
if ( scantype == LINKHOLES )
{
// Tophole links can be linked at the begin or end point, depending on
// which is higher in Y.
// A link pointing to the low node, and which is a tophole link,
// and which was not linked in sofar should be linked now.
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
//records containing links towards the new low node
//are links to be removed
if ( ( record->GetLink()->GetEndNode() == _low ) ||
( record->GetLink()->GetBeginNode() == _low )
)
{
holes = ProcessHoles( false, _I ) || holes;
}
_BI++;
}
}
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
//records containing links towards the new low node
//are links to be removed
if ( ( record->GetLink()->GetEndNode() == _low ) ||
( record->GetLink()->GetBeginNode() == _low )
)
{
delete _BI.item();
_BI.remove();
found = true;
}
else
{
//recalculate ysp for the new scanline
record->Calc_Ysp( _low );
_BI++;
}
}
// all records are renewed in Ysp.
// found links to remove, we search the new insert position for new links.
if ( found )
{
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
if ( record->Ysp() < _low->GetY() )
{
break;
}
_BI++;
}
}
}
else
{
// nothing is removed from the beam, still we moved forward with the scanline
// at the new _low, so we need to recalculate the intersections of the links
// with the new scanline.
// Also the the insert position for new links is determined, being the first
// link below _low.
_BBI.Attach( this );
_BBI.toroot();
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
record->Calc_Ysp( _low );
if ( !found && ( record->Ysp() < _low->GetY() ) )
{
found = true;
_BBI.toiter( &_BI );
}
_BI++;
}
_BI.toiter( &_BBI );
_BBI.Detach();
}
}
else // _type == NORMAL
{ //because the previous beam was flat the links to remove are
//below the last insert position
if ( _low->GetBinHighest( true ) ) //is there something to remove
{
if ( scantype == LINKHOLES )
{
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
//records containing links towards the new low node
//are links to be removed
if ( ( record->GetLink()->GetEndNode() == _low ) ||
( record->GetLink()->GetBeginNode() == _low )
)
{
holes = ProcessHoles( false, _I ) || holes;
}
_BI++;
}
}
//on record back bring us to the last inserted record
//or if nothing was inserted the record before the last deleted record
//if there was no record before the last deleted record this means
//we where at the beginning of the beam, so at root
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
//records containing links towards the new low node
//are links to be removed
if ( ( record->GetLink()->GetEndNode() == _low ) ||
( record->GetLink()->GetBeginNode() == _low )
)
{
delete _BI.item();
_BI.remove();
found = true;
}
else if ( found ) //only once in here
break;
else if ( record->Ysp() < _low->GetY() )
//if flatlinks are not in the beam nothing will be found
//this will bring us to the right insertion point
break;
else
_BI++;
}
}
else
{
//on record back bring us to the last inserted record
//or if nothing was inserted the record before the last deleted record
//if there was no record before the last deleted record this means
//we where at the beginning of the beam, so at root
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
if ( record->Ysp() < _low->GetY() )
break;
_BI++;
}
}
}
break;
case NODELINK:
case LINKLINK:
{
if ( _type == NORMAL )
{
Calc_Ysp();
if ( scantype == LINKLINK )
foundnew = Process_LinkToLink_Crossings() != 0 || foundnew;
else
SortTheBeam( false );
}
//else beam is already sorted because the added/removed flat links
//do not change the ysp of links already there, new non flat links
//are inserted in order, as result the beam stays sorted
if ( _low->GetBinHighest( true ) ) //is there something to remove
{
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
//records containing links towards the new low node
//are links to be removed
if ( ( record->GetLink()->GetEndNode() == _low ) ||
( record->GetLink()->GetBeginNode() == _low )
)
{
kbLine * line = record->GetLine();
if ( scantype == NODELINK )
foundnew = Process_PointToLink_Crossings() != 0 || foundnew;
line->ProcessCrossings( _I );
delete _BI.item();
_BI.remove();
found = true;
}
//because the beam is sorted on ysp, stop when nothing can be there to remove
//and the right insertion point for new links has been found
else if ( ( record->Ysp() < _low->GetY() ) )
break;
else
_BI++;
}
}
else
{
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
//because the beam is sorted on ysp, stop when
//the right insertion point for new links has been found
if ( ( record->Ysp() < _low->GetY() ) )
break;
_BI++;
}
}
}
break;
default:
break;
}
return foundnew;
}
/*
bool ScanBeam::RemoveOld(SCANTYPE scantype,TDLI<kbLink>* _I, bool& holes )
{
bool found = false;
bool foundnew = false;
DL_Iter<kbRecord*> _BBI=DL_Iter<kbRecord*>();
bool attached=false;
_low = _I->item()->GetBeginNode();
switch(scantype)
{
case INOUT:
case GENLR:
case LINKHOLES:
if (_type==NORMAL )
{
kbLink* link = _low->GetBinHighest(true);
if ( link ) //is there something to remove
{
link->SetRecordNode( NULL );
if ( scantype == LINKHOLES )
{
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record = _BI.item();
//records containing links towards the new low node
//are links to be removed
if ((record->GetLink()->GetEndNode() == _low) ||
(record->GetLink()->GetBeginNode() == _low)
)
{
holes = ProcessHoles(false,_I) || holes;
}
_BI++;
}
}
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record=_BI.item();
//records containing links towards the new low node
//are links to be removed
if ((record->GetLink()->GetEndNode() == _low) ||
(record->GetLink()->GetBeginNode() == _low)
)
{
delete _BI.item();
_BI.remove();
found=true;
}
else if (found) //only once in here
{
attached=true;
found=false;
//recalculate ysp for the new scanline
record->Calc_Ysp(_low);
_BI++;
}
else
{
//recalculate ysp for the new scanline
record->Calc_Ysp(_low);
_BI++;
}
}
}
else
{
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record=_BI.item();
record->Calc_Ysp(_low);
_BI++;
}
}
}
else
{ //because the previous beam was flat the links to remove are
//below the last insert position
kbLink* link;
link = _low->GetBinHighest(true);
if( link )//is there something to remove
{
link->SetRecordNode( NULL );
bool linkf = false;
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record = _BI.item();
if (record->GetLink() == link)
linkf = true;
_BI++;
}
if ( !linkf )
_BI.tohead();
if ( scantype == LINKHOLES )
{
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record=_BI.item();
//records containing links towards the new low node
//are links to be removed
if ((record->GetLink()->GetEndNode() == _low) ||
(record->GetLink()->GetBeginNode() == _low)
)
{
holes = ProcessHoles(false,_I) || holes;
}
_BI++;
}
}
//_BI.tonode( link->GetRecordNode() );
//delete _BI.item();
//_BI.remove();
//on record back bring us to the last inserted record
//or if nothing was inserted the record before the last deleted record
//if there was no record before the last deleted record this means
//we where at the beginning of the beam, so at root
//_BI << (lastinserted+1);
//_BI--;
//if (_BI.hitroot()) //only possible when at the begin of the beam
//found=false;
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record=_BI.item();
//records containing links towards the new low node
//are links to be removed
if ((record->GetLink()->GetEndNode() == _low) ||
(record->GetLink()->GetBeginNode() == _low)
)
{
if ( link != record->GetLink() )
{
break;
}
if ( link->GetRecordNode() != _BI.node() )
{
delete _BI.item();
_BI.remove();
}
else
{
delete _BI.item();
_BI.remove();
}
found=true;
}
else if (found) //only once in here
break;
else if (record->Ysp() < _low->GetY())
//if flatlinks are not in the beam nothing will be found
//this will bring us to the right insertion point
break;
else
_BI++;
}
}
else
{
//on record back bring us to the last inserted record
//or if nothing was inserted the record before the last deleted record
//if there was no record before the last deleted record this means
//we where at the beginning of the beam, so at root
//_BI << (lastinserted+ 1);
//_BI--;
//if (_BI.hitroot()) //only possible when at the begin of the beam
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record=_BI.item();
if (record->Ysp() < _low->GetY())
break;
_BI++;
}
}
}
break;
case NODELINK:
case LINKLINK:
{
if (_type == NORMAL)
{
Calc_Ysp();
if (scantype==LINKLINK)
foundnew = Process_LinkToLink_Crossings() !=0 || foundnew;
else
SortTheBeam( false );
}
//else beam is already sorted because the added/removed flat links
//do not change the ysp of links already there, new non flat links
//are inserted in order, as result the beam stays sorted
if (_low->GetBinHighest(true)) //is there something to remove
{
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record=_BI.item();
//records containing links towards the new low node
//are links to be removed
if ((record->GetLink()->GetEndNode() == _low) ||
(record->GetLink()->GetBeginNode() == _low)
)
{
kbLine* line=record->GetLine();
if (scantype==NODELINK)
foundnew = Process_PointToLink_Crossings() !=0 || foundnew;
line->ProcessCrossings(_I);
delete _BI.item();
_BI.remove();
found=true;
}
//because the beam is sorted on ysp, stop when nothing can be there to remove
//and the right insertion point for new links has been found
else if ((record->Ysp() < _low->GetY()))
break;
else
_BI++;
}
}
else
{
_BI.tohead();
while (!_BI.hitroot())
{
kbRecord* record=_BI.item();
//because the beam is sorted on ysp, stop when
//the right insertion point for new links has been found
if ((record->Ysp() < _low->GetY()))
break;
_BI++;
}
}
}
break;
default:
break;
}
return foundnew;
}
*/
void ScanBeam::SortTheBeam( bool backangle )
{
if ( backangle )
_BI.mergesort( recordsorter_ysp_angle_back );
else
_BI.mergesort( recordsorter_ysp_angle );
}
void ScanBeam::Calc_Ysp()
{
_BI.tohead();
while ( !_BI.hitroot() )
{
kbRecord * record = _BI.item();
// kbLink* link=_BI.item()->GetLink();
record->Calc_Ysp( _low );
_BI++;
}
}
// this function will set for all the records which contain a link with the
// corresponding graphnumber the inc flag.
// The inc flag's function is to see in a beam if we go deeper in the graph or not
void ScanBeam::Generate_INOUT( int graphnumber )
{
DIRECTION first_dir = GO_LEFT;
int diepte = 0;
DL_Iter<kbRecord*> _BBI = DL_Iter<kbRecord*>();
_BBI.Attach( this );
for( _BBI.tohead(); !_BBI.hitroot(); _BBI++ )
{
// recalculate _inc again
if ( _BBI.item()->GetLink()->GetGraphNum() == graphnumber )
{ //found a link that belongs to the graph
if ( diepte == 0 )
{ // first link found or at depth zero again
// the direction is important since this is used to find out
// if we go further in or out for coming links
first_dir = _BBI.item()->Direction();
_BBI.item()->GetLink()->SetInc( true );
diepte = 1;
}
else
{ // according to depth=1 links set depth
// verhoog of verlaag diepte
if ( _BBI.item()->Direction() == first_dir )
{
diepte++;
_BBI.item()->GetLink()->SetInc( true );
}
else
{
diepte--;
_BBI.item()->GetLink()->SetInc( false );
}
}
}
if ( _BBI.item() == _BI.item() ) break; //not need to do the rest, will come in a later beam
}
_BBI.Detach();
}
// function ProcessHoles
//
// this function will search the closest link to a hole
// step one, search for a link that is marked (this is a hole)
// step two, this is tricky, the closest link is the previous link in
// the beam, but only in the beam that contains the highest node
// from the marked link.
// why ? : if the marked link has for the begin and end node different
// x,y values, see below as link C
// B
// A +---------+
// +----------+
// ___--+
// ___---
// +--- C
//
// when we at first detect link C we would link it to link A, should work he
// but; we always link a hole at its topleft node, so the highest node
// and then we can't link to A but we should link to B
// so when we found the link, we will look if the node that will come
// in a later beam will be higher than the current, if so we will wait
// till that node comes around otherwise we will link this node to the
// closest link (prev in beam)
bool ScanBeam::ProcessHoles( bool atinsert, TDLI<kbLink>* _LI )
{
// The scanbeam must already be sorted at this moment
kbNode * topnode;
bool foundholes = false;
kbRecord* record = _BI.item();
kbLink* link = record->GetLink();
if ( !record->GetLine()->CrossListEmpty() )
{
SortTheBeam( atinsert );
// link the holes in the graph to a link above.
// a the link where the linecrosslist is not empty, means that
// there are links which refer to this link (must be linked to this link)
// make new nodes and links and set them, re-use the old link, so the links
// that still stand in the linecrosslist will not be lost.
// There is a hole that must be linked to this link !
TDLI<kbNode> I( record->GetLine()->GetCrossList() );
I.tohead();
while( !I.hitroot() )
{
topnode = I.item();
I.remove();
kbLine line( _GC );
line.Set( link );
B_INT Y = line.Calculate_Y( topnode->GetX() );
// Now we'll create new nodes and new links to make the link between
// the graphs.
//holes are always linked in a non hole or hole
//for a non hole this link will be to the right
//because non holes are right around
//for holes this will be to the right also,
//because they are left around but the link is always on the
//bottom of the hole
// linkA linkD
// o-------->--------NodeA------->------------o
// | |
// | |
// linkB v ^ linkBB
// | |
// | |
// outgoing* | | incoming*
// o------<---------topnode--------<----------o
//
// all holes are oriented left around
kbNode * leftnode; //left node of clossest link
( link->GetBeginNode()->GetX() < link->GetEndNode()->GetX() ) ?
leftnode = link->GetBeginNode() :
leftnode = link->GetEndNode();
kbNode *node_A = new kbNode( topnode->GetX(), Y, _GC );
kbLink *link_A = new kbLink( 0, leftnode, node_A, _GC );
kbLink *link_B = new kbLink( 0, node_A, topnode, _GC );
kbLink *link_BB = new kbLink( 0, topnode, node_A, _GC );
kbLink *link_D = _BI.item()->GetLink();
link_D->Replace( leftnode, node_A );
_LI->insbegin( link_A );
_LI->insbegin( link_B );
_LI->insbegin( link_BB );
//mark those two segments as hole linking segments
link_B->SetHoleLink( true );
link_BB->SetHoleLink( true );
//is where we come from/link to a hole
bool closest_is_hole = link->GetHole();
// if the polygon linked to, is a hole, this hole here
// just gets bigger, so we take over the links its hole marking.
link_A->SetHole( closest_is_hole );
link_B->SetHole( closest_is_hole );
link_BB->SetHole( closest_is_hole );
// we have only one operation at the time, taking
// over the operation flags is enough, since the linking segments will
// be part of that output for any operation done.
link_A->TakeOverOperationFlags( link );
link_B->TakeOverOperationFlags( link );
link_BB->TakeOverOperationFlags( link );
}
}
if ( link->IsTopHole() )
{
SortTheBeam( atinsert );
writebeam();
}
if ( link->IsTopHole() && !_BI.athead() )
{
// now we check if this hole should now be linked, or later
// we always link on the node with the maximum y value, Why ? because i like it !
// to link we put the node of the hole into the crosslist of the closest link !
assert( record->Direction() == GO_LEFT );
// he goes to the left
if ( atinsert )
{
if ( link->GetBeginNode()->GetY() <= link->GetEndNode()->GetY() )
{
topnode = link->GetEndNode();
//the previous link in the scanbeam == the closest link to the hole in vertical
//direction PUT this node into this link
_BI--;
_BI.item()->GetLine()->AddCrossing( topnode );
_BI++;
//reset tophole flag, hole has been processed
link->SetTopHole( false );
foundholes = true;
}
}
else //remove stage of links from te beam
{
//the tophole link was NOT linked at the insert stage, so it most be linked now
topnode = _BI.item()->GetLink()->GetBeginNode();
//the previous link in the scanbeam == the closest link to the hole in vertical
//direction PUT this node into this link
_BI--;
_BI.item()->GetLine()->AddCrossing( topnode );
_BI++;
//reset mark to flag that this hole has been processed
link->SetTopHole( false );
foundholes = true;
}
}
return foundholes;
}
//sort the records on Ysp if eqaul, sort on tangent at ysp
int recordsorter_ysp_angle( kbRecord* rec1, kbRecord* rec2 )
{
if ( rec1->Ysp() > rec2->Ysp() )
return( 1 );
if ( rec1->Ysp() < rec2->Ysp() )
return( -1 );
//it seems they are equal
B_INT rightY1;
if ( rec1->Direction() == GO_LEFT )
rightY1 = rec1->GetLink()->GetBeginNode()->GetY();
else
rightY1 = rec1->GetLink()->GetEndNode()->GetY();
B_INT rightY2;
if ( rec2->Direction() == GO_LEFT )
rightY2 = rec2->GetLink()->GetBeginNode()->GetY();
else
rightY2 = rec2->GetLink()->GetEndNode()->GetY();
if ( rightY1 > rightY2 )
return( 1 );
if ( rightY1 < rightY2 )
return( -1 );
return( 0 );
}
//sort the records on Ysp if eqaul, sort on tangent at ysp
int recordsorter_ysp_angle_back( kbRecord* rec1, kbRecord* rec2 )
{
if ( rec1->Ysp() > rec2->Ysp() )
return( 1 );
if ( rec1->Ysp() < rec2->Ysp() )
return( -1 );
//it seems they are equal
B_INT leftY1;
if ( rec1->Direction() == GO_RIGHT )
leftY1 = rec1->GetLink()->GetBeginNode()->GetY();
else
leftY1 = rec1->GetLink()->GetEndNode()->GetY();
B_INT leftY2;
if ( rec2->Direction() == GO_RIGHT )
leftY2 = rec2->GetLink()->GetBeginNode()->GetY();
else
leftY2 = rec2->GetLink()->GetEndNode()->GetY();
if ( leftY1 > leftY2 )
return( 1 );
if ( leftY1 < leftY2 )
return( -1 );
return( 0 );
}
// swap functie for cocktailsort ==> each swap means an intersection of links
bool swap_crossing_normal( kbRecord *a, kbRecord *b )
{
if ( !a->Equal( b ) ) // records NOT parallel
{
a->GetLine()->Intersect_simple( b->GetLine() );
return true;
}
return false;
}
int ScanBeam::Process_LinkToLink_Crossings()
{
// sort on y value of next intersection; and find the intersections
return _BI.cocktailsort( recordsorter_ysp_angle_back, swap_crossing_normal );
}
//catch node to link crossings
// must be sorted on ysp
int ScanBeam::Process_PointToLink_Crossings()
{
int merges = 0;
kbRecord* record;
if ( _BI.count() > 1 )
{
DL_Iter<kbRecord*> IL = DL_Iter<kbRecord*>( this );
IL.toiter( &_BI );
//from IL search back for close links
IL--;
while( !IL.hitroot() )
{
record = IL.item();
if ( record->Ysp() > _low->GetY() + _GC->GetInternalMarge() )
break;
// the distance to the lo/hi node is smaller then the _GC->GetInternalMarge()
if( ( record->GetLink()->GetBeginNode() != _low ) &&
( record->GetLink()->GetEndNode() != _low )
)
{ // the link is not towards the lohi node
record->GetLine()->AddCrossing( _low );
merges++;
}
IL--;
}
//from IL search forward for close links
IL.toiter( &_BI );
IL++;
while( !IL.hitroot() )
{
record = IL.item();
if ( record->Ysp() < _low->GetY() - _GC->GetInternalMarge() )
break;
// the distance to the lohi node is smaller then the booleng->Get_Marge()
if( ( record->GetLink()->GetBeginNode() != _low ) &&
( record->GetLink()->GetEndNode() != _low )
)
{ // the link is not towards the low node
record->GetLine()->AddCrossing( _low );
merges++;
}
IL++;
}
}
return merges;
}
int ScanBeam::Process_LinkToLink_Flat( kbLine* flatline )
{
int crossfound = 0;
kbRecord* record;
DL_Iter<kbRecord*> _BBI = DL_Iter<kbRecord*>();
_BBI.Attach( this );
_BBI.toiter( &_BI );
for( _BI.tohead(); !_BI.hitroot(); _BI++ )
{
record = _BI.item();
if ( record->Ysp() < ( flatline->GetLink()->GetLowNode()->GetY() - _GC->GetInternalMarge() ) )
break;//they are sorted so no other can be there
if ( ( record->Ysp() > ( flatline->GetLink()->GetLowNode()->GetY() - _GC->GetInternalMarge() ) )
&&
( record->Ysp() < ( flatline->GetLink()->GetHighNode()->GetY() + _GC->GetInternalMarge() ) )
)
{ //it is in between the flat link region
//create a new node at ysp and insert it in both the flatlink and the crossing link
if (
( record->GetLink()->GetEndNode() != flatline->GetLink()->GetHighNode() ) &&
( record->GetLink()->GetEndNode() != flatline->GetLink()->GetLowNode() ) &&
( record->GetLink()->GetBeginNode() != flatline->GetLink()->GetHighNode() ) &&
( record->GetLink()->GetBeginNode() != flatline->GetLink()->GetLowNode() )
)
{
kbNode * newnode = new kbNode( _low->GetX(), _BI.item()->Ysp(), _GC );
flatline->AddCrossing( newnode );
record->GetLine()->AddCrossing( newnode );
crossfound++;
}
}
}
_BI.toiter( &_BBI );
_BBI.Detach();
return crossfound;
}
bool ScanBeam::checksort()
{
// if empty then just insert
if ( empty() )
return true;
// put new item left of the one that is bigger
_BI.tohead();
kbRecord* prev = _BI.item();
_BI++;
while( !_BI.hitroot() )
{
kbRecord * curr = _BI.item();
if ( recordsorter_ysp_angle( prev, curr ) == -1 )
{
recordsorter_ysp_angle( prev, curr );
return false;
}
prev = _BI.item();
_BI++;
}
return true;
}
bool ScanBeam::writebeam()
{
#if KBOOL_DEBUG == 1
FILE * file = _GC->GetLogFile();
if ( file == NULL )
return true;
fprintf( file, "# beam %d \n", count() );
fprintf( file, " low %I64d %I64d \n", _low->GetX() , _low->GetY() );
fprintf( file, " type %d \n", _type );
if ( empty() )
{
fprintf( file, " empty \n" );
return true;
}
DL_Iter<kbRecord*> _BI( this );
// put new item left of the one that is bigger
_BI.tohead();
while( !_BI.hitroot() )
{
kbRecord * cur = _BI.item();
fprintf( file, " ysp %I64d \n", cur->Ysp() );
kbLink* curl = cur->GetLink();
fprintf( file, " linkbegin %I64d %I64d \n", curl->GetBeginNode()->GetX(), curl->GetBeginNode()->GetY() );
fprintf( file, " linkend %I64d %I64d \n", curl->GetEndNode()->GetX(), curl->GetEndNode()->GetY() );
if ( curl->GetEndNode()->GetX() == -2459565876494606883 )
fprintf( file, " linkend %I64d %I64d \n", curl->GetEndNode()->GetX(), curl->GetEndNode()->GetY() );
_BI++;
}
#endif
return true;
}