586 lines
12 KiB
C++
586 lines
12 KiB
C++
/*! \file src/booleng.cpp
|
|
\author Klaas Holwerda
|
|
|
|
Copyright: 2001-2004 (C) Klaas Holwerda
|
|
|
|
Licence: see kboollicense.txt
|
|
|
|
RCS-ID: $Id: booleng.cpp,v 1.7 2009/09/14 16:50:12 titato Exp $
|
|
*/
|
|
|
|
#include "kbool/booleng.h"
|
|
|
|
#include <time.h>
|
|
|
|
#include "kbool/link.h"
|
|
#include "kbool/line.h"
|
|
#include "kbool/node.h"
|
|
#include "kbool/graph.h"
|
|
#include "kbool/graphlst.h"
|
|
|
|
B_INT bmin( B_INT const value1, B_INT const value2 )
|
|
{
|
|
return( ( value1 < value2 ) ? value1 : value2 );
|
|
}
|
|
|
|
B_INT bmax( B_INT const value1, B_INT const value2 )
|
|
{
|
|
return( ( value1 > value2 ) ? value1 : value2 );
|
|
}
|
|
|
|
B_INT babs( B_INT a )
|
|
{
|
|
if ( a < 0 ) a = -a;
|
|
return a;
|
|
}
|
|
|
|
//-------------------------------------------------------------------/
|
|
//----------------- Bool_Engine_Error -------------------------------/
|
|
//-------------------------------------------------------------------/
|
|
|
|
Bool_Engine_Error::Bool_Engine_Error( string message, string header, int degree, int fatal )
|
|
{
|
|
_message = message;
|
|
_header = header;
|
|
|
|
_degree = degree;
|
|
_fatal = fatal;
|
|
|
|
}
|
|
|
|
Bool_Engine_Error::Bool_Engine_Error( const Bool_Engine_Error& a )
|
|
{
|
|
_message = a._message;
|
|
_header = a._header;
|
|
|
|
_degree = a._degree;
|
|
_fatal = a._fatal;
|
|
|
|
}
|
|
|
|
Bool_Engine_Error::~Bool_Engine_Error()
|
|
{
|
|
_message = "";
|
|
_header = "";
|
|
}
|
|
|
|
string Bool_Engine_Error::GetErrorMessage()
|
|
{
|
|
return _message;
|
|
}
|
|
|
|
string Bool_Engine_Error::GetHeaderMessage()
|
|
{
|
|
return _header;
|
|
}
|
|
|
|
int Bool_Engine_Error::GetErrorDegree()
|
|
{
|
|
return _degree;
|
|
}
|
|
|
|
int Bool_Engine_Error::GetFatal()
|
|
{
|
|
return _fatal;
|
|
}
|
|
|
|
//-------------------------------------------------------------------/
|
|
//----------------- Bool_Engine -------------------------------------/
|
|
//-------------------------------------------------------------------/
|
|
|
|
Bool_Engine::Bool_Engine()
|
|
{
|
|
_linkiter = new TDLI<kbLink>();
|
|
m_intersectionruns = 1;
|
|
|
|
m_orientationEntryMode = false;
|
|
m_doLinkHoles = true;
|
|
m_allowNonTopHoleLinking = true;
|
|
|
|
m_graphlist = new kbGraphList( this );
|
|
m_ACCUR = 1e-4;
|
|
m_WINDINGRULE = true;
|
|
m_GraphToAdd = NULL;
|
|
m_firstNodeToAdd = NULL;
|
|
m_lastNodeToAdd = NULL;
|
|
|
|
m_logfile = NULL;
|
|
|
|
#if KBOOL_LOG == 1
|
|
SetLog( true );
|
|
#else
|
|
SetLog( false );
|
|
#endif
|
|
}
|
|
|
|
Bool_Engine::~Bool_Engine()
|
|
{
|
|
if ( m_logfile != NULL )
|
|
fclose ( m_logfile );
|
|
|
|
delete _linkiter;
|
|
delete m_graphlist;
|
|
}
|
|
|
|
void Bool_Engine::SetLog( bool OnOff )
|
|
{
|
|
m_doLog = OnOff;
|
|
if ( m_doLog )
|
|
{
|
|
if ( m_logfile == NULL )
|
|
{
|
|
// create a new logfile
|
|
m_logfile = fopen( KBOOL_LOGFILE, "w" );
|
|
if ( m_logfile == NULL )
|
|
fprintf( stderr, "Bool_Engine: Unable to write to Boolean Engine logfile\n" );
|
|
else
|
|
{
|
|
time_t timer;
|
|
struct tm * today;
|
|
timer = time( NULL );
|
|
today = localtime( &timer );
|
|
|
|
fprintf( m_logfile, "Logfile created on:\t\t\t%s", ctime( &timer ) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( m_logfile != NULL )
|
|
{
|
|
fclose ( m_logfile );
|
|
m_logfile = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Bool_Engine::SetState( string process )
|
|
{
|
|
Write_Log( process );
|
|
}
|
|
|
|
void Bool_Engine::error( string text, string title )
|
|
{
|
|
Write_Log( "FATAL ERROR: ", title );
|
|
Write_Log( "FATAL ERROR: ", text );
|
|
throw Bool_Engine_Error( " Fatal Error", "Fatal Error", 9, 1 );
|
|
};
|
|
|
|
void Bool_Engine::info( string text, string title )
|
|
{
|
|
Write_Log( "FATAL ERROR: ", title );
|
|
Write_Log( "FATAL ERROR: ", text );
|
|
};
|
|
|
|
void Bool_Engine::SetMarge( double marge )
|
|
{
|
|
m_MARGE = marge;
|
|
Write_Log( "Bool_Engine::m_MARGE = %f\n", m_MARGE );
|
|
}
|
|
|
|
double Bool_Engine::GetAccur()
|
|
{
|
|
return m_ACCUR;
|
|
}
|
|
|
|
void Bool_Engine::SetRoundfactor( double roundfac )
|
|
{
|
|
m_ROUNDFACTOR = roundfac;
|
|
Write_Log( "Bool_Engine::m_ROUNDFACTOR = %f\n", m_ROUNDFACTOR );
|
|
}
|
|
|
|
double Bool_Engine::GetRoundfactor()
|
|
{
|
|
return m_ROUNDFACTOR;
|
|
}
|
|
|
|
double Bool_Engine::GetMarge()
|
|
{
|
|
return m_MARGE;
|
|
}
|
|
|
|
void Bool_Engine::SetDGrid( double dgrid )
|
|
{
|
|
m_DGRID = dgrid;
|
|
Write_Log( "Bool_Engine::m_DGRID = %f\n", m_DGRID );
|
|
}
|
|
|
|
double Bool_Engine::GetDGrid()
|
|
{
|
|
return m_DGRID;
|
|
}
|
|
|
|
void Bool_Engine::SetGrid( B_INT grid )
|
|
{
|
|
m_GRID = grid;
|
|
Write_Log( "Bool_Engine::m_GRID = %lld\n", m_GRID );
|
|
}
|
|
|
|
B_INT Bool_Engine::GetGrid()
|
|
{
|
|
return m_GRID;
|
|
}
|
|
|
|
void Bool_Engine::SetCorrectionAber( double aber )
|
|
{
|
|
m_CORRECTIONABER = aber;
|
|
Write_Log( "Bool_Engine::m_CORRECTIONABER = %f\n", m_CORRECTIONABER );
|
|
}
|
|
|
|
double Bool_Engine::GetCorrectionAber()
|
|
{
|
|
return m_CORRECTIONABER;
|
|
}
|
|
|
|
void Bool_Engine::SetCorrectionFactor( double aber )
|
|
{
|
|
m_CORRECTIONFACTOR = aber;
|
|
Write_Log( "Bool_Engine::m_CORRECTIONFACTOR = %f\n", m_CORRECTIONFACTOR );
|
|
}
|
|
|
|
double Bool_Engine::GetCorrectionFactor()
|
|
{
|
|
return m_CORRECTIONFACTOR;
|
|
}
|
|
|
|
void Bool_Engine::SetSmoothAber( double aber )
|
|
{
|
|
m_SMOOTHABER = aber;
|
|
Write_Log( "Bool_Engine::m_SMOOTHABER = %f\n", m_SMOOTHABER );
|
|
}
|
|
|
|
double Bool_Engine::GetSmoothAber()
|
|
{
|
|
return m_SMOOTHABER;
|
|
}
|
|
|
|
void Bool_Engine::SetMaxlinemerge( double maxline )
|
|
{
|
|
m_MAXLINEMERGE = maxline;
|
|
Write_Log( "Bool_Engine::m_MAXLINEMERGE = %f\n", m_MAXLINEMERGE );
|
|
}
|
|
|
|
double Bool_Engine::GetMaxlinemerge()
|
|
{
|
|
return m_MAXLINEMERGE;
|
|
}
|
|
|
|
void Bool_Engine::SetWindingRule( bool rule )
|
|
{
|
|
m_WINDINGRULE = rule;
|
|
}
|
|
|
|
bool Bool_Engine::GetWindingRule()
|
|
{
|
|
return m_WINDINGRULE;
|
|
}
|
|
|
|
|
|
void Bool_Engine::SetInternalMarge( B_INT marge )
|
|
{
|
|
m_MARGE = ( double )marge / m_GRID / m_DGRID;
|
|
}
|
|
|
|
B_INT Bool_Engine::GetInternalMarge()
|
|
{
|
|
return ( B_INT ) ( m_MARGE * m_GRID * m_DGRID );
|
|
}
|
|
|
|
double Bool_Engine::GetInternalCorrectionAber()
|
|
{
|
|
return m_CORRECTIONABER * m_GRID * m_DGRID;
|
|
}
|
|
|
|
double Bool_Engine::GetInternalCorrectionFactor()
|
|
{
|
|
return m_CORRECTIONFACTOR * m_GRID * m_DGRID;
|
|
}
|
|
|
|
double Bool_Engine::GetInternalSmoothAber()
|
|
{
|
|
return m_SMOOTHABER * m_GRID * m_DGRID;
|
|
}
|
|
|
|
B_INT Bool_Engine::GetInternalMaxlinemerge()
|
|
{
|
|
return ( B_INT ) ( m_MAXLINEMERGE * m_GRID * m_DGRID );
|
|
}
|
|
|
|
#define TRIALS 30
|
|
|
|
bool Bool_Engine::Do_Operation( BOOL_OP operation )
|
|
{
|
|
|
|
#if KBOOL_DEBUG
|
|
kbGraphList * saveme = new kbGraphList( m_graphlist );
|
|
#endif
|
|
|
|
try
|
|
{
|
|
switch ( operation )
|
|
{
|
|
case ( BOOL_OR ):
|
|
case ( BOOL_AND ):
|
|
case ( BOOL_EXOR ):
|
|
case ( BOOL_A_SUB_B ):
|
|
case ( BOOL_B_SUB_A ):
|
|
m_graphlist->Boolean( operation, m_intersectionruns );
|
|
break;
|
|
case ( BOOL_CORRECTION ):
|
|
m_graphlist->Correction();
|
|
break;
|
|
case ( BOOL_MAKERING ):
|
|
m_graphlist->MakeRings();
|
|
break;
|
|
case ( BOOL_SMOOTHEN ):
|
|
m_graphlist->Smoothen( GetInternalSmoothAber() );
|
|
break;
|
|
default:
|
|
{
|
|
error( "Wrong operation", "Command Error" );
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
catch ( Bool_Engine_Error & error )
|
|
{
|
|
|
|
#if KBOOL_DEBUG
|
|
delete m_graphlist;
|
|
m_graphlist = new kbGraphList( saveme );
|
|
m_graphlist->WriteGraphsKEY( this );
|
|
#endif
|
|
|
|
if ( m_logfile != NULL )
|
|
{
|
|
fclose ( m_logfile );
|
|
m_logfile = NULL;
|
|
}
|
|
|
|
info( error.GetErrorMessage(), "error" );
|
|
throw error;
|
|
}
|
|
catch( ... )
|
|
{
|
|
|
|
#if KBOOL_DEBUG
|
|
delete m_graphlist;
|
|
m_graphlist = new kbGraphList( saveme );
|
|
m_graphlist->WriteGraphsKEY( this );
|
|
#endif
|
|
|
|
if ( m_logfile != NULL )
|
|
{
|
|
fclose ( m_logfile );
|
|
m_logfile = NULL;
|
|
}
|
|
|
|
info( "Unknown exception", "error" );
|
|
throw ;
|
|
}
|
|
|
|
#if KBOOL_DEBUG
|
|
delete saveme;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Bool_Engine::StartPolygonAdd( GroupType A_or_B )
|
|
{
|
|
#if KBOOL_DEBUG
|
|
if ( m_logfile != NULL )
|
|
fprintf( m_logfile, "-> StartPolygonAdd(%d)\n", A_or_B );
|
|
#endif
|
|
if ( m_GraphToAdd != NULL )
|
|
return false;
|
|
|
|
kbGraph *myGraph = new kbGraph( this );
|
|
m_graphlist->insbegin( myGraph );
|
|
m_GraphToAdd = myGraph;
|
|
m_groupType = A_or_B;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Bool_Engine::AddPoint( double x, double y )
|
|
{
|
|
if ( m_GraphToAdd == NULL ){return false;}
|
|
|
|
double scaledx = x * m_DGRID * m_GRID;
|
|
double scaledy = y * m_DGRID * m_GRID;
|
|
|
|
if ( scaledx > MAXB_INT || scaledx < MINB_INT )
|
|
error( "X coordinate of vertex to big", "" );
|
|
if ( scaledy > MAXB_INT || scaledy < MINB_INT )
|
|
error( "Y coordinate of vertex to big", "" );
|
|
|
|
|
|
B_INT rintx = ( ( B_INT ) ( x * m_DGRID ) ) * m_GRID;
|
|
B_INT rinty = ( ( B_INT ) ( y * m_DGRID ) ) * m_GRID;
|
|
kbNode *myNode = new kbNode( rintx, rinty, this );
|
|
|
|
// adding first point to graph
|
|
if ( m_firstNodeToAdd == NULL )
|
|
{
|
|
#if KBOOL_DEBUG
|
|
if ( m_logfile != NULL )
|
|
{
|
|
fprintf( m_logfile, "-> AddPt() *FIRST* :" );
|
|
fprintf( m_logfile, " input: x = %f, y = %f\n", x, y );
|
|
fprintf( m_logfile, " input: x = %I64d, y = %I64d\n", rintx, rinty ) ;
|
|
}
|
|
#endif
|
|
|
|
m_firstNodeToAdd = ( kbNode * ) myNode;
|
|
m_lastNodeToAdd = ( kbNode * ) myNode;
|
|
}
|
|
else
|
|
{
|
|
#if KBOOL_DEBUG
|
|
if ( m_logfile != NULL )
|
|
{
|
|
fprintf( m_logfile, "-> AddPt():" );
|
|
fprintf( m_logfile, " input: x = %f, y = %f\n", x, y );
|
|
fprintf( m_logfile, " input: x = %I64d, y = %I64d\n", rintx, rinty ) ;
|
|
}
|
|
#endif
|
|
|
|
m_GraphToAdd->AddLink( m_lastNodeToAdd, myNode );
|
|
m_lastNodeToAdd = ( kbNode * ) myNode;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Bool_Engine::EndPolygonAdd()
|
|
{
|
|
if ( m_GraphToAdd == NULL ) {return false;}
|
|
|
|
m_GraphToAdd->AddLink( m_lastNodeToAdd, m_firstNodeToAdd );
|
|
m_GraphToAdd->SetGroup( m_groupType );
|
|
m_GraphToAdd = NULL;
|
|
m_lastNodeToAdd = NULL;
|
|
m_firstNodeToAdd = NULL;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Bool_Engine::StartPolygonGet()
|
|
{
|
|
if ( !m_graphlist->empty() )
|
|
{
|
|
m_getGraph = ( kbGraph* ) m_graphlist->headitem();
|
|
m_getLink = m_getGraph->GetFirstLink();
|
|
m_getNode = m_getLink->GetBeginNode();
|
|
m_numPtsInPolygon = m_getGraph->GetNumberOfLinks();
|
|
m_numNodesVisited = 0;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Bool_Engine::PolygonHasMorePoints()
|
|
{
|
|
// see if first point
|
|
if ( m_numNodesVisited == 0 )
|
|
{
|
|
// don't need to touch the m_getNode
|
|
m_numNodesVisited++;
|
|
return true;
|
|
}
|
|
|
|
if ( m_numNodesVisited < m_numPtsInPolygon )
|
|
{
|
|
// traverse to the next node
|
|
m_getNode = m_getLink->GetOther( m_getNode );
|
|
m_getLink = m_getLink->Forth( m_getNode );
|
|
|
|
m_numNodesVisited++;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void Bool_Engine::EndPolygonGet()
|
|
{
|
|
m_graphlist->removehead();
|
|
delete m_getGraph;
|
|
}
|
|
|
|
double Bool_Engine::GetPolygonXPoint()
|
|
{
|
|
return m_getNode->GetX() / m_GRID / m_DGRID;
|
|
}
|
|
|
|
double Bool_Engine::GetPolygonYPoint()
|
|
{
|
|
return m_getNode->GetY() / m_GRID / m_DGRID;
|
|
}
|
|
|
|
bool Bool_Engine::GetHoleSegment()
|
|
{
|
|
if ( m_getLink->GetHole() )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool Bool_Engine::GetHoleConnectionSegment()
|
|
{
|
|
if ( m_getLink->GetHoleLink() )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
kbEdgeType Bool_Engine::GetPolygonPointEdgeType()
|
|
{
|
|
// see if the point is the beginning of a false edge
|
|
if ( m_getLink->GetHoleLink() )
|
|
return KB_FALSE_EDGE;
|
|
else
|
|
// it's either an outside or inside edge
|
|
if ( m_getLink->GetHole() )
|
|
return KB_INSIDE_EDGE;
|
|
else
|
|
return KB_OUTSIDE_EDGE;
|
|
}
|
|
|
|
|
|
void Bool_Engine::Write_Log( string msg1 )
|
|
{
|
|
if ( m_logfile == NULL )
|
|
return;
|
|
|
|
fprintf( m_logfile, "%s \n", msg1.c_str() );
|
|
}
|
|
|
|
void Bool_Engine::Write_Log( string msg1, string msg2 )
|
|
{
|
|
if ( m_logfile == NULL )
|
|
return;
|
|
|
|
fprintf( m_logfile, "%s %s\n", msg1.c_str(), msg2.c_str() );
|
|
}
|
|
|
|
void Bool_Engine::Write_Log( string fmt, double dval )
|
|
{
|
|
if ( m_logfile == NULL )
|
|
return;
|
|
|
|
fprintf( m_logfile, fmt.c_str(), dval );
|
|
}
|
|
|
|
void Bool_Engine::Write_Log( string fmt, B_INT bval )
|
|
{
|
|
if ( m_logfile == NULL )
|
|
return;
|
|
|
|
fprintf( m_logfile, fmt.c_str(), bval );
|
|
}
|