diff --git a/change_log.txt b/change_log.txt index defc65cd42..f215ec579a 100644 --- a/change_log.txt +++ b/change_log.txt @@ -5,6 +5,15 @@ Started 2007-June-11 Please add newer entries at the top, list the date and your name with email address. +2008-May-15 UPDATE Jean-Pierre Charras +================================================================================ ++eeschema: + Solved netlist problems for multiple parts per package components + in complex hierarchies. + B.O.M. generation still have a minor problem wih this. + + + 2008-May-5 UPDATE Dick Hollenbeck ================================================================================ +common.c diff --git a/eeschema/backanno.cpp b/eeschema/backanno.cpp index cacb05d4d5..44b882ab72 100644 --- a/eeschema/backanno.cpp +++ b/eeschema/backanno.cpp @@ -4,7 +4,6 @@ ****************************************************************/ #include "fctsys.h" -#include "gr_basic.h" #include "common.h" #include "program.h" @@ -15,8 +14,6 @@ #include "dialog_backanno.cpp" -#include "protos.h" - /**************************************************************/ SCH_COMPONENT * WinEDA_SchematicFrame::FindComponentByRef( const wxString& reference ) @@ -72,7 +69,7 @@ bool WinEDA_SchematicFrame::ProcessStuffFile( FILE* StuffFile ) continue; #if defined(DEBUG) - printf( " %s %s\n", CONV_TO_UTF8(Cmp->m_Field[REFERENCE].m_Text), + printf( " %s %s\n", CONV_TO_UTF8(Cmp->m_Field[REFERENCE].m_Text), CONV_TO_UTF8(Cmp->m_Field[VALUE].m_Text) ); #endif TextField = &Cmp->m_Field[FOOTPRINT]; diff --git a/eeschema/component_class.cpp b/eeschema/component_class.cpp index 6a891fa954..96d341e20e 100644 --- a/eeschema/component_class.cpp +++ b/eeschema/component_class.cpp @@ -293,9 +293,7 @@ SCH_COMPONENT::SCH_COMPONENT( const wxPoint& aPos ) : m_Pos = aPos; - //m_FlagControlMulti = 0; - m_UsedOnSheets.Clear(); - m_Convert = 0; /* Gestion des mutiples representations (conversion De Morgan) */ + m_Convert = 0; /* De Morgan Handling */ /* The rotation/mirror transformation matrix. pos normal*/ m_Transform[0][0] = 1; @@ -469,32 +467,40 @@ void SCH_COMPONENT::ClearAnnotation( DrawSheetPath* aSheet ) defRef.Append( wxT( "?" ) ); wxString multi = wxT( "1" ); - wxString NewHref; - wxString path; - if( aSheet ) - path = GetPath( aSheet );; - for( unsigned int ii = 0; ii< m_PathsAndReferences.GetCount(); ii++ ) + + if ( KeepMulti ) // We cannot remove all annotations: part selection must be kept { - // Break hierachical reference in path, ref and multi selection: - reference_fields = wxStringTokenize( m_PathsAndReferences[ii], separators ); - if( aSheet == NULL || reference_fields[0].Cmp( path ) == 0 ) + wxString NewHref; + wxString path; + if( aSheet ) + path = GetPath( aSheet );; + for( unsigned int ii = 0; ii< m_PathsAndReferences.GetCount(); ii++ ) { - if( KeepMulti ) // Get and keep part selection - multi = reference_fields[2]; - NewHref = reference_fields[0]; - NewHref << wxT( " " ) << defRef << wxT( " " ) << multi; - m_PathsAndReferences[ii] = NewHref; + // Break hierachical reference in path, ref and multi selection: + reference_fields = wxStringTokenize( m_PathsAndReferences[ii], separators ); + if( aSheet == NULL || reference_fields[0].Cmp( path ) == 0 ) + { + if( KeepMulti ) // Get and keep part selection + multi = reference_fields[2]; + NewHref = reference_fields[0]; + NewHref << wxT( " " ) << defRef << wxT( " " ) << multi; + m_PathsAndReferences[ii] = NewHref; + } } } + else + { + m_PathsAndReferences.Empty(); // Empty strings, but does not free memory because a new annotation will reuse it + m_Multi = 1; + } + // These 2 changes do not work in complex hierarchy. // When a clear annotation is made, the calling function must call a // UpdateAllScreenReferences for the active sheet. - // But this call does not made here. + // But this call cannot made here. m_Field[REFERENCE].m_Text = defRef; //for drawing. - if( !KeepMulti ) - m_Multi = 1; } @@ -510,8 +516,6 @@ SCH_COMPONENT* SCH_COMPONENT::GenCopy() new_item->m_ChipName = m_ChipName; new_item->m_PrefixString = m_PrefixString; - //new_item->m_FlagControlMulti = m_FlagControlMulti; - new_item->m_UsedOnSheets = m_UsedOnSheets; new_item->m_Convert = m_Convert; new_item->m_Transform[0][0] = m_Transform[0][0]; new_item->m_Transform[0][1] = m_Transform[0][1]; diff --git a/eeschema/component_class.h b/eeschema/component_class.h index d91e52e277..fd53f89284 100644 --- a/eeschema/component_class.h +++ b/eeschema/component_class.h @@ -100,8 +100,6 @@ public: * determined, upon file load, by the first non-digits in the reference fields. */ PartTextStruct m_Field[NUMBER_OF_FIELDS]; - //int m_FlagControlMulti; - ArrayOfSheetLists m_UsedOnSheets; // Used as flags when calculating netlist int m_Convert; /* Gestion (management) des mutiples representations (ex: conversion De Morgan) */ int m_Transform[2][2]; /* The rotation/mirror transformation matrix. */ diff --git a/eeschema/dangling_ends.cpp b/eeschema/dangling_ends.cpp index 7e8d30d8c6..162d9b2c72 100644 --- a/eeschema/dangling_ends.cpp +++ b/eeschema/dangling_ends.cpp @@ -402,7 +402,7 @@ DanglingEndHandle* RebuildEndList( EDA_BaseStruct* DrawList ) #undef STRUCT #define STRUCT ( (SCH_COMPONENT*) DrawItem ) EDA_LibComponentStruct* Entry; - Entry = FindLibPart( STRUCT->m_ChipName, wxEmptyString, FIND_ROOT ); + Entry = FindLibPart( STRUCT->m_ChipName.GetData(), wxEmptyString, FIND_ROOT ); if( Entry == NULL ) break; diff --git a/eeschema/dialog_build_BOM.cpp b/eeschema/dialog_build_BOM.cpp index 24912ca6c1..a5e3be8e79 100644 --- a/eeschema/dialog_build_BOM.cpp +++ b/eeschema/dialog_build_BOM.cpp @@ -691,13 +691,6 @@ void WinEDA_Build_BOM_Frame::GenereListeOfItems( const wxString& FullFileName ) GenListeCmp( List ); -#if 0 - for( int i = 0; iGetValue() ) DeleteSubCmp( List, NbItems ); @@ -785,20 +772,17 @@ void WinEDA_Build_BOM_Frame::GenereListeOfItems( const wxString& FullFileName ) int GenListeCmp( ListComponent* List ) /****************************************/ -/* Routine de generation de la liste des elements utiles du dessin - * Si List == NULL: comptage des elements - * Sinon remplissage de la liste - * Initialise "FlagControlMulti" a SheetNumber pour la sortie des listes - * et m_Father comme pointeur sur la sheet d'appartenance +/* Creates the list of components in the schematic * * routine for generating a list of the used components. * if List == null, just returns the count. if not, fills the list. * goes through the sheets, not the screens, so that we account for * multiple instances of a given screen. + * Also Initialise m_Father as pointer pointeur of the SCH_SCREN parent */ { int ItemCount = 0; - EDA_BaseStruct* DrawList; + EDA_BaseStruct* SchItem; SCH_COMPONENT* DrawLibItem; DrawSheetPath* sheet; @@ -807,31 +791,23 @@ int GenListeCmp( ListComponent* List ) for( sheet = SheetList.GetFirst(); sheet != NULL; sheet = SheetList.GetNext() ) { - DrawList = sheet->LastDrawList(); - while( DrawList ) + for ( SchItem = sheet->LastDrawList(); SchItem;SchItem = SchItem->Next() ) { - switch( DrawList->Type() ) + if( SchItem->Type() != TYPE_SCH_COMPONENT ) + continue; + + ItemCount++; + DrawLibItem = (SCH_COMPONENT*) SchItem; + DrawLibItem->m_Parent = sheet->LastScreen(); + if( List ) { - case TYPE_SCH_COMPONENT: - ItemCount++; - DrawLibItem = (SCH_COMPONENT*) DrawList; - DrawLibItem->m_Parent = sheet->LastScreen(); - if( List ) - { - List->m_Comp = DrawLibItem; - List->m_SheetList = *sheet; - strncpy( List->m_Ref, - CONV_TO_UTF8( DrawLibItem->GetRef( sheet ) ), - sizeof( List->m_Ref ) ); - List++; - } - break; - - default: - break; + List->m_Comp = DrawLibItem; + List->m_SheetList = *sheet; + strncpy( List->m_Ref, + CONV_TO_UTF8( DrawLibItem->GetRef( sheet ) ), + sizeof( List->m_Ref ) ); + List++; } - - DrawList = DrawList->Pnext; } } diff --git a/eeschema/netform.cpp b/eeschema/netform.cpp index 1efbf5b894..27038ad984 100644 --- a/eeschema/netform.cpp +++ b/eeschema/netform.cpp @@ -23,19 +23,24 @@ static void WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f, bool use_ static void WriteGENERICListOfNets( FILE* f, ObjetNetListStruct* ObjNet ); static void AddPinToComponentPinList( SCH_COMPONENT* Component, - DrawSheetPath* sheet, - LibDrawPin* PinEntry ); -static void FindOthersUnits( SCH_COMPONENT* Component, DrawSheetPath* Sheet_in); + DrawSheetPath* sheet, + LibDrawPin* PinEntry ); +static void FindAllsInstancesOfComponent( SCH_COMPONENT* Component, + EDA_LibComponentStruct* Entry, + DrawSheetPath* Sheet_in ); static int SortPinsByNum( ObjetNetListStruct** Pin1, ObjetNetListStruct** Pin2 ); static void EraseDuplicatePins( ObjetNetListStruct** TabPin, int NbrPin ); -static void ClearUsedFlags( WinEDA_SchematicFrame* frame ); +static void ClearUsedFlags( void ); -/* Variable locales */ +/* Local variables */ static int s_SortedPinCount; static ObjetNetListStruct** s_SortedComponentPinList; +// list of references arready found for multi part per packages components +// (used to avoid to used more than one time a component) +static wxArrayString s_ReferencesAlreadyFound; /******************************************************************************/ void WriteNetList( WinEDA_SchematicFrame* frame, const wxString& FileNameNL, @@ -82,16 +87,16 @@ void WriteNetList( WinEDA_SchematicFrame* frame, const wxString& FileNameNL, fclose( f ); break; - default: - Write_GENERIC_NetList( frame, FileNameNL ); + default: + Write_GENERIC_NetList( frame, FileNameNL ); break; - } + } } /****************************************************************************/ static SCH_COMPONENT* FindNextComponentAndCreatPinList( - EDA_BaseStruct* DrawList, DrawSheetPath* sheet) + EDA_BaseStruct* DrawList, DrawSheetPath* sheet ) /****************************************************************************/ /* Find a "suitable" component from the DrawList @@ -103,7 +108,7 @@ static SCH_COMPONENT* FindNextComponentAndCreatPinList( * Must be deallocated by the user */ { - SCH_COMPONENT* Component = NULL; + SCH_COMPONENT* Component = NULL; EDA_LibComponentStruct* Entry; LibEDA_BaseStruct* DEntry; @@ -115,15 +120,12 @@ static SCH_COMPONENT* FindNextComponentAndCreatPinList( continue; Component = (SCH_COMPONENT*) DrawList; - /* already tested ? : */ - bool found = false; - for(unsigned int i =0; im_UsedOnSheets.GetCount(); i++){ - if( Component->m_UsedOnSheets.Item(i) == *sheet ){ - found = true; - break; - } - } - if( found ) continue; + /* Power symbol and other component which have the reference starting by + * "#" are not included in netlist (pseudo or virtual components) */ + wxString str = Component->GetRef( sheet ); + if( str[0] == '#' ) // ignore it + continue; + //if( Component->m_FlagControlMulti == 1 ) // continue; /* yes */ // removed because with multiple instances of one schematic @@ -133,11 +135,26 @@ static SCH_COMPONENT* FindNextComponentAndCreatPinList( if( Entry == NULL ) continue; - /* Power symbol and other component which have the reference starting by - * "#" are not included in netlist (pseudo components) */ - wxString str = Component->GetRef(sheet); - if( str[0] == '#' ) - continue; + if( Entry->m_UnitCount > 1 ) // Multi parts per package: test if already visited: + { + bool found = false; + for( unsigned jj = 0; jj < s_ReferencesAlreadyFound.GetCount(); jj++ ) + { + if( str == s_ReferencesAlreadyFound[jj] ) // Already visited + { + found = true; + break; + } + } + + if( found ) + continue; + else + { + s_ReferencesAlreadyFound.Add( str ); // Mark as visited + } + } + /* Create the pin table for this component */ int ii = sizeof(ObjetNetListStruct*) * MAXPIN; @@ -146,32 +163,31 @@ static SCH_COMPONENT* FindNextComponentAndCreatPinList( memset( s_SortedComponentPinList, 0, ii ); DEntry = Entry->m_Drawings; - for( ; DEntry != NULL; DEntry = DEntry->Next() ) + if( Entry->m_UnitCount <= 1 ) // One part per package { - if( DEntry->Type() != COMPONENT_PIN_DRAW_TYPE ) - continue; - if( DEntry->m_Unit - && (DEntry->m_Unit != Component->m_Multi) ) - continue; - if( DEntry->m_Convert - && (DEntry->m_Convert != Component->m_Convert) ) - continue; + for( ; DEntry != NULL; DEntry = DEntry->Next() ) { - AddPinToComponentPinList( Component, sheet, (LibDrawPin*) DEntry ); + if( DEntry->Type() != COMPONENT_PIN_DRAW_TYPE ) + continue; + if( DEntry->m_Unit + && ( DEntry->m_Unit != Component->GetUnitSelection( sheet ) ) ) + continue; + if( DEntry->m_Convert + && (DEntry->m_Convert != Component->m_Convert) ) + continue; + { + AddPinToComponentPinList( Component, sheet, (LibDrawPin*) DEntry ); + } } } + else // Multiple parts per package: Collect all parts ans pins for this reference + FindAllsInstancesOfComponent( Component, Entry, sheet ); - //Component->m_FlagControlMulti = 1; - Component->m_UsedOnSheets.Add(*sheet); - - if( Entry->m_UnitCount > 1 ) - FindOthersUnits( Component, sheet); - - /* Tri sur le numero de Pin de TabListePin */ + /* Sort Pins in s_SortedComponentPinList by pin number */ qsort( s_SortedComponentPinList, s_SortedPinCount, sizeof(ObjetNetListStruct*), ( int( * ) ( const void*, const void* ) )SortPinsByNum ); - /* Elimination des Pins redondantes du s_SortedComponentPinList */ + /* Remove duplicate Pins in s_SortedComponentPinList */ EraseDuplicatePins( s_SortedComponentPinList, s_SortedPinCount ); return Component; @@ -209,7 +225,7 @@ static wxString ReturnPinNetName( ObjetNetListStruct* Pin, continue; if( ( g_TabObjNet[jj].m_Type != NET_HIERLABEL) && ( g_TabObjNet[jj].m_Type != NET_LABEL) - && ( g_TabObjNet[jj].m_Type != NET_PINLABEL) ) + && ( g_TabObjNet[jj].m_Type != NET_PINLABEL) ) continue; NetName = *g_TabObjNet[jj].m_Label; @@ -218,11 +234,13 @@ static wxString ReturnPinNetName( ObjetNetListStruct* Pin, if( !NetName.IsEmpty() ) { - if( g_TabObjNet[jj].m_Type != NET_PINLABEL ){ + if( g_TabObjNet[jj].m_Type != NET_PINLABEL ) + { NetName = g_TabObjNet[jj].m_SheetList.PathHumanReadable() - + NetName; + + NetName; } - //NetName << wxT("_") << g_TabObjNet[jj].m_SheetList.PathHumanReadable(); + + //NetName << wxT("_") << g_TabObjNet[jj].m_SheetList.PathHumanReadable(); } else { @@ -232,6 +250,7 @@ static wxString ReturnPinNetName( ObjetNetListStruct* Pin, return NetName; } + /***********************************************************************/ void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, const wxString& FullFileName ) @@ -239,17 +258,16 @@ void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, /* Create a generic netlist, and call an external netlister * to change the netlist syntax and create the file - * -- does this still work? */ { - wxString Line, FootprintName; - DrawSheetPath* sheet; - EDA_BaseStruct* DrawList; - SCH_COMPONENT* Component; - wxString netname; - int ii; - FILE* tmpfile; - wxString TmpFullFileName = FullFileName; + wxString Line, FootprintName; + DrawSheetPath* sheet; + EDA_BaseStruct* SchItem; + SCH_COMPONENT* Component; + wxString netname; + int ii; + FILE* tmpfile; + wxString TmpFullFileName = FullFileName; ChangeFileNameExt( TmpFullFileName, wxT( ".tmp" ) ); @@ -260,7 +278,7 @@ void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, return; } - ClearUsedFlags( frame ); /* Reset the flags FlagControlMulti in all schematic files*/ + ClearUsedFlags( ); /* Reset the flags FlagControlMulti in all schematic files*/ fprintf( tmpfile, "$BeginNetlist\n" ); /* Create netlist module section */ @@ -269,12 +287,12 @@ void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, for( sheet = SheetList.GetFirst(); sheet != NULL; sheet = SheetList.GetNext() ) { - for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Pnext ) + for( SchItem = sheet->LastDrawList(); SchItem != NULL; SchItem = SchItem->Pnext ) { - DrawList = Component = FindNextComponentAndCreatPinList( DrawList, sheet ); + SchItem = Component = FindNextComponentAndCreatPinList( SchItem, sheet ); if( Component == NULL ) - break; // No component left + break; // No component left FootprintName.Empty(); if( !Component->m_Field[FOOTPRINT].IsVoid() ) @@ -286,7 +304,7 @@ void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, fprintf( tmpfile, "\n$BeginComponent\n" ); fprintf( tmpfile, "TimeStamp=%8.8lX\n", Component->m_TimeStamp ); fprintf( tmpfile, "Footprint=%s\n", CONV_TO_UTF8( FootprintName ) ); - Line = wxT( "Reference=" ) + Component->GetRef(sheet) + wxT( "\n" ); + Line = wxT( "Reference=" ) + Component->GetRef( sheet ) + wxT( "\n" ); Line.Replace( wxT( " " ), wxT( "_" ) ); fprintf( tmpfile, CONV_TO_UTF8( Line ) ); @@ -346,30 +364,12 @@ void Write_GENERIC_NetList( WinEDA_SchematicFrame* frame, } -/********************************************************/ -static void ClearUsedFlags( WinEDA_SchematicFrame* frame ) -/********************************************************/ +/**********************************/ +static void ClearUsedFlags( void ) +/*********************************/ /* Clear flag list, used in netlist generation */ { - SCH_SCREEN* screen; - EDA_BaseStruct* DrawList; - - EDA_ScreenList ScreenList; - - for( screen = ScreenList.GetFirst(); screen != NULL; screen = ScreenList.GetNext() ) - { - DrawList = screen->EEDrawList; - while( DrawList ) - { - if( DrawList->Type() == TYPE_SCH_COMPONENT ) - { - SCH_COMPONENT* Component = (SCH_COMPONENT*) DrawList; - //Component->m_FlagControlMulti = 0; - Component->m_UsedOnSheets.Clear(); - } - DrawList = DrawList->Pnext; - } - } + s_ReferencesAlreadyFound.Clear(); } @@ -384,22 +384,22 @@ static void WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f, * sinon les nodes sont identifies par le netnumber * * tous les textes graphiques commen�ant par [.-+]pspice ou [.-+]gnucap - * sont consid�r�s comme des commandes a placer dans la netliste + * sont consideres comme des commandes a placer dans la netliste * [.-]pspice ou gnucap sont en debut +pspice et +gnucap sont en fin de netliste */ { - char Line[1024]; - DrawSheetPath* sheet; - EDA_BaseStruct* DrawList; - SCH_COMPONENT* Component; - int ii, nbitems; - wxString text; - wxArrayString SpiceCommandAtBeginFile, SpiceCommandAtEndFile; - wxString msg; + char Line[1024]; + DrawSheetPath* sheet; + EDA_BaseStruct* DrawList; + SCH_COMPONENT* Component; + int ii, nbitems; + wxString text; + wxArrayString SpiceCommandAtBeginFile, SpiceCommandAtEndFile; + wxString msg; #define BUFYPOS_LEN 4 - wxChar bufnum[BUFYPOS_LEN + 1]; + wxChar bufnum[BUFYPOS_LEN + 1]; DateAndTime( Line ); fprintf( f, "* %s (Spice format) creation date: %s\n\n", NETLIST_HEAD_STRING, Line ); @@ -417,7 +417,7 @@ static void WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f, if( DrawList->Type() != TYPE_SCH_TEXT ) continue; #define DRAWTEXT ( (SCH_TEXT*) DrawList ) - text = DRAWTEXT->m_Text; if( text.IsEmpty() ) + text = DRAWTEXT->m_Text; if( text.IsEmpty() ) continue; ident = text.GetChar( 0 ); if( ident != '.' && ident != '-' && ident != '+' ) @@ -461,7 +461,7 @@ static void WriteNetListPspice( WinEDA_SchematicFrame* frame, FILE* f, /* Create component list */ - ClearUsedFlags( frame ); /* Reset the flags FlagControlMulti in all schematic files*/ + ClearUsedFlags(); /* Reset the flags FlagControlMulti in all schematic files*/ for( sheet = SheetList.GetFirst(); sheet != NULL; sheet = SheetList.GetNext() ) { for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Pnext ) @@ -524,7 +524,7 @@ static void WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with /*****************************************************************************************/ /* Routine de generation du fichier netliste ( Format ORCAD PCB 2 ameliore ) - * si with_pcbnew = FALSE + * si with_pcbnew = TRUE * format PCBNEW (OrcadPcb2 + commentaires et liste des nets) * si with_pcbnew = FALSE * Format ORCADPCB2 strict @@ -546,8 +546,8 @@ static void WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with fprintf( f, "( { %s created %s }\n", NETLIST_HEAD_STRING, Buf ); - /* Create netlist module section */ - ClearUsedFlags( frame ); /* Reset the flags FlagControlMulti in all schematic files*/ + /* Create netlist module section */ + ClearUsedFlags( ); /* Reset the flags FlagControlMulti in all schematic files*/ EDA_SheetList SheetList( NULL ); @@ -555,7 +555,7 @@ static void WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with { for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Pnext ) { - DrawList = Component = FindNextComponentAndCreatPinList( DrawList, sheet); + DrawList = Component = FindNextComponentAndCreatPinList( DrawList, sheet ); if( Component == NULL ) break; @@ -566,9 +566,10 @@ static void WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with { if( Entry->m_FootprintList.GetCount() != 0 ) /* Put in list */ { - if( CmpList == NULL ){ + if( CmpList == NULL ) + { CmpList = (ListComponent*) - MyZMalloc( sizeof(ListComponent) * CmpListSize ); + MyZMalloc( sizeof(ListComponent) * CmpListSize ); } if( CmpListCount >= CmpListSize ) { @@ -578,7 +579,7 @@ static void WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with sizeof(ListComponent) * CmpListSize ); } CmpList[CmpListCount].m_Comp = Component; - strcpy(CmpList[CmpListCount].m_Ref, Component->GetRef( sheet ).mb_str()); + strcpy( CmpList[CmpListCount].m_Ref, Component->GetRef( sheet ).mb_str() ); CmpListCount++; } } @@ -591,9 +592,9 @@ static void WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with else FootprintName = wxT( "$noname" ); - Line = Component->GetRef(sheet); + Line = Component->GetRef( sheet ); fprintf( f, " ( %s %s", - CONV_TO_UTF8(Component->GetPath(sheet)), + CONV_TO_UTF8( Component->GetPath( sheet ) ), CONV_TO_UTF8( FootprintName ) ); fprintf( f, " %s", CONV_TO_UTF8( Line ) ); @@ -640,13 +641,16 @@ static void WriteNetListPCBNEW( WinEDA_SchematicFrame* frame, FILE* f, bool with { Component = CmpList[ii].m_Comp; Entry = FindLibPart( Component->m_ChipName.GetData(), wxEmptyString, FIND_ROOT ); + //Line.Printf(_("%s"), CmpList[ii].m_Ref); //Line.Replace( wxT( " " ), wxT( "_" ) ); unsigned int i; - for(i=0; im_FootprintList.GetCount(); jj++ ) @@ -714,10 +718,12 @@ static void EraseDuplicatePins( ObjetNetListStruct** TabPin, int NbrPin ) /**********************************************************************/ /* - * Routine qui elimine les Pins de meme Numero de la liste des objets - * (Liste des Pins d'un boitier) passee en entree - * Ces pins redondantes proviennent des pins (alims... ) communes a plusieurs - * elements d'un boitier a multiple parts. + * Function to remove duplicate Pins in the TabPin pin list + * (This is a list of pins found in the whole schematic, for a given component) + * These duplicate pins were put in list because some pins (powers... ) + * are found more than one time when we have a multiple parts per package component + * for instance, a 74ls00 has 4 parts, and therefor the VCC pin and GND pin apperas 4 times + * in the list. */ { int ii, jj; @@ -725,7 +731,7 @@ static void EraseDuplicatePins( ObjetNetListStruct** TabPin, int NbrPin ) for( ii = 0; ii < NbrPin - 1; ii++ ) { if( TabPin[ii] == NULL ) - continue; /* Deja supprime */ + continue; /* Deja supprime */ if( TabPin[ii]->m_PinNum != TabPin[ii + 1]->m_PinNum ) continue; /* 2 Pins doublees */ @@ -739,82 +745,56 @@ static void EraseDuplicatePins( ObjetNetListStruct** TabPin, int NbrPin ) } -/**********************************************************************/ -static void FindOthersUnits( SCH_COMPONENT* Component_in, DrawSheetPath* Sheet_in) -/**********************************************************************/ +/**********************************************************************************/ +static void FindAllsInstancesOfComponent( SCH_COMPONENT* Component_in, + EDA_LibComponentStruct* Entry, + DrawSheetPath* Sheet_in ) +/**********************************************************************************/ -/* Recherche les autres parts du boitier auquel appartient la part Component, - * pour les boitiers a parts multiples. - * Appelle AddPinToComponentPinList() pour classer les pins des parts trouvees +/** + * Used for multiple parts per package components + * Search all instances of Component_in, + * Calls AddPinToComponentPinList() to and pins founds to the current component pin list */ { - EDA_BaseStruct* DrawList; + EDA_BaseStruct* SchItem; SCH_COMPONENT* Component2; - EDA_LibComponentStruct* Entry; LibEDA_BaseStruct* DEntry; DrawSheetPath* sheet; - wxString str; + wxString str, Reference = Component_in->GetRef( Sheet_in ); EDA_SheetList SheetList( NULL ); - unsigned int i; - bool found; + for( sheet = SheetList.GetFirst(); sheet != NULL; sheet = SheetList.GetNext() ) { - DrawList = sheet->LastDrawList(); - while( DrawList ) + for( SchItem = sheet->LastDrawList(); SchItem; SchItem = SchItem->Pnext ) { - switch( DrawList->Type() ) + if( SchItem->Type() != TYPE_SCH_COMPONENT ) + continue; + + Component2 = (SCH_COMPONENT*) SchItem; + + str = Component2->GetRef( sheet ); + if( str.CmpNoCase( Reference ) != 0 ) + continue; + + if( Entry && Entry->m_Drawings != NULL ) { - case TYPE_SCH_COMPONENT: - Component2 = (SCH_COMPONENT*) DrawList; - - found = false; - for( i =0; im_UsedOnSheets.GetCount(); i++){ - if( Component2->m_UsedOnSheets.Item(i) == *Sheet_in ){ - found = true; - } - } - if( found ) break; - //if( Component2->m_FlagControlMulti == 1 ) //has it been used? (on this sheet?) - // break; - str = Component2->GetRef(sheet); - if( str.CmpNoCase( Component_in->GetRef(Sheet_in) ) != 0 ) - break; - - Entry = FindLibPart( Component2->m_ChipName.GetData(), wxEmptyString, FIND_ROOT ); - if( Entry == NULL ) - break; - - if( str[0] == '#' ) - break; - - if( Entry->m_Drawings != NULL ) + DEntry = Entry->m_Drawings; + for( ; DEntry != NULL; DEntry = DEntry->Next() ) { - DEntry = Entry->m_Drawings; - for( ; DEntry != NULL; DEntry = DEntry->Next() ) - { - if( DEntry->Type() != COMPONENT_PIN_DRAW_TYPE ) - continue; - if( DEntry->m_Unit - && (DEntry->m_Unit != Component2->m_Multi) ) - continue; - if( DEntry->m_Convert - && (DEntry->m_Convert != Component2->m_Convert) ) - continue; - { - AddPinToComponentPinList( Component2, sheet, (LibDrawPin*) DEntry ); - } - } + if( DEntry->Type() != COMPONENT_PIN_DRAW_TYPE ) + continue; + if( DEntry->m_Unit + && ( DEntry->m_Unit != Component2->GetUnitSelection( sheet ) ) ) + continue; + if( DEntry->m_Convert + && (DEntry->m_Convert != Component2->m_Convert) ) + continue; + // A suitable pin in found: add it to the current list + AddPinToComponentPinList( Component2, sheet, (LibDrawPin*) DEntry ); } - Component2->m_UsedOnSheets.Add(*Sheet_in); - //Component2->m_FlagControlMulti = 1; //mark it as used.. - break; - - default: - break; } - - DrawList = DrawList->Pnext; } } } @@ -858,11 +838,11 @@ static void WriteGENERICListOfNets( FILE* f, ObjetNetListStruct* ObjNet ) for( ii = 0; ii < g_NbrObjNet; ii++ ) { - if( (NetCode = ObjNet[ii].GetNet()) != LastNetCode ) // New net found, write net id; + if( ( NetCode = ObjNet[ii].GetNet() ) != LastNetCode ) // New net found, write net id; { - SameNetcodeCount = 0; // Items count for this net + SameNetcodeCount = 0; // Items count for this net NetName.Empty(); - for( jj = 0; jj < g_NbrObjNet; jj++ ) // Find a label (if exists) for this net + for( jj = 0; jj < g_NbrObjNet; jj++ ) // Find a label (if exists) for this net { if( ObjNet[jj].GetNet() != NetCode ) continue; @@ -879,10 +859,12 @@ static void WriteGENERICListOfNets( FILE* f, ObjetNetListStruct* ObjNet ) if( !NetName.IsEmpty() ) { NetcodeName += NetName; - if( g_TabObjNet[jj].m_Type != NET_PINLABEL ){ + if( g_TabObjNet[jj].m_Type != NET_PINLABEL ) + { // usual net name, add in the sheet path NetcodeName = g_TabObjNet[jj].m_SheetList.PathHumanReadable() - + NetcodeName; + + NetcodeName; + //NetcodeName << wxT("_") << // g_TabObjNet[jj].m_SheetList.PathHumanReadable(); } @@ -895,9 +877,9 @@ static void WriteGENERICListOfNets( FILE* f, ObjetNetListStruct* ObjNet ) continue; Cmp = (SCH_COMPONENT*) ObjNet[ii].m_Link; - CmpRef = Cmp->GetRef(&ObjNet[ii].m_SheetList); //is this correct? + CmpRef = Cmp->GetRef( &ObjNet[ii].m_SheetList ); //is this correct? if( CmpRef.StartsWith( wxT( "#" ) ) ) - continue; // Pseudo component (Like Power symbol) + continue; // Pseudo component (Like Power symbol) // Print the pin list for this net, if 2 or more items are connected: SameNetcodeCount++; @@ -971,14 +953,14 @@ static void WriteNetListCADSTAR( WinEDA_SchematicFrame* frame, FILE* f ) fprintf( f, "\n" ); /* Create netlist module section */ - ClearUsedFlags( frame ); /* Reset the flags FlagControlMulti in all schematic files*/ + ClearUsedFlags( ); /* Reset the flags FlagControlMulti in all schematic files*/ EDA_SheetList SheetList( NULL ); for( sheet = SheetList.GetFirst(); sheet != NULL; sheet = SheetList.GetNext() ) { for( DrawList = sheet->LastDrawList(); DrawList != NULL; DrawList = DrawList->Pnext ) { - DrawList = Component = FindNextComponentAndCreatPinList( DrawList, sheet); + DrawList = Component = FindNextComponentAndCreatPinList( DrawList, sheet ); if( Component == NULL ) break; @@ -990,7 +972,7 @@ static void WriteNetListCADSTAR( WinEDA_SchematicFrame* frame, FILE* f ) else FootprintName = wxT( "$noname" ); - msg = Component->GetRef(sheet); + msg = Component->GetRef( sheet ); fprintf( f, "%s ", CONV_TO_UTF8( StartCmpDesc ) ); fprintf( f, "%s", CONV_TO_UTF8( msg ) ); @@ -1038,7 +1020,7 @@ static void WriteListOfNetsCADSTAR( FILE* f, ObjetNetListStruct* ObjNet ) for( ii = 0; ii < g_NbrObjNet; ii++ ) { // Get the NetName of the current net : - if( (NetCode = ObjNet[ii].GetNet()) != LastNetCode ) + if( ( NetCode = ObjNet[ii].GetNet() ) != LastNetCode ) { NetName.Empty(); for( jj = 0; jj < g_NbrObjNet; jj++ ) @@ -1057,9 +1039,11 @@ static void WriteListOfNetsCADSTAR( FILE* f, ObjetNetListStruct* ObjNet ) if( !NetName.IsEmpty() ) { NetcodeName += NetName; - if( g_TabObjNet[jj].m_Type != NET_PINLABEL ){ + if( g_TabObjNet[jj].m_Type != NET_PINLABEL ) + { NetcodeName = g_TabObjNet[jj].m_SheetList.PathHumanReadable() - + NetcodeName; + + NetcodeName; + //NetcodeName << wxT("_") << // g_TabObjNet[jj].m_SheetList.PathHumanReadable(); } @@ -1079,7 +1063,7 @@ static void WriteListOfNetsCADSTAR( FILE* f, ObjetNetListStruct* ObjNet ) continue; Cmp = (SCH_COMPONENT*) ObjNet[ii].m_Link; - wxString refstr = Cmp->GetRef(&(ObjNet[ii].m_SheetList)); + wxString refstr = Cmp->GetRef( &(ObjNet[ii].m_SheetList) ); if( refstr[0] == '#' ) continue; // Pseudo composant (symboles d'alims) @@ -1127,9 +1111,9 @@ static void WriteListOfNetsCADSTAR( FILE* f, ObjetNetListStruct* ObjNet ) continue; SCH_COMPONENT* tstcmp = (SCH_COMPONENT*) ObjNet[jj].m_Link; - wxString p = Cmp->GetPath( &( ObjNet[ii].m_SheetList ) ); + wxString p = Cmp->GetPath( &( ObjNet[ii].m_SheetList ) ); wxString tstp = tstcmp->GetPath( &( ObjNet[jj].m_SheetList ) ); - if( p.Cmp(tstp) != 0 ) + if( p.Cmp( tstp ) != 0 ) continue; if( ObjNet[jj].m_PinNum == ObjNet[ii].m_PinNum ) diff --git a/eeschema/netlist.cpp b/eeschema/netlist.cpp index 272cba357a..547091b5ce 100644 --- a/eeschema/netlist.cpp +++ b/eeschema/netlist.cpp @@ -17,8 +17,7 @@ /* Routines locales */ static void PropageNetCode( int OldNetCode, int NewNetCode, int IsBus ); static void SheetLabelConnect( ObjetNetListStruct* SheetLabel ); -static int ListeObjetConnection( WinEDA_SchematicFrame* frame, - DrawSheetPath* sheetlist, +static int ListeObjetConnection( DrawSheetPath* sheetlist, ObjetNetListStruct* ObjNet ); static int ConvertBusToMembers( ObjetNetListStruct* ObjNet ); static void PointToPointConnect( ObjetNetListStruct* Ref, int IsBus, @@ -165,7 +164,7 @@ void* WinEDA_SchematicFrame::BuildNetListBase() /* Build the sheet (not screen) list (flattened)*/ EDA_SheetList SheetListList( NULL ); i=0; - /* 1ere passe : Comptage du nombre d'objet de Net */ + /* first pass : count objects used in connectivty calculation */ g_NbrObjNet = 0; g_TabObjNet = NULL; /* Init pour le 1er passage dans ListeObjetConnection */ @@ -174,7 +173,7 @@ void* WinEDA_SchematicFrame::BuildNetListBase() for( sheet = SheetListList.GetFirst(); sheet != NULL; sheet = SheetListList.GetNext() ) { - g_NbrObjNet += ListeObjetConnection( this, sheet, NULL ); + g_NbrObjNet += ListeObjetConnection( sheet, NULL ); } if( g_NbrObjNet == 0 ) @@ -188,8 +187,7 @@ void* WinEDA_SchematicFrame::BuildNetListBase() if( g_TabObjNet == NULL ) return NULL; - /* 2eme passe : Remplissage des champs des structures des objets de Net */ - /* second pass: fill the fields of the structures in the Net */ + /* second pass: fill the fields of the structures used in connectivty calculation */ s_PassNumber++; @@ -197,15 +195,14 @@ void* WinEDA_SchematicFrame::BuildNetListBase() for( ObjetNetListStruct* tabObjNet = g_TabObjNet; sheet != NULL; sheet = SheetListList.GetNext() ) { - tabObjNet += ListeObjetConnection( this, sheet, tabObjNet ); + tabObjNet += ListeObjetConnection( sheet, tabObjNet ); } activity.Empty(); activity << wxT(" ") << _( "NbItems" ) << wxT(" ") << g_NbrObjNet; SetStatusText( activity ); - /* Recherche des connections pour les Segments et les Pins */ - /* Tri du Tableau des objets de Net par Sheet */ + /* Sort objects by Sheet */ qsort( g_TabObjNet, g_NbrObjNet, sizeof(ObjetNetListStruct), TriBySheet ); @@ -363,7 +360,7 @@ void* WinEDA_SchematicFrame::BuildNetListBase() SheetLabelConnect( g_TabObjNet + i ); } - /* Tri du Tableau des objets de Net par NetCode */ + /* Sort objects by NetCode */ qsort( g_TabObjNet, g_NbrObjNet, sizeof(ObjetNetListStruct), TriNetCode ); @@ -438,17 +435,14 @@ static void SheetLabelConnect( ObjetNetListStruct* SheetLabel ) } -/*****************************************************************************/ -static int ListeObjetConnection( WinEDA_SchematicFrame* frame, DrawSheetPath* sheetlist, - ObjetNetListStruct* ObjNet ) -/*****************************************************************************/ +/**************************************************************************************/ +static int ListeObjetConnection( DrawSheetPath* sheetlist, ObjetNetListStruct* ObjNet ) +/**************************************************************************************/ -/* Routine generant la liste des objets relatifs aux connection - * entree: - * sheetlist: pointer to a sheetlist. - * ObjNet: - * si NULL: la routine compte seulement le nombre des objets - * sinon: pointe le tableau a remplir +/** Function ListeObjetConnection + * Creates the list of objects related to connections (pins of components, wires, labels, junctions ...) + * @param sheetlist: pointer to a sheetlist. + * @param ObjNet: if NULL, objects count else list to fill */ { int ii, NbrItem = 0; @@ -596,8 +590,7 @@ static int ListeObjetConnection( WinEDA_SchematicFrame* frame, DrawSheetPath* sh if( DEntry->Type() != COMPONENT_PIN_DRAW_TYPE ) continue; - if( DEntry->m_Unit - && (DEntry->m_Unit != DrawLibItem->m_Multi) ) + if( DEntry->m_Unit && (DEntry->m_Unit != DrawLibItem->GetUnitSelection( sheetlist ) ) ) continue; if( DEntry->m_Convert @@ -684,7 +677,7 @@ static int ListeObjetConnection( WinEDA_SchematicFrame* frame, DrawSheetPath* sh break; case DRAW_HIERARCHICAL_PIN_SHEET_STRUCT_TYPE: - DisplayError( frame, wxT( "Netlist: Type DRAW_SHEETLABEL inattendu" ) ); + DisplayError( NULL, wxT( "Netlist: Type DRAW_SHEETLABEL inattendu" ) ); break; default: @@ -692,7 +685,7 @@ static int ListeObjetConnection( WinEDA_SchematicFrame* frame, DrawSheetPath* sh wxString msg; msg.Printf( wxT( "Netlist: unexpected type struct %d" ), DrawList->Type() ); - DisplayError( frame, msg ); + DisplayError( NULL, msg ); break; } } diff --git a/eeschema/netlist.h b/eeschema/netlist.h index 0a2eb2dc17..c4587763a8 100644 --- a/eeschema/netlist.h +++ b/eeschema/netlist.h @@ -26,7 +26,7 @@ typedef enum { * NET_TYPE_CUSTOM1+CUSTOMPANEL_COUNTMAX-1 * is the last id for user netlist format */ - NET_TYPE_CUSTOM_MAX = NET_TYPE_CUSTOM1+CUSTOMPANEL_COUNTMAX-1 + NET_TYPE_CUSTOM_MAX = NET_TYPE_CUSTOM1 + CUSTOMPANEL_COUNTMAX - 1 } TypeNetForm; @@ -39,7 +39,7 @@ enum NetObjetType { /* Type des objets de Net */ NET_JONCTION, NET_LABEL, NET_GLOBLABEL, - NET_HIERLABEL, //on a screen to indicate connection to a higher-level sheet + NET_HIERLABEL, //on a screen to indicate connection to a higher-level sheet NET_SHEETLABEL, //on a drawscreen element to indicate connection to a lower-level sheet. NET_BUSLABELMEMBER, NET_GLOBBUSLABELMEMBER, @@ -51,9 +51,9 @@ enum NetObjetType { /* Type des objets de Net */ }; -enum IsConnectType { /* Valeur du Flag de connection */ - UNCONNECT, /* Pin ou Label non connecte */ - NOCONNECT, /* Pin volontairement non connectee (Symb. NoConnect utilise) */ +enum IsConnectType { /* Valeur du Flag de connection */ + UNCONNECT, /* Pin ou Label non connecte */ + NOCONNECT, /* Pin volontairement non connectee (Symb. NoConnect utilise) */ PAD_CONNECT /* connexion normale */ }; @@ -62,23 +62,23 @@ enum IsConnectType { /* Valeur du Flag de connection */ class ObjetNetListStruct { public: - EDA_BaseStruct* m_Comp; /* Pointeur sur la definition de l'objet */ + EDA_BaseStruct* m_Comp; /* Pointeur sur la definition de l'objet */ void* m_Link; /* Pour SheetLabelStruct: Pointeur sur la feuille de hierarchie * Pour les Pins: pointeur sur le composant */ - int m_Flag; /* flag pour calculs internes */ + int m_Flag; /* flag pour calculs internes */ DrawSheetPath m_SheetList; NetObjetType m_Type; - int m_ElectricalType;/* Pour Pins et sheet labels: type electrique */ + int m_ElectricalType; /* Pour Pins et sheet labels: type electrique */ private: - int m_NetCode; /* pour elements simples */ + int m_NetCode; /* pour elements simples */ public: - int m_BusNetCode; /* pour connexions type bus */ + int m_BusNetCode; /* pour connexions type bus */ int m_Member; /* pour les labels type BUSWIRE ( labels de bus eclate ) - * numero de membre */ + * numero de membre */ IsConnectType m_FlagOfConnection; - DrawSheetPath m_SheetListInclude; /* sheet that the hierarchal label connects to.*/ - long m_PinNum; /* numero de pin( 4 octets -> 4 codes ascii) */ - const wxString* m_Label; /* Tous types Labels:pointeur sur la wxString definissant le label */ + DrawSheetPath m_SheetListInclude; /* sheet that the hierarchal label connects to.*/ + long m_PinNum; /* numero de pin( 4 octets -> 4 codes ascii) */ + const wxString* m_Label; /* Tous types Labels:pointeur sur la wxString definissant le label */ wxPoint m_Start, m_End; #if defined (DEBUG) @@ -93,15 +93,17 @@ public: /* Structures pour memo et liste des elements */ typedef struct ListLabel { - int m_LabelType; - void * m_Label; - char m_SheetPath[64]; + int m_LabelType; + void* m_Label; + char m_SheetPath[64]; } ListLabel; + typedef struct ListComponent { - SCH_COMPONENT * m_Comp; - char m_Ref[32]; - //have to store it here since the object refrerences will be duplicated. + SCH_COMPONENT* m_Comp; + char m_Ref[32]; + + //have to store it here since the object references will be duplicated. DrawSheetPath m_SheetList; //composed of UIDs } ListComponent; @@ -109,19 +111,19 @@ typedef struct ListComponent struct CmpListStruct { public: - SCH_COMPONENT* m_Cmp; /* Pointeur sur le composant */ - int m_NbParts; /* Nombre de parts par boitier */ - bool m_PartsLocked; // For multi part components: True if the part cannot be changed - int m_Unit; /* Numero de part */ - DrawSheetPath m_SheetList; - unsigned long m_TimeStamp; /* unique identification number */ - int m_IsNew; /* != 0 pour composants non annotes */ - char m_TextValue[32]; /* Valeur */ - char m_TextRef[32]; /* Reference ( hors numero ) */ - int m_NumRef; /* Numero de reference */ - int m_Flag; /* flag pour calculs internes */ - wxPoint m_Pos; /* position components */ - char m_Path[128]; // the 'path' of the object in the sheet hierarchy. + SCH_COMPONENT* m_Cmp; /* Pointeur sur le composant */ + int m_NbParts; /* Nombre de parts par boitier */ + bool m_PartsLocked; // For multi part components: True if the part cannot be changed + int m_Unit; /* Numero de part */ + DrawSheetPath m_SheetList; + unsigned long m_TimeStamp; /* unique identification number */ + int m_IsNew; /* != 0 pour composants non annotes */ + char m_TextValue[32]; /* Valeur */ + char m_TextRef[32]; /* Reference ( hors numero ) */ + int m_NumRef; /* Numero de reference */ + int m_Flag; /* flag pour calculs internes */ + wxPoint m_Pos; /* position components */ + char m_Path[128]; // the 'path' of the object in the sheet hierarchy. }; diff --git a/eeschema/schedit.cpp b/eeschema/schedit.cpp index ec722c521b..a2ebf17029 100644 --- a/eeschema/schedit.cpp +++ b/eeschema/schedit.cpp @@ -610,7 +610,7 @@ void WinEDA_SchematicFrame::Process_Special_Functions( wxCommandEvent& event ) { EDA_LibComponentStruct* LibEntry; LibEntry = FindLibPart( - ( (SCH_COMPONENT*) screen->GetCurItem() )->m_ChipName, + ( (SCH_COMPONENT*) screen->GetCurItem() )->m_ChipName.GetData(), wxEmptyString, FIND_ALIAS ); if( LibEntry && LibEntry->m_DocFile != wxEmptyString ) diff --git a/eeschema/viewlibs.cpp b/eeschema/viewlibs.cpp index 8833cd39fe..2a85465d4e 100644 --- a/eeschema/viewlibs.cpp +++ b/eeschema/viewlibs.cpp @@ -253,7 +253,7 @@ wxClientDC dc(DrawPanel); g_ViewUnit = 1; g_ViewConvert = 1; - LibEntry = FindLibPart(CmpName,Lib->m_Name, FIND_ALIAS); + LibEntry = FindLibPart(CmpName.GetData(),Lib->m_Name, FIND_ALIAS); g_CurrentViewComponentName = CmpName; DisplayLibInfos(); Zoom_Automatique(FALSE); diff --git a/include/bitmaps.h b/include/bitmaps.h index e72b0e953d..4f2887c79d 100644 --- a/include/bitmaps.h +++ b/include/bitmaps.h @@ -208,24 +208,24 @@ #include "../bitmaps/Auto_track_width.xpm" #include "../bitmaps/fill_zone.xpm" #include "../bitmaps/Width_Segment.xpm" - + #include "../bitmaps/annotate_right_down.xpm" #include "../bitmaps/annotate_down_right.xpm" - #include "../bitmaps/new_sch.xpm" +// #include "../bitmaps/new_sch.xpm" #include "../bitmaps/Open_Project.xpm" - #include "../bitmaps/new_cvpcb.xpm" +// #include "../bitmaps/new_cvpcb.xpm" #include "../bitmaps/zip.xpm" #include "../bitmaps/icon_gerbview_small.xpm" - #include "../bitmaps/zip_tool.xpm" +// #include "../bitmaps/zip_tool.xpm" #include "../bitmaps/unzip.xpm" #include "../bitmaps/Browse_Files.xpm" #include "../bitmaps/New_Project.xpm" - #include "../bitmaps/new_gerb.xpm" - #include "../bitmaps/new_python.xpm" +// #include "../bitmaps/new_gerb.xpm" +// #include "../bitmaps/new_python.xpm" #include "../bitmaps/icon_cvpcb_small.xpm" #include "../bitmaps/unknown.xpm" - #include "../bitmaps/new_pcb.xpm" +// #include "../bitmaps/new_pcb.xpm" #include "../bitmaps/reload.xpm" diff --git a/pcbnew/zones_test_and_combine_areas.cpp b/pcbnew/zones_test_and_combine_areas.cpp index 7fec10bfc8..805634ed51 100644 --- a/pcbnew/zones_test_and_combine_areas.cpp +++ b/pcbnew/zones_test_and_combine_areas.cpp @@ -280,7 +280,7 @@ int BOARD::ClipAreaPolygon( ZONE_CONTAINER* CurrArea, { std::vector * pa = new std::vector; curr_polygon->Undraw(); - int n_poly = CurrArea->m_Poly->NormalizeWithGpc( pa, bRetainArcs ); + int n_poly = CurrArea->m_Poly->NormalizeAreaOutlines( pa, bRetainArcs ); if( n_poly > 1 ) // i.e if clipping has created some polygons, we must add these new copper areas { ZONE_CONTAINER* NewArea; @@ -310,7 +310,8 @@ int BOARD::ClipAreaPolygon( ZONE_CONTAINER* CurrArea, * itself and the polygons for any other areas on the same net. * This may change the number and order of copper areas in the net. * @param modified_area = area to test - * @param bMessageBox : if true, shows message boxes when clipping occurs. + * @param bMessageBoxInt == true, shows message when clipping occurs. + * @param bMessageBoxArc == true, shows message when clipping can't be done due to arcs. * @return : * -1 if arcs intersect other sides, so polygon can't be clipped * 0 if no intersecting sides @@ -697,8 +698,11 @@ int BOARD::CombineAreas( ZONE_CONTAINER* area_ref, ZONE_CONTAINER* area_to_combi CPolyLine* poly2 = area_to_combine->m_Poly; std::vector arc_array1; std::vector arc_array2; - poly1->MakeGpcPoly( -1, &arc_array1 ); - poly2->MakeGpcPoly( -1, &arc_array2 ); + + poly1->MakePolygonFromAreaOutlines( -1, &arc_array1 ); + poly2->MakePolygonFromAreaOutlines( -1, &arc_array2 ); + +#ifdef USE_GPC_POLY_LIB int n_ext_cont1 = 0; for( int ic = 0; icGetGpcPoly()->num_contours; ic++ ) if( !( (poly1->GetGpcPoly()->hole)[ic] ) ) @@ -776,7 +780,97 @@ int BOARD::CombineAreas( ZONE_CONTAINER* area_ref, ZONE_CONTAINER* area_to_combi area_ref->m_Poly->Draw(); gpc_free_polygon( union_gpc ); delete union_gpc; + return 1; +#endif + +#ifdef USE_GPL_POLY_LIB +//@todo +#warning work in progress +wxString msg; + int n_ext_cont1 = poly1->GetPhpPoly()->m_cnt; + + int n_ext_cont2 = poly2->GetPhpPoly()->m_cnt; + + polygon* union_polygon = NULL; + union_polygon = poly1->GetPhpPoly()->boolean( poly2->GetPhpPoly(), A_OR_B ); + + if ( union_polygon == NULL ) + return 0; + // get number of outside contours + int n_union_ext_cont = union_polygon->m_cnt; +msg.Printf(wxT("cnt res = %d, pts1,2 = %d,%d"), n_union_ext_cont , n_ext_cont1, n_ext_cont2); +wxMessageBox(msg); + + // if no intersection, free new gpc and return +#if 0 + if( n_union_ext_cont == n_ext_cont1 + n_ext_cont2 ) + { +wxMessageBox(wxT("no change polys")); + delete union_polygon; + return 0; + } +#endif + +wxMessageBox(wxT("merge areas")); + // intersection, replace area_ref->m_Poly with combined areas and remove area_to_combine + RemoveArea( area_to_combine ); + area_ref->m_Poly->RemoveAllContours(); + // create area with external contour +// for( int ic = 0; ic < union_polygon->m_cnt; ic++ ) + { + // if( !(union_gpc->hole)[ic] ) // Recreate only area edges, NOT holes (todo..) + { + // external contour, replace this poly + vertex * curr_vertex = union_polygon->getFirst(); + for( int ii = 0; ii < union_polygon->m_cnt; ii++ ) + { + int x = (int) curr_vertex->X(); + int y = (int) curr_vertex->Y(); +msg.Printf(wxT("ii = %d, pts = %d,%d, wid = %d"), ii , x, y, curr_vertex->id()); +wxMessageBox(msg); + + if( ii==0 ) + { + area_ref->m_Poly->Start( area_ref->GetLayer( + ), x, y, area_ref->m_Poly->GetHatchStyle() ); + } + else + area_ref->m_Poly->AppendCorner( x, y ); + + curr_vertex = curr_vertex->Next(); +// if( curr_vertex->id() == union_polygon->getFirst()->id() ) break; + } + area_ref->m_Poly->Close(); + } + } + + // add holes +#if 0 + for( int ic = 0; icnum_contours; ic++ ) + { + if( (union_gpc->hole)[ic] ) + { + // hole + for( int i = 0; icontour[ic].num_vertices; i++ ) + { + int x = (int) ( (union_gpc->contour)[ic].vertex )[i].x; + int y = (int) ( (union_gpc->contour)[ic].vertex )[i].y; + area_ref->m_Poly->AppendCorner( x, y ); + } + + area_ref->m_Poly->Close(); + } + } +#endif + area_ref->utility = 1; + area_ref->m_Poly->RestoreArcs( &arc_array1 ); + area_ref->m_Poly->RestoreArcs( &arc_array2 ); + area_ref->m_Poly->Draw(); + delete union_polygon; + return 0; +#endif + } @@ -785,7 +879,7 @@ int BOARD::CombineAreas( ZONE_CONTAINER* area_ref, ZONE_CONTAINER* area_to_combi /** * Function Is_Area_Inside_Area * Test a given area to see if it is inside an other area, or an other area is inside the given area - * an area is inside an other are if ALL its corners are inside + * an area is inside an other are if ALL its edges are inside the other area * @param Area_Ref: the given area to compare with other areas * used to remove redundant areas */ @@ -1007,7 +1101,7 @@ int BOARD::Test_Drc_Areas_Outlines_To_Areas_Outlines( ZONE_CONTAINER* aArea_To_E bool DRC::doEdgeZoneDrc( ZONE_CONTAINER * aArea, int aCornerIndex ) { wxString str; - + wxPoint start = aArea->GetCornerPosition(aCornerIndex); wxPoint end; // Search the end point of the edge starting at aCornerIndex diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp index 0656cef9c4..e878cb0671 100644 --- a/polygon/PolyLine.cpp +++ b/polygon/PolyLine.cpp @@ -1,4 +1,5 @@ // PolyLine.cpp ... implementation of CPolyLine class from FreePCB. + // // implementation for kicad // @@ -12,33 +13,47 @@ using namespace std; #include "PolyLine.h" -#define to_int(x) (int)round((x)) +#define to_int( x ) (int) round( (x) ) -#define pi 3.14159265359 -#define DENOM 10 // to use mils for php clipping -//#define DENOM 1 // to use internal units for php clipping +#define pi 3.14159265359 CPolyLine::CPolyLine() -{ - m_HatchStyle = 0; - m_Width = 0; - utility = 0; - m_gpc_poly = new gpc_polygon; - m_gpc_poly->num_contours = 0; - m_php_poly = new polygon; +{ + m_HatchStyle = 0; + m_Width = 0; + utility = 0; +#ifdef USE_GPC_POLY_LIB + m_gpc_poly = new gpc_polygon; + m_gpc_poly->num_contours = 0; +#else + m_gpc_poly = NULL; +#endif +#ifdef USE_GPL_POLY_LIB + m_php_poly = new polygon; +#else + m_php_poly = NULL; +#endif } + // destructor, removes display elements // CPolyLine::~CPolyLine() { - Undraw(); - FreeGpcPoly(); - delete m_gpc_poly; - delete m_php_poly; + Undraw(); +#ifdef USE_GPC_POLY_LIB + FreeGpcPoly(); + delete m_gpc_poly; +#endif +#ifdef USE_GPL_POLY_LIB + delete m_php_poly; +#endif } + +#ifdef USE_GPC_POLY_LIB + // Use the General Polygon Clipping Library to clip contours // If this results in new polygons, return them as std::vector p // If bRetainArcs == TRUE, try to retain arcs in polys @@ -46,546 +61,613 @@ CPolyLine::~CPolyLine() // int CPolyLine::NormalizeWithGpc( std::vector * pa, bool bRetainArcs ) { - std::vector arc_array; + std::vector arc_array; - if( bRetainArcs ) - MakeGpcPoly( -1, &arc_array ); - else - MakeGpcPoly( -1, NULL ); + if( bRetainArcs ) + MakeGpcPoly( -1, &arc_array ); + else + MakeGpcPoly( -1, NULL ); - Undraw(); + Undraw(); - // now, recreate poly - // first, find outside contours and create new CPolyLines if necessary - int n_ext_cont = 0; - for( int ic=0; icnum_contours; ic++ ) - { - if( !(m_gpc_poly->hole)[ic] ) - { - if( n_ext_cont == 0 ) - { - // first external contour, replace this poly - corner.clear(); - side_style.clear(); - for( int i=0; icontour[ic].num_vertices; i++ ) - { - int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x); - int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y); - if( i==0 ) - Start( m_layer, x, y, m_HatchStyle ); - else - AppendCorner( x, y, STRAIGHT, FALSE ); - } - Close(); - n_ext_cont++; - } - else if( pa ) - { - // next external contour, create new poly - CPolyLine * poly = new CPolyLine; - pa->push_back(poly); // put in array - for( int i=0; icontour[ic].num_vertices; i++ ) - { - int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x); - int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y); - if( i==0 ) - poly->Start( m_layer, x, y, m_HatchStyle ); - else - poly->AppendCorner( x, y, STRAIGHT, FALSE ); - } - poly->Close( STRAIGHT, FALSE ); - n_ext_cont++; - } - } - } + // now, recreate poly + // first, find outside contours and create new CPolyLines if necessary + int n_ext_cont = 0; + for( int ic = 0; icnum_contours; ic++ ) + { + if( !(m_gpc_poly->hole)[ic] ) + { + if( n_ext_cont == 0 ) + { + // first external contour, replace this poly + corner.clear(); + side_style.clear(); + for( int i = 0; icontour[ic].num_vertices; i++ ) + { + int x = to_int( ( (m_gpc_poly->contour)[ic].vertex )[i].x ); + int y = to_int( ( (m_gpc_poly->contour)[ic].vertex )[i].y ); + if( i==0 ) + Start( m_layer, x, y, m_HatchStyle ); + else + AppendCorner( x, y, STRAIGHT, FALSE ); + } + Close(); + n_ext_cont++; + } + else if( pa ) + { + // next external contour, create new poly + CPolyLine* poly = new CPolyLine; + pa->push_back( poly ); // put in array + for( int i = 0; icontour[ic].num_vertices; i++ ) + { + int x = to_int( ( (m_gpc_poly->contour)[ic].vertex )[i].x ); + int y = to_int( ( (m_gpc_poly->contour)[ic].vertex )[i].y ); + if( i==0 ) + poly->Start( m_layer, x, y, m_HatchStyle ); + else + poly->AppendCorner( x, y, STRAIGHT, FALSE ); + } - // now add cutouts to the CPolyLine(s) - for( int ic=0; icnum_contours; ic++ ) - { - if( (m_gpc_poly->hole)[ic] ) - { - CPolyLine * ext_poly = NULL; - if( n_ext_cont == 1 ) - { - ext_poly = this; - } - else - { - // find the polygon that contains this hole - for( int i=0; icontour[ic].num_vertices; i++ ) - { - int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x); - int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y); - if( TestPointInside( x, y ) ) - ext_poly = this; - else - { - for( int ext_ic=0; ext_icTestPointInside( x, y ) ) - { - ext_poly = (*pa)[ext_ic]; - break; - } - } - } - if( ext_poly ) - break; - } - } - if( !ext_poly ) - wxASSERT(0); - for( int i=0; icontour[ic].num_vertices; i++ ) - { - int x = to_int(((m_gpc_poly->contour)[ic].vertex)[i].x); - int y = to_int(((m_gpc_poly->contour)[ic].vertex)[i].y); - ext_poly->AppendCorner( x, y, STRAIGHT, FALSE ); - } - ext_poly->Close( STRAIGHT, FALSE ); - } - } - if( bRetainArcs ) - RestoreArcs( &arc_array, pa ); - FreeGpcPoly(); + poly->Close( STRAIGHT, FALSE ); + n_ext_cont++; + } + } + } - return n_ext_cont; + // now add cutouts to the CPolyLine(s) + for( int ic = 0; icnum_contours; ic++ ) + { + if( (m_gpc_poly->hole)[ic] ) + { + CPolyLine* ext_poly = NULL; + if( n_ext_cont == 1 ) + { + ext_poly = this; + } + else + { + // find the polygon that contains this hole + for( int i = 0; icontour[ic].num_vertices; i++ ) + { + int x = to_int( ( (m_gpc_poly->contour)[ic].vertex )[i].x ); + int y = to_int( ( (m_gpc_poly->contour)[ic].vertex )[i].y ); + if( TestPointInside( x, y ) ) + ext_poly = this; + else + { + for( int ext_ic = 0; ext_icTestPointInside( x, y ) ) + { + ext_poly = (*pa)[ext_ic]; + break; + } + } + } + if( ext_poly ) + break; + } + } + if( !ext_poly ) + wxASSERT( 0 ); + for( int i = 0; icontour[ic].num_vertices; i++ ) + { + int x = to_int( ( (m_gpc_poly->contour)[ic].vertex )[i].x ); + int y = to_int( ( (m_gpc_poly->contour)[ic].vertex )[i].y ); + ext_poly->AppendCorner( x, y, STRAIGHT, FALSE ); + } + + ext_poly->Close( STRAIGHT, FALSE ); + } + } + + if( bRetainArcs ) + RestoreArcs( &arc_array, pa ); + FreeGpcPoly(); + + return n_ext_cont; } + +#endif + +#ifdef USE_GPL_POLY_LIB + // make a php_polygon from first contour int CPolyLine::MakePhpPoly() { - FreePhpPoly(); - polygon test_poly; - int nv = GetContourEnd(0); - for( int iv=0; iv<=nv; iv++ ) - { - int x = GetX(iv)/DENOM; - int y = GetY(iv)/DENOM; - m_php_poly->addv( x, y ); - } - return 0; + FreePhpPoly(); + int nv = GetContourEnd( 0 ); + for( int iv = 0; iv <= nv; iv++ ) + m_php_poly->addv( GetX( iv ), GetY( iv ) ); + + m_php_poly->getFirst()->m_id = 1; + return 0; } + void CPolyLine::FreePhpPoly() { - // delete all vertices - while( m_php_poly->m_cnt > 1 ) - { - vertex * fv = m_php_poly->getFirst(); - m_php_poly->del( fv->m_nextV ); - } - delete m_php_poly->m_first; - m_php_poly->m_first = NULL; - m_php_poly->m_cnt = 0; + // delete all vertices + while( m_php_poly->m_cnt > 1 ) + { + vertex* fv = m_php_poly->getFirst(); + m_php_poly->del( fv->m_nextV ); + } + + delete m_php_poly->m_first; + m_php_poly->m_first = NULL; + m_php_poly->m_cnt = 0; } + // Use the php clipping lib to clip this poly against poly // -void CPolyLine::ClipPhpPolygon( int php_op, CPolyLine * poly ) +void CPolyLine::ClipPhpPolygon( int php_op, CPolyLine* poly ) { - Undraw(); - poly->MakePhpPoly(); - MakePhpPoly(); - polygon * p = m_php_poly->boolean( poly->m_php_poly, php_op ); - poly->FreePhpPoly(); - FreePhpPoly(); + Undraw(); + poly->MakePhpPoly(); + MakePhpPoly(); + polygon* p = m_php_poly->boolean( poly->m_php_poly, php_op ); + poly->FreePhpPoly(); + FreePhpPoly(); + + if( p ) + { + // now screw with the PolyLine + corner.clear(); + side_style.clear(); + + do + { + vertex* v = p->getFirst(); + Start( m_layer, to_int( v->X() ), to_int( v->Y() ), m_HatchStyle ); + + do + { + vertex* n = v->Next(); + AppendCorner( to_int( v->X() ), to_int( (v->Y()) ) ); + v = n; + } while( v->id() != p->getFirst()->id() ); + + Close(); - if( p ) - { - // now screw with the PolyLine - corner.clear(); - side_style.clear(); - do - { - vertex * v = p->getFirst(); - Start( m_layer, to_int(v->X()*DENOM), to_int(v->Y()*DENOM), m_HatchStyle ); - do - { - vertex * n = v->Next(); - AppendCorner( to_int(v->X()*DENOM), to_int((v->Y()*DENOM )) ); - v = n; - } - while( v->id() != p->getFirst()->id() ); - Close(); // p = p->NextPoly(); - delete p; - p = NULL; - } - while( p ); - } - Draw(); + delete p; + p = NULL; + } while( p ); + } + Draw(); } -// make a gpc_polygon for a closed polyline contour -// approximates arcs with multiple straight-line segments -// if icontour = -1, make polygon with all contours, -// combining intersecting contours if possible -// returns data on arcs in arc_array -// + +#endif + +int CPolyLine::MakePolygonFromAreaOutlines( int icontour, std::vector * arc_array ) +{ + #ifdef USE_GPC_POLY_LIB + return MakeGpcPoly( icontour, arc_array ); + #endif + #ifdef USE_GPL_POLY_LIB + return MakePhpPoly( ); + #endif +} + + +void CPolyLine::FreePolygon() +{ + #ifdef USE_GPC_POLY_LIB + FreeGpcPoly(); + #endif + #ifdef USE_GPL_POLY_LIB + FreePhpPoly(); + #endif +} + + +int CPolyLine::NormalizeAreaOutlines( std::vector * pa, bool bRetainArcs ) +{ + #ifdef USE_GPC_POLY_LIB + return NormalizeWithGpc( pa, bRetainArcs ); + #endif + #ifdef USE_GPL_POLY_LIB + #warning NormalizeAreaOutlines with GPL lib must be finished (TODO) + return 1; + #endif +} + + +#ifdef USE_GPC_POLY_LIB + +/** Function MakeGpcPoly + * make a gpc_polygon for a closed polyline contour + * approximates arcs with multiple straight-line segments + * @param icontour : if icontour = -1, make polygon with all contours, + * combining intersecting contours if possible + * @param arc_array : return data on arcs in arc_array + * @return error: 0 if Ok, 1 if error + */ int CPolyLine::MakeGpcPoly( int icontour, std::vector * arc_array ) { - if( m_gpc_poly->num_contours ) - FreeGpcPoly(); - if( !GetClosed() && (icontour == (GetNumContours()-1) || icontour == -1)) - return 1; // error + if( m_gpc_poly->num_contours ) + FreeGpcPoly(); + if( !GetClosed() && (icontour == (GetNumContours() - 1) || icontour == -1) ) + return 1; // error - // initialize m_gpc_poly - m_gpc_poly->num_contours = 0; - m_gpc_poly->hole = NULL; - m_gpc_poly->contour = NULL; - int n_arcs = 0; + // initialize m_gpc_poly + m_gpc_poly->num_contours = 0; + m_gpc_poly->hole = NULL; + m_gpc_poly->contour = NULL; + int n_arcs = 0; - int first_contour = icontour; - int last_contour = icontour; - if( icontour == -1 ) - { - first_contour = 0; - last_contour = GetNumContours() - 1; - } - if( arc_array ) - arc_array->clear(); - int iarc = 0; - for( int icont=first_contour; icont<=last_contour; icont++ ) - { - // make gpc_polygon for this contour - gpc_polygon * gpc = new gpc_polygon; - gpc->num_contours = 0; - gpc->hole = NULL; - gpc->contour = NULL; + int first_contour = icontour; + int last_contour = icontour; + if( icontour == -1 ) + { + first_contour = 0; + last_contour = GetNumContours() - 1; + } + if( arc_array ) + arc_array->clear(); + int iarc = 0; + for( int icont = first_contour; icont<=last_contour; icont++ ) + { + // make gpc_polygon for this contour + gpc_polygon* gpc = new gpc_polygon; + gpc->num_contours = 0; + gpc->hole = NULL; + gpc->contour = NULL; - // first, calculate number of vertices in contour - int n_vertices = 0; - int ic_st = GetContourStart(icont); - int ic_end = GetContourEnd(icont); - for( int ic=ic_st; ic<=ic_end; ic++ ) - { - int style = side_style[ic]; - int x1 = corner[ic].x; - int y1 = corner[ic].y; - int x2, y2; - if( ic < ic_end ) - { - x2 = corner[ic+1].x; - y2 = corner[ic+1].y; - } - else - { - x2 = corner[ic_st].x; - y2 = corner[ic_st].y; - } - if( style == STRAIGHT ) - n_vertices++; - else - { - // style is ARC_CW or ARC_CCW - int n; // number of steps for arcs - n = (abs(x2-x1)+abs(y2-y1))/(CArc::MAX_STEP); - n = max( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc - n_vertices += n; - n_arcs++; - } - } - // now create gcp_vertex_list for this contour - gpc_vertex_list * g_v_list = new gpc_vertex_list; - g_v_list->vertex = (gpc_vertex*)calloc( sizeof(gpc_vertex), n_vertices ); - g_v_list->num_vertices = n_vertices; - int ivtx = 0; - for( int ic=ic_st; ic<=ic_end; ic++ ) - { - int style = side_style[ic]; - int x1 = corner[ic].x; - int y1 = corner[ic].y; - int x2, y2; - if( ic < ic_end ) - { - x2 = corner[ic+1].x; - y2 = corner[ic+1].y; - } - else - { - x2 = corner[ic_st].x; - y2 = corner[ic_st].y; - } - if( style == STRAIGHT ) - { - g_v_list->vertex[ivtx].x = x1; - g_v_list->vertex[ivtx].y = y1; - ivtx++; - } - else - { - // style is arc_cw or arc_ccw - int n; // number of steps for arcs - n = (abs(x2-x1)+abs(y2-y1))/(CArc::MAX_STEP); - n = max( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc - double xo, yo, theta1, theta2, a, b; - a = fabs( (double)(x1 - x2) ); - b = fabs( (double)(y1 - y2) ); - if( style == CPolyLine::ARC_CW ) - { - // clockwise arc (ie.quadrant of ellipse) - if( x2 > x1 && y2 > y1 ) - { - // first quadrant, draw second quadrant of ellipse - xo = x2; - yo = y1; - theta1 = pi; - theta2 = pi/2.0; - } - else if( x2 < x1 && y2 > y1 ) - { - // second quadrant, draw third quadrant of ellipse - xo = x1; - yo = y2; - theta1 = 3.0*pi/2.0; - theta2 = pi; - } - else if( x2 < x1 && y2 < y1 ) - { - // third quadrant, draw fourth quadrant of ellipse - xo = x2; - yo = y1; - theta1 = 2.0*pi; - theta2 = 3.0*pi/2.0; - } - else - { - xo = x1; // fourth quadrant, draw first quadrant of ellipse - yo = y2; - theta1 = pi/2.0; - theta2 = 0.0; - } - } - else - { - // counter-clockwise arc - if( x2 > x1 && y2 > y1 ) - { - xo = x1; // first quadrant, draw fourth quadrant of ellipse - yo = y2; - theta1 = 3.0*pi/2.0; - theta2 = 2.0*pi; - } - else if( x2 < x1 && y2 > y1 ) - { - xo = x2; // second quadrant - yo = y1; - theta1 = 0.0; - theta2 = pi/2.0; - } - else if( x2 < x1 && y2 < y1 ) - { - xo = x1; // third quadrant - yo = y2; - theta1 = pi/2.0; - theta2 = pi; - } - else - { - xo = x2; // fourth quadrant - yo = y1; - theta1 = pi; - theta2 = 3.0*pi/2.0; - } - } - // now write steps for arc - if( arc_array ) - { - CArc new_arc; - new_arc.style = style; - new_arc.n_steps = n; - new_arc.xi = x1; - new_arc.yi = y1; - new_arc.xf = x2; - new_arc.yf = y2; - arc_array->push_back(new_arc); - iarc++; - } - for( int is=0; isvertex[ivtx].x = x; - g_v_list->vertex[ivtx].y = y; - ivtx++; - } - } - } - if( n_vertices != ivtx ) - wxASSERT(0); - // add vertex_list to gpc - gpc_add_contour( gpc, g_v_list, 0 ); - // now clip m_gpc_poly with gpc, put new poly into result - gpc_polygon * result = new gpc_polygon; - if( icontour == -1 && icont != 0 ) - gpc_polygon_clip( GPC_DIFF, m_gpc_poly, gpc, result ); // hole - else - gpc_polygon_clip( GPC_UNION, m_gpc_poly, gpc, result ); // outside - // now copy result to m_gpc_poly - gpc_free_polygon( m_gpc_poly ); - delete m_gpc_poly; - m_gpc_poly = result; - gpc_free_polygon( gpc ); - delete gpc; - free( g_v_list->vertex ); - free( g_v_list ); - } - return 0; + // first, calculate number of vertices in contour + int n_vertices = 0; + int ic_st = GetContourStart( icont ); + int ic_end = GetContourEnd( icont ); + for( int ic = ic_st; ic<=ic_end; ic++ ) + { + int style = side_style[ic]; + int x1 = corner[ic].x; + int y1 = corner[ic].y; + int x2, y2; + if( ic < ic_end ) + { + x2 = corner[ic + 1].x; + y2 = corner[ic + 1].y; + } + else + { + x2 = corner[ic_st].x; + y2 = corner[ic_st].y; + } + if( style == STRAIGHT ) + n_vertices++; + else + { + // style is ARC_CW or ARC_CCW + int n; // number of steps for arcs + n = ( abs( x2 - x1 ) + abs( y2 - y1 ) ) / (CArc::MAX_STEP); + n = max( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc + n_vertices += n; + n_arcs++; + } + } + + // now create gcp_vertex_list for this contour + gpc_vertex_list* g_v_list = new gpc_vertex_list; + g_v_list->vertex = (gpc_vertex*) calloc( sizeof(gpc_vertex), n_vertices ); + g_v_list->num_vertices = n_vertices; + int ivtx = 0; + for( int ic = ic_st; ic<=ic_end; ic++ ) + { + int style = side_style[ic]; + int x1 = corner[ic].x; + int y1 = corner[ic].y; + int x2, y2; + if( ic < ic_end ) + { + x2 = corner[ic + 1].x; + y2 = corner[ic + 1].y; + } + else + { + x2 = corner[ic_st].x; + y2 = corner[ic_st].y; + } + if( style == STRAIGHT ) + { + g_v_list->vertex[ivtx].x = x1; + g_v_list->vertex[ivtx].y = y1; + ivtx++; + } + else + { + // style is arc_cw or arc_ccw + int n; // number of steps for arcs + n = ( abs( x2 - x1 ) + abs( y2 - y1 ) ) / (CArc::MAX_STEP); + n = max( n, CArc::MIN_STEPS ); // or at most 5 degrees of arc + double xo, yo, theta1, theta2, a, b; + a = fabs( (double) (x1 - x2) ); + b = fabs( (double) (y1 - y2) ); + if( style == CPolyLine::ARC_CW ) + { + // clockwise arc (ie.quadrant of ellipse) + if( x2 > x1 && y2 > y1 ) + { + // first quadrant, draw second quadrant of ellipse + xo = x2; + yo = y1; + theta1 = pi; + theta2 = pi / 2.0; + } + else if( x2 < x1 && y2 > y1 ) + { + // second quadrant, draw third quadrant of ellipse + xo = x1; + yo = y2; + theta1 = 3.0 * pi / 2.0; + theta2 = pi; + } + else if( x2 < x1 && y2 < y1 ) + { + // third quadrant, draw fourth quadrant of ellipse + xo = x2; + yo = y1; + theta1 = 2.0 * pi; + theta2 = 3.0 * pi / 2.0; + } + else + { + xo = x1; // fourth quadrant, draw first quadrant of ellipse + yo = y2; + theta1 = pi / 2.0; + theta2 = 0.0; + } + } + else + { + // counter-clockwise arc + if( x2 > x1 && y2 > y1 ) + { + xo = x1; // first quadrant, draw fourth quadrant of ellipse + yo = y2; + theta1 = 3.0 * pi / 2.0; + theta2 = 2.0 * pi; + } + else if( x2 < x1 && y2 > y1 ) + { + xo = x2; // second quadrant + yo = y1; + theta1 = 0.0; + theta2 = pi / 2.0; + } + else if( x2 < x1 && y2 < y1 ) + { + xo = x1; // third quadrant + yo = y2; + theta1 = pi / 2.0; + theta2 = pi; + } + else + { + xo = x2; // fourth quadrant + yo = y1; + theta1 = pi; + theta2 = 3.0 * pi / 2.0; + } + } + + // now write steps for arc + if( arc_array ) + { + CArc new_arc; + new_arc.style = style; + new_arc.n_steps = n; + new_arc.xi = x1; + new_arc.yi = y1; + new_arc.xf = x2; + new_arc.yf = y2; + arc_array->push_back( new_arc ); + iarc++; + } + for( int is = 0; isvertex[ivtx].x = x; + g_v_list->vertex[ivtx].y = y; + ivtx++; + } + } + } + + if( n_vertices != ivtx ) + wxASSERT( 0 ); + + // add vertex_list to gpc + gpc_add_contour( gpc, g_v_list, 0 ); + + // now clip m_gpc_poly with gpc, put new poly into result + gpc_polygon* result = new gpc_polygon; + if( icontour == -1 && icont != 0 ) + gpc_polygon_clip( GPC_DIFF, m_gpc_poly, gpc, result ); // hole + else + gpc_polygon_clip( GPC_UNION, m_gpc_poly, gpc, result ); // outside + // now copy result to m_gpc_poly + gpc_free_polygon( m_gpc_poly ); + delete m_gpc_poly; + m_gpc_poly = result; + gpc_free_polygon( gpc ); + delete gpc; + free( g_v_list->vertex ); + free( g_v_list ); + } + + return 0; } -int CPolyLine::FreeGpcPoly() + +void CPolyLine::FreeGpcPoly() { - if( m_gpc_poly->num_contours ) - { - delete m_gpc_poly->contour->vertex; - delete m_gpc_poly->contour; - delete m_gpc_poly->hole; - } - m_gpc_poly->num_contours = 0; - return 0; + if( m_gpc_poly->num_contours ) + { + delete m_gpc_poly->contour->vertex; + delete m_gpc_poly->contour; + delete m_gpc_poly->hole; + } + m_gpc_poly->num_contours = 0; } +#endif + // Restore arcs to a polygon where they were replaced with steps // If pa != NULL, also use polygons in pa array // int CPolyLine::RestoreArcs( std::vector * arc_array, std::vector * pa ) { - // get poly info - int n_polys = 1; - if( pa ) - n_polys += pa->size(); - CPolyLine * poly; + // get poly info + int n_polys = 1; - // undraw polys and clear utility flag for all corners - for( int ip=0; ipUndraw(); - for( int ic=0; icGetNumCorners(); ic++ ) - poly->SetUtility( ic, 0 ); // clear utility flag - } + if( pa ) + n_polys += pa->size(); + CPolyLine* poly; - // find arcs and replace them - bool bFound; - int arc_start = 0; - int arc_end = 0; - for( unsigned iarc=0; iarcsize(); iarc++ ) - { - int arc_xi = (*arc_array)[iarc].xi; - int arc_yi = (*arc_array)[iarc].yi; - int arc_xf = (*arc_array)[iarc].xf; - int arc_yf = (*arc_array)[iarc].yf; - int n_steps = (*arc_array)[iarc].n_steps; - int style = (*arc_array)[iarc].style; - bFound = FALSE; - // loop through polys - for( int ip=0; ipGetNumContours(); icont++ ) - { - int ic_start = poly->GetContourStart(icont); - int ic_end = poly->GetContourEnd(icont); - if( (ic_end-ic_start) > n_steps ) - { - for( int ic=ic_start; ic<=ic_end; ic++ ) - { - int ic_next = ic+1; - if( ic_next > ic_end ) - ic_next = ic_start; - int xi = poly->GetX(ic); - int yi = poly->GetY(ic); - if( xi == arc_xi && yi == arc_yi ) - { - // test for forward arc - int ic2 = ic + n_steps; - if( ic2 > ic_end ) - ic2 = ic2 - ic_end + ic_start - 1; - int xf = poly->GetX(ic2); - int yf = poly->GetY(ic2); - if( xf == arc_xf && yf == arc_yf ) - { - // arc from ic to ic2 - bFound = TRUE; - arc_start = ic; - arc_end = ic2; - } - else - { - // try reverse arc - ic2 = ic - n_steps; - if( ic2 < ic_start ) - ic2 = ic2 - ic_start + ic_end + 1; - xf = poly->GetX(ic2); - yf = poly->GetY(ic2); - if( xf == arc_xf && yf == arc_yf ) - { - // arc from ic2 to ic - bFound = TRUE; - arc_start = ic2; - arc_end = ic; - style = 3 - style; - } - } - if( bFound ) - { - poly->side_style[arc_start] = style; - // mark corners for deletion from arc_start+1 to arc_end-1 - for( int i=arc_start+1; i!=arc_end; ) - { - if( i > ic_end ) - i = ic_start; - poly->SetUtility( i, 1 ); - if( i == ic_end ) - i = ic_start; - else - i++; - } - break; - } - } - if( bFound ) - break; - } - } - if( bFound ) - break; - } - } - if( bFound ) - (*arc_array)[iarc].bFound = TRUE; - } + // undraw polys and clear utility flag for all corners + for( int ip = 0; ipUndraw(); + for( int ic = 0; icGetNumCorners(); ic++ ) + poly->SetUtility( ic, 0 ); // clear utility flag - // now delete all marked corners - for( int ip=0; ipGetNumCorners()-1; ic>=0; ic-- ) - { - if( poly->GetUtility(ic) ) - poly->DeleteCorner( ic, FALSE ); - } - } - return 0; + } + + // find arcs and replace them + bool bFound; + int arc_start = 0; + int arc_end = 0; + for( unsigned iarc = 0; iarcsize(); iarc++ ) + { + int arc_xi = (*arc_array)[iarc].xi; + int arc_yi = (*arc_array)[iarc].yi; + int arc_xf = (*arc_array)[iarc].xf; + int arc_yf = (*arc_array)[iarc].yf; + int n_steps = (*arc_array)[iarc].n_steps; + int style = (*arc_array)[iarc].style; + bFound = FALSE; + + // loop through polys + for( int ip = 0; ipGetNumContours(); icont++ ) + { + int ic_start = poly->GetContourStart( icont ); + int ic_end = poly->GetContourEnd( icont ); + if( (ic_end - ic_start) > n_steps ) + { + for( int ic = ic_start; ic<=ic_end; ic++ ) + { + int ic_next = ic + 1; + if( ic_next > ic_end ) + ic_next = ic_start; + int xi = poly->GetX( ic ); + int yi = poly->GetY( ic ); + if( xi == arc_xi && yi == arc_yi ) + { + // test for forward arc + int ic2 = ic + n_steps; + if( ic2 > ic_end ) + ic2 = ic2 - ic_end + ic_start - 1; + int xf = poly->GetX( ic2 ); + int yf = poly->GetY( ic2 ); + if( xf == arc_xf && yf == arc_yf ) + { + // arc from ic to ic2 + bFound = TRUE; + arc_start = ic; + arc_end = ic2; + } + else + { + // try reverse arc + ic2 = ic - n_steps; + if( ic2 < ic_start ) + ic2 = ic2 - ic_start + ic_end + 1; + xf = poly->GetX( ic2 ); + yf = poly->GetY( ic2 ); + if( xf == arc_xf && yf == arc_yf ) + { + // arc from ic2 to ic + bFound = TRUE; + arc_start = ic2; + arc_end = ic; + style = 3 - style; + } + } + if( bFound ) + { + poly->side_style[arc_start] = style; + + // mark corners for deletion from arc_start+1 to arc_end-1 + for( int i = arc_start + 1; i!=arc_end; ) + { + if( i > ic_end ) + i = ic_start; + poly->SetUtility( i, 1 ); + if( i == ic_end ) + i = ic_start; + else + i++; + } + + break; + } + } + if( bFound ) + break; + } + } + if( bFound ) + break; + } + } + + if( bFound ) + (*arc_array)[iarc].bFound = TRUE; + } + + // now delete all marked corners + for( int ip = 0; ipGetNumCorners() - 1; ic>=0; ic-- ) + { + if( poly->GetUtility( ic ) ) + poly->DeleteCorner( ic, FALSE ); + } + } + + return 0; } + // initialize new polyline // set layer, width, selection box size, starting point, id and pointer // @@ -605,147 +687,154 @@ int CPolyLine::RestoreArcs( std::vector * arc_array, std::vector 0 && !corner[corner.size()-1].end_contour ) - side_style[corner.size()-1] = style; - int dl_type; - if( style == CPolyLine::STRAIGHT ) - dl_type = DL_LINE; - else if( style == CPolyLine::ARC_CW ) - dl_type = DL_ARC_CW; - else if( style == CPolyLine::ARC_CCW ) - dl_type = DL_ARC_CCW; - else - wxASSERT(0); - if( bDraw ) - Draw(); + // add entries for new corner and side + corner.push_back( poly_pt ); + side_style.push_back( style ); + if( corner.size() > 0 && !corner[corner.size() - 1].end_contour ) + side_style[corner.size() - 1] = style; + int dl_type; + if( style == CPolyLine::STRAIGHT ) + dl_type = DL_LINE; + else if( style == CPolyLine::ARC_CW ) + dl_type = DL_ARC_CW; + else if( style == CPolyLine::ARC_CCW ) + dl_type = DL_ARC_CCW; + else + wxASSERT( 0 ); + if( bDraw ) + Draw(); } + // close last polyline contour // void CPolyLine::Close( int style, bool bDraw ) { - if( GetClosed() ) - wxASSERT(0); - Undraw(); - side_style[corner.size()-1] = style; - corner[corner.size()-1].end_contour = TRUE; - if( bDraw ) - Draw(); + if( GetClosed() ) + wxASSERT( 0 ); + Undraw(); + side_style[corner.size() - 1] = style; + corner[corner.size() - 1].end_contour = TRUE; + if( bDraw ) + Draw(); } + // move corner of polyline // void CPolyLine::MoveCorner( int ic, int x, int y ) { - Undraw(); - corner[ic].x = x; - corner[ic].y = y; - Draw(); + Undraw(); + corner[ic].x = x; + corner[ic].y = y; + Draw(); } + // delete corner and adjust arrays // void CPolyLine::DeleteCorner( int ic, bool bDraw ) { - Undraw(); - int icont = GetContour( ic ); - int istart = GetContourStart( icont ); - int iend = GetContourEnd( icont ); - bool bClosed = icont < GetNumContours()-1 || GetClosed(); + Undraw(); + int icont = GetContour( ic ); + int istart = GetContourStart( icont ); + int iend = GetContourEnd( icont ); + bool bClosed = icont < GetNumContours() - 1 || GetClosed(); - if( !bClosed ) - { - // open contour, must be last contour - corner.erase( corner.begin() + ic ); + if( !bClosed ) + { + // open contour, must be last contour + corner.erase( corner.begin() + ic ); - if( ic != istart ) - side_style.erase( side_style.begin() + ic-1 ); - } - else - { - // closed contour - corner.erase( corner.begin() + ic ); - side_style.erase( side_style.begin() + ic ); - if( ic == iend ) - corner[ic-1].end_contour = TRUE; - } - if( bClosed && GetContourSize(icont) < 3 ) - { - // delete the entire contour - RemoveContour( icont ); - } - if( bDraw ) - Draw(); + if( ic != istart ) + side_style.erase( side_style.begin() + ic - 1 ); + } + else + { + // closed contour + corner.erase( corner.begin() + ic ); + side_style.erase( side_style.begin() + ic ); + if( ic == iend ) + corner[ic - 1].end_contour = TRUE; + } + if( bClosed && GetContourSize( icont ) < 3 ) + { + // delete the entire contour + RemoveContour( icont ); + } + if( bDraw ) + Draw(); } + /******************************************/ void CPolyLine::RemoveContour( int icont ) /******************************************/ + /** * Function RemoveContour * @param icont = contour number to remove * remove a contour only if there is more than 1 contour */ { - Undraw(); - int istart = GetContourStart( icont ); - int iend = GetContourEnd( icont ); + Undraw(); + int istart = GetContourStart( icont ); + int iend = GetContourEnd( icont ); - if( icont == 0 && GetNumContours() == 1 ) - { - // remove the only contour - wxASSERT(0); - } - else if( icont == GetNumContours()-1 ) - { - // remove last contour - corner.erase( corner.begin() + istart, corner.end() ); - side_style.erase( side_style.begin() + istart, side_style.end() ); - } - else - { - // remove closed contour - for( int ic=iend; ic>=istart; ic-- ) - { - corner.erase( corner.begin() + ic ); - side_style.erase( side_style.begin() + ic ); - } - } - Draw(); + if( icont == 0 && GetNumContours() == 1 ) + { + // remove the only contour + wxASSERT( 0 ); + } + else if( icont == GetNumContours() - 1 ) + { + // remove last contour + corner.erase( corner.begin() + istart, corner.end() ); + side_style.erase( side_style.begin() + istart, side_style.end() ); + } + else + { + // remove closed contour + for( int ic = iend; ic>=istart; ic-- ) + { + corner.erase( corner.begin() + ic ); + side_style.erase( side_style.begin() + ic ); + } + } + Draw(); } /******************************************/ void CPolyLine::RemoveAllContours( void ) /******************************************/ + /** * function RemoveAllContours * removes all corners from the lists. * Others params are not chnaged */ { - corner.clear( ); - side_style.clear( ); + corner.clear(); + side_style.clear(); } @@ -756,594 +845,651 @@ void CPolyLine::RemoveAllContours( void ) */ void CPolyLine::InsertCorner( int ic, int x, int y ) { - Undraw(); - if ( (unsigned)(ic) >= corner.size() ) - { - corner.push_back( CPolyPt(x,y) ); - side_style.push_back( STRAIGHT ); - } - else - { - corner.insert( corner.begin() + ic + 1, CPolyPt(x,y) ); - side_style.insert( side_style.begin() + ic + 1, STRAIGHT ); - } - - if( (unsigned)(ic+1) < corner.size() ) - { - if( corner[ic].end_contour ) - { - corner[ic+1].end_contour = TRUE; - corner[ic].end_contour = FALSE; - } - } - Draw(); + Undraw(); + if( (unsigned) (ic) >= corner.size() ) + { + corner.push_back( CPolyPt( x, y ) ); + side_style.push_back( STRAIGHT ); + } + else + { + corner.insert( corner.begin() + ic + 1, CPolyPt( x, y ) ); + side_style.insert( side_style.begin() + ic + 1, STRAIGHT ); + } + + if( (unsigned) (ic + 1) < corner.size() ) + { + if( corner[ic].end_contour ) + { + corner[ic + 1].end_contour = TRUE; + corner[ic].end_contour = FALSE; + } + } + Draw(); } + // undraw polyline by removing all graphic elements from display list // void CPolyLine::Undraw() { - m_HatchLines.clear(); - bDrawn = FALSE; + m_HatchLines.clear(); + bDrawn = FALSE; } + // draw polyline by adding all graphics to display list // if side style is ARC_CW or ARC_CCW but endpoints are not angled, // convert to STRAIGHT // -void CPolyLine::Draw( ) +void CPolyLine::Draw() { + // first, undraw if necessary + if( bDrawn ) + Undraw(); - // first, undraw if necessary - if( bDrawn ) - Undraw(); - - Hatch(); - bDrawn = TRUE; + Hatch(); + bDrawn = TRUE; } -int CPolyLine::GetX( int ic ) -{ - return corner[ic].x; +int CPolyLine::GetX( int ic ) +{ + return corner[ic].x; } -int CPolyLine::GetY( int ic ) -{ - return corner[ic].y; + +int CPolyLine::GetY( int ic ) +{ + return corner[ic].y; } -int CPolyLine::GetEndContour( int ic ) -{ - return corner[ic].end_contour; + +int CPolyLine::GetEndContour( int ic ) +{ + return corner[ic].end_contour; } + CRect CPolyLine::GetBounds() { - CRect r = GetCornerBounds(); - r.left -= m_Width/2; - r.right += m_Width/2; - r.bottom -= m_Width/2; - r.top += m_Width/2; - return r; + CRect r = GetCornerBounds(); + + r.left -= m_Width / 2; + r.right += m_Width / 2; + r.bottom -= m_Width / 2; + r.top += m_Width / 2; + return r; } + CRect CPolyLine::GetCornerBounds() { - CRect r; - r.left = r.bottom = INT_MAX; - r.right = r.top = INT_MIN; - for( unsigned i=0; i max_x ) - max_x = corner[ic].x; - if( corner[ic].y < min_y ) - min_y = corner[ic].y; - if( corner[ic].y > max_y ) - max_y = corner[ic].y; - } - int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1 - double slope = 0.707106*slope_flag; - int spacing; - if( m_HatchStyle == DIAGONAL_EDGE ) - spacing = 10*PCBU_PER_MIL; - else - spacing = 50*PCBU_PER_MIL; - int max_a, min_a; - if( slope_flag == 1 ) - { - max_a = (int)(max_y - slope*min_x); - min_a = (int)(min_y - slope*max_x); - } - else - { - max_a = (int)(max_y - slope*max_x); - min_a = (int)(min_y - slope*min_x); - } - min_a = (min_a/spacing)*spacing; - // calculate an offset depending on layer number, for a better display of hatches on a multilayer board - int offset = (layer * 7) / 8; - min_a += offset; + if( GetClosed() ) // If not closed, the poly is beeing created and not finalised. Not not hatch + { + enum { + MAXPTS = 100, + }; + int xx[MAXPTS], yy[MAXPTS]; - // now calculate and draw hatch lines - int nc = corner.size(); - // loop through hatch lines - for( int a=min_a; a max_x ) + max_x = corner[ic].x; + if( corner[ic].y < min_y ) + min_y = corner[ic].y; + if( corner[ic].y > max_y ) + max_y = corner[ic].y; + } + + int slope_flag = (layer & 1) ? 1 : -1; // 1 or -1 + double slope = 0.707106 * slope_flag; + int spacing; + if( m_HatchStyle == DIAGONAL_EDGE ) + spacing = 10 * PCBU_PER_MIL; + else + spacing = 50 * PCBU_PER_MIL; + int max_a, min_a; + if( slope_flag == 1 ) + { + max_a = (int) (max_y - slope * min_x); + min_a = (int) (min_y - slope * max_x); + } + else + { + max_a = (int) (max_y - slope * max_x); + min_a = (int) (min_y - slope * min_x); + } + min_a = (min_a / spacing) * spacing; + + // calculate an offset depending on layer number, for a better display of hatches on a multilayer board + int offset = (layer * 7) / 8; + min_a += offset; + + // now calculate and draw hatch lines + int nc = corner.size(); + + // loop through hatch lines + for( int a = min_a; a2 ) - { - for( int istart=0; istart<(npts-1); istart++ ) - { - int max_x = INT_MIN; - int imax = INT_MIN; - for( int i=istart; i max_x ) - { - max_x = xx[i]; - imax = i; - } - } - int temp = xx[istart]; - xx[istart] = xx[imax]; - xx[imax] = temp; - temp = yy[istart]; - yy[istart] = yy[imax]; - yy[imax] = temp; - } - } + // sort points in order of descending x (if more than 2) + if( npts>2 ) + { + for( int istart = 0; istart<(npts - 1); istart++ ) + { + int max_x = INT_MIN; + int imax = INT_MIN; + for( int i = istart; i max_x ) + { + max_x = xx[i]; + imax = i; + } + } - // draw lines - for( int ip=0; ip 0 ) - dx = 20*NM_PER_MIL; - else - dx = -20*NM_PER_MIL; - double x1 = xx[ip] + dx; - double x2 = xx[ip+1] - dx; - double y1 = yy[ip] + dx*slope; - double y2 = yy[ip+1] - dx*slope; - m_HatchLines.push_back(CSegment(xx[ip], yy[ip], to_int(x1), to_int(y1)) ); - m_HatchLines.push_back(CSegment(xx[ip+1], yy[ip+1], to_int(x2), to_int(y2)) ); - } - } - } // end for - } + int temp = xx[istart]; + xx[istart] = xx[imax]; + xx[imax] = temp; + temp = yy[istart]; + yy[istart] = yy[imax]; + yy[imax] = temp; + } + } + + // draw lines + for( int ip = 0; ip 0 ) + dx = 20 * NM_PER_MIL; + else + dx = -20 * NM_PER_MIL; + double x1 = xx[ip] + dx; + double x2 = xx[ip + 1] - dx; + double y1 = yy[ip] + dx * slope; + double y2 = yy[ip + 1] - dx * slope; + m_HatchLines.push_back( CSegment( xx[ip], yy[ip], to_int( x1 ), to_int( y1 ) ) ); + m_HatchLines.push_back( CSegment( xx[ip + 1], yy[ip + 1], to_int( x2 ), + to_int( y2 ) ) ); + } + } + } // end for + + } } + // test to see if a point is inside polyline // bool CPolyLine::TestPointInside( int x, int y ) { - enum { MAXPTS = 100 }; - if( !GetClosed() ) - wxASSERT(0); + enum { + MAXPTS = 100 + }; + if( !GetClosed() ) + wxASSERT( 0 ); - // define line passing through (x,y), with slope = 2/3; - // get intersection points - double xx[MAXPTS], yy[MAXPTS]; - double slope = (double)2.0/3.0; - double a = y - slope*x; - int nloops = 0; - int npts; - // make this a loop so if my homebrew algorithm screws up, we try it again - do - { - // now find all intersection points of line with polyline sides - npts = 0; - for( int icont=0; icont x ) - ncount++; - } - if( ncount%2 ) - return TRUE; - else - return FALSE; + // make this a loop so if my homebrew algorithm screws up, we try it again + do + { + // now find all intersection points of line with polyline sides + npts = 0; + for( int icont = 0; icont x ) + ncount++; + } + + if( ncount % 2 ) + return TRUE; + else + return FALSE; } + // test to see if a point is inside polyline contour // bool CPolyLine::TestPointInsideContour( int icont, int x, int y ) { - if( icont >= GetNumContours() ) - return FALSE; + if( icont >= GetNumContours() ) + return FALSE; - enum { MAXPTS = 100 }; - if( !GetClosed() ) - wxASSERT(0); + enum { + MAXPTS = 100 + }; + if( !GetClosed() ) + wxASSERT( 0 ); - // define line passing through (x,y), with slope = 2/3; - // get intersection points - double xx[MAXPTS], yy[MAXPTS]; - double slope = (double)2.0/3.0; - double a = y - slope*x; - int nloops = 0; - int npts; - // make this a loop so if my homebrew algorithm screws up, we try it again - do - { - // now find all intersection points of line with polyline sides - npts = 0; - int istart = GetContourStart( icont ); - int iend = GetContourEnd( icont ); - for( int ic=istart; ic<=iend; ic++ ) - { - double x, y, x2, y2; - int ok; - if( ic == istart ) - ok = FindLineSegmentIntersection( a, slope, - corner[iend].x, corner[iend].y, - corner[istart].x, corner[istart].y, - side_style[corner.size()-1], - &x, &y, &x2, &y2 ); - else - ok = FindLineSegmentIntersection( a, slope, - corner[ic-1].x, corner[ic-1].y, - corner[ic].x, corner[ic].y, - side_style[ic-1], - &x, &y, &x2, &y2 ); - if( ok ) - { - xx[npts] = (int)x; - yy[npts] = (int)y; - npts++; - wxASSERT( npts x ) - ncount++; - } - if( ncount%2 ) - return TRUE; - else - return FALSE; + // make this a loop so if my homebrew algorithm screws up, we try it again + do + { + // now find all intersection points of line with polyline sides + npts = 0; + int istart = GetContourStart( icont ); + int iend = GetContourEnd( icont ); + for( int ic = istart; ic<=iend; ic++ ) + { + double x, y, x2, y2; + int ok; + if( ic == istart ) + ok = FindLineSegmentIntersection( a, slope, + corner[iend].x, corner[iend].y, + corner[istart].x, corner[istart].y, + side_style[corner.size() - 1], + &x, &y, &x2, &y2 ); + else + ok = FindLineSegmentIntersection( a, slope, + corner[ic - 1].x, corner[ic - 1].y, + corner[ic].x, corner[ic].y, + side_style[ic - 1], + &x, &y, &x2, &y2 ); + if( ok ) + { + xx[npts] = (int) x; + yy[npts] = (int) y; + npts++; + wxASSERT( npts x ) + ncount++; + } + + if( ncount % 2 ) + return TRUE; + else + return FALSE; } // copy data from another poly, but don't draw it // -void CPolyLine::Copy( CPolyLine * src ) +void CPolyLine::Copy( CPolyLine* src ) { - Undraw(); + Undraw(); - // copy corners - for( unsigned ii=0; ii < src->corner.size(); ii++ ) - corner.push_back(src->corner[ii]); - // copy side styles - for( unsigned ii=0; ii < src->side_style.size(); ii++ ) - side_style.push_back(src->side_style[ii]); - // don't copy the Gpc_poly, just clear the old one - FreeGpcPoly(); + // copy corners + for( unsigned ii = 0; ii < src->corner.size(); ii++ ) + corner.push_back( src->corner[ii] ); + + // copy side styles + for( unsigned ii = 0; ii < src->side_style.size(); ii++ ) + side_style.push_back( src->side_style[ii] ); + +#ifdef USE_GPC_POLY_LIB + + // don't copy the Gpc_poly, just clear the old one + FreeGpcPoly(); +#endif } /*******************************************/ bool CPolyLine::IsCutoutContour( int icont ) /*******************************************/ + /* * return true if the corner icont is inside the outline (i.e it is a hole) */ { - int ncont = GetContour( icont ); - if ( ncont == 0 ) // the first contour is the main outline, not an hole - return false; - return true; + int ncont = GetContour( icont ); + + if( ncont == 0 ) // the first contour is the main outline, not an hole + return false; + return true; } + void CPolyLine::MoveOrigin( int x_off, int y_off ) { - Undraw(); - for( int ic=0; ic < GetNumCorners(); ic++ ) - { - SetX( ic, GetX(ic) + x_off ); - SetY( ic, GetY(ic) + y_off ); - } - Draw(); + Undraw(); + for( int ic = 0; ic < GetNumCorners(); ic++ ) + { + SetX( ic, GetX( ic ) + x_off ); + SetY( ic, GetY( ic ) + y_off ); + } + + Draw(); } @@ -1351,191 +1497,208 @@ void CPolyLine::MoveOrigin( int x_off, int y_off ) // the calling function should Undraw() before calling them, // and Draw() after // -void CPolyLine::SetX( int ic, int x ) { corner[ic].x = x; } -void CPolyLine::SetY( int ic, int y ) { corner[ic].y = y; } +void CPolyLine::SetX( int ic, int x ) +{ + corner[ic].x = x; +} + + +void CPolyLine::SetY( int ic, int y ) +{ + corner[ic].y = y; +} + void CPolyLine::SetEndContour( int ic, bool end_contour ) { - corner[ic].end_contour = end_contour; + corner[ic].end_contour = end_contour; } + // Create CPolyLine for a pad // -CPolyLine * CPolyLine::MakePolylineForPad( int type, int x, int y, int w, int l, int r, int angle ) +CPolyLine* CPolyLine::MakePolylineForPad( int type, int x, int y, int w, int l, int r, int angle ) { - CPolyLine * poly = new CPolyLine; - int dx = l/2; - int dy = w/2; - if( angle%180 == 90 ) - { - dx = w/2; - dy = l/2; - } - if( type == PAD_ROUND ) - { - poly->Start( 0, x-dx, y, 0 ); - poly->AppendCorner( x, y+dy, ARC_CW, 0 ); - poly->AppendCorner( x+dx, y, ARC_CW, 0 ); - poly->AppendCorner( x, y-dy, ARC_CW, 0 ); - poly->Close( ARC_CW ); - } - return poly; + CPolyLine* poly = new CPolyLine; + int dx = l / 2; + int dy = w / 2; + + if( angle % 180 == 90 ) + { + dx = w / 2; + dy = l / 2; + } + if( type == PAD_ROUND ) + { + poly->Start( 0, x - dx, y, 0 ); + poly->AppendCorner( x, y + dy, ARC_CW, 0 ); + poly->AppendCorner( x + dx, y, ARC_CW, 0 ); + poly->AppendCorner( x, y - dy, ARC_CW, 0 ); + poly->Close( ARC_CW ); + } + return poly; } + // Add cutout for a pad // Convert arcs to multiple straight lines // Do NOT draw or undraw // -void CPolyLine::AddContourForPadClearance( int type, int x, int y, int w, - int l, int r, int angle, int fill_clearance, - int hole_w, int hole_clearance, bool bThermal, int spoke_w ) +void CPolyLine::AddContourForPadClearance( int type, + int x, + int y, + int w, + int l, + int r, + int angle, + int fill_clearance, + int hole_w, + int hole_clearance, + bool bThermal, + int spoke_w ) { - int dx = l/2; - int dy = w/2; - if( angle%180 == 90 ) - { - dx = w/2; - dy = l/2; - } - int x_clearance = max( fill_clearance, hole_clearance+hole_w/2-dx); - int y_clearance = max( fill_clearance, hole_clearance+hole_w/2-dy); - dx += x_clearance; - dy += y_clearance; - if( !bThermal ) - { - // normal clearance - if( type == PAD_ROUND || (type == PAD_NONE && hole_w > 0) ) - { - AppendCorner( x-dx, y, ARC_CW, 0 ); - AppendCorner( x, y+dy, ARC_CW, 0 ); - AppendCorner( x+dx, y, ARC_CW, 0 ); - AppendCorner( x, y-dy, ARC_CW, 0 ); - Close( ARC_CW ); - } - else if( type == PAD_SQUARE || type == PAD_RECT - || type == PAD_RRECT || type == PAD_OVAL ) - { - AppendCorner( x-dx, y-dy, STRAIGHT, 0 ); - AppendCorner( x+dx, y-dy, STRAIGHT, 0 ); - AppendCorner( x+dx, y+dy, STRAIGHT, 0 ); - AppendCorner( x-dx, y+dy, STRAIGHT, 0 ); - Close( STRAIGHT ); - } - } - else - { - // thermal relief - if( type == PAD_ROUND || (type == PAD_NONE && hole_w > 0) ) - { - // draw 4 "wedges" - double r = max(w/2 + fill_clearance, hole_w/2 + hole_clearance); - double start_angle = asin( spoke_w/(2.0*r) ); - double th1, th2, corner_x, corner_y; - th1 = th2 = corner_x = corner_y = 0; // gcc warning fix - for( int i=0; i<4; i++ ) - { - if( i == 0 ) - { - corner_x = spoke_w/2; - corner_y = spoke_w/2; - th1 = start_angle; - th2 = pi/2.0 - start_angle; - } - else if( i == 1 ) - { - corner_x = -spoke_w/2; - corner_y = spoke_w/2; - th1 = pi/2.0 + start_angle; - th2 = pi - start_angle; - } - else if( i == 2 ) - { - corner_x = -spoke_w/2; - corner_y = -spoke_w/2; - th1 = -pi + start_angle; - th2 = -pi/2.0 - start_angle; - } - else if( i == 3 ) - { - corner_x = spoke_w/2; - corner_y = -spoke_w/2; - th1 = -pi/2.0 + start_angle; - th2 = -start_angle; - } - AppendCorner( to_int(x+corner_x), to_int(y+corner_y), STRAIGHT, 0 ); - AppendCorner( to_int(x+r*cos(th1)), to_int(y+r*sin(th1)), STRAIGHT, 0 ); - AppendCorner( to_int(x+r*cos(th2)), to_int(y+r*sin(th2)), ARC_CCW, 0 ); - Close( STRAIGHT ); - } - } - else if( type == PAD_SQUARE || type == PAD_RECT - || type == PAD_RRECT || type == PAD_OVAL ) - { - // draw 4 rectangles - int xL = x - dx; - int xR = x - spoke_w/2; - int yB = y - dy; - int yT = y - spoke_w/2; - AppendCorner( xL, yB, STRAIGHT, 0 ); - AppendCorner( xR, yB, STRAIGHT, 0 ); - AppendCorner( xR, yT, STRAIGHT, 0 ); - AppendCorner( xL, yT, STRAIGHT, 0 ); - Close( STRAIGHT ); - xL = x + spoke_w/2; - xR = x + dx; - AppendCorner( xL, yB, STRAIGHT, 0 ); - AppendCorner( xR, yB, STRAIGHT, 0 ); - AppendCorner( xR, yT, STRAIGHT, 0 ); - AppendCorner( xL, yT, STRAIGHT, 0 ); - Close( STRAIGHT ); - xL = x - dx; - xR = x - spoke_w/2; - yB = y + spoke_w/2; - yT = y + dy; - AppendCorner( xL, yB, STRAIGHT, 0 ); - AppendCorner( xR, yB, STRAIGHT, 0 ); - AppendCorner( xR, yT, STRAIGHT, 0 ); - AppendCorner( xL, yT, STRAIGHT, 0 ); - Close( STRAIGHT ); - xL = x + spoke_w/2; - xR = x + dx; - AppendCorner( xL, yB, STRAIGHT, 0 ); - AppendCorner( xR, yB, STRAIGHT, 0 ); - AppendCorner( xR, yT, STRAIGHT, 0 ); - AppendCorner( xL, yT, STRAIGHT, 0 ); - Close( STRAIGHT ); - } - } - return; + int dx = l / 2; + int dy = w / 2; + + if( angle % 180 == 90 ) + { + dx = w / 2; + dy = l / 2; + } + int x_clearance = max( fill_clearance, hole_clearance + hole_w / 2 - dx ); + int y_clearance = max( fill_clearance, hole_clearance + hole_w / 2 - dy ); + dx += x_clearance; + dy += y_clearance; + if( !bThermal ) + { + // normal clearance + if( type == PAD_ROUND || (type == PAD_NONE && hole_w > 0) ) + { + AppendCorner( x - dx, y, ARC_CW, 0 ); + AppendCorner( x, y + dy, ARC_CW, 0 ); + AppendCorner( x + dx, y, ARC_CW, 0 ); + AppendCorner( x, y - dy, ARC_CW, 0 ); + Close( ARC_CW ); + } + else if( type == PAD_SQUARE || type == PAD_RECT + || type == PAD_RRECT || type == PAD_OVAL ) + { + AppendCorner( x - dx, y - dy, STRAIGHT, 0 ); + AppendCorner( x + dx, y - dy, STRAIGHT, 0 ); + AppendCorner( x + dx, y + dy, STRAIGHT, 0 ); + AppendCorner( x - dx, y + dy, STRAIGHT, 0 ); + Close( STRAIGHT ); + } + } + else + { + // thermal relief + if( type == PAD_ROUND || (type == PAD_NONE && hole_w > 0) ) + { + // draw 4 "wedges" + double r = max( w / 2 + fill_clearance, hole_w / 2 + hole_clearance ); + double start_angle = asin( spoke_w / (2.0 * r) ); + double th1, th2, corner_x, corner_y; + th1 = th2 = corner_x = corner_y = 0; // gcc warning fix + for( int i = 0; i<4; i++ ) + { + if( i == 0 ) + { + corner_x = spoke_w / 2; + corner_y = spoke_w / 2; + th1 = start_angle; + th2 = pi / 2.0 - start_angle; + } + else if( i == 1 ) + { + corner_x = -spoke_w / 2; + corner_y = spoke_w / 2; + th1 = pi / 2.0 + start_angle; + th2 = pi - start_angle; + } + else if( i == 2 ) + { + corner_x = -spoke_w / 2; + corner_y = -spoke_w / 2; + th1 = -pi + start_angle; + th2 = -pi / 2.0 - start_angle; + } + else if( i == 3 ) + { + corner_x = spoke_w / 2; + corner_y = -spoke_w / 2; + th1 = -pi / 2.0 + start_angle; + th2 = -start_angle; + } + AppendCorner( to_int( x + corner_x ), to_int( y + corner_y ), STRAIGHT, 0 ); + AppendCorner( to_int( x + r * cos( th1 ) ), to_int( y + r * sin( + th1 ) ), STRAIGHT, 0 ); + AppendCorner( to_int( x + r * cos( th2 ) ), to_int( y + r * sin( + th2 ) ), ARC_CCW, 0 ); + Close( STRAIGHT ); + } + } + else if( type == PAD_SQUARE || type == PAD_RECT + || type == PAD_RRECT || type == PAD_OVAL ) + { + // draw 4 rectangles + int xL = x - dx; + int xR = x - spoke_w / 2; + int yB = y - dy; + int yT = y - spoke_w / 2; + AppendCorner( xL, yB, STRAIGHT, 0 ); + AppendCorner( xR, yB, STRAIGHT, 0 ); + AppendCorner( xR, yT, STRAIGHT, 0 ); + AppendCorner( xL, yT, STRAIGHT, 0 ); + Close( STRAIGHT ); + xL = x + spoke_w / 2; + xR = x + dx; + AppendCorner( xL, yB, STRAIGHT, 0 ); + AppendCorner( xR, yB, STRAIGHT, 0 ); + AppendCorner( xR, yT, STRAIGHT, 0 ); + AppendCorner( xL, yT, STRAIGHT, 0 ); + Close( STRAIGHT ); + xL = x - dx; + xR = x - spoke_w / 2; + yB = y + spoke_w / 2; + yT = y + dy; + AppendCorner( xL, yB, STRAIGHT, 0 ); + AppendCorner( xR, yB, STRAIGHT, 0 ); + AppendCorner( xR, yT, STRAIGHT, 0 ); + AppendCorner( xL, yT, STRAIGHT, 0 ); + Close( STRAIGHT ); + xL = x + spoke_w / 2; + xR = x + dx; + AppendCorner( xL, yB, STRAIGHT, 0 ); + AppendCorner( xR, yB, STRAIGHT, 0 ); + AppendCorner( xR, yT, STRAIGHT, 0 ); + AppendCorner( xL, yT, STRAIGHT, 0 ); + Close( STRAIGHT ); + } + } + return; } + void CPolyLine::AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num ) { - // get radius - double r = sqrt( (double)(xi-xc)*(xi-xc) + (double)(yi-yc)*(yi-yc) ); - // get angles of start and finish - double th_i = atan2( (double)yi-yc, (double)xi-xc ); - double th_f = atan2( (double)yf-yc, (double)xf-xc ); - double th_d = (th_f - th_i)/(num-1); - double theta = th_i; - // generate arc - for( int ic=0; icGetGpcPoly(), result ); - gpc_free_polygon( m_gpc_poly ); - delete m_gpc_poly; - m_gpc_poly = result; -} - diff --git a/polygon/PolyLine.h b/polygon/PolyLine.h index 47a8b3c3d9..c1a788ee9d 100644 --- a/polygon/PolyLine.h +++ b/polygon/PolyLine.h @@ -5,10 +5,10 @@ // There may be multiple contours in a polyline. // The last contour may be open or closed, any others must be closed. // All of the corners and side-styles are concatenated into 2 arrays, -// separated by setting the end_contour flag of the last corner of +// separated by setting the end_contour flag of the last corner of // each contour. // -// When used for copper areas, the first contour is the outer edge +// When used for copper areas, the first contour is the outer edge // of the area, subsequent ones are "holes" in the copper. #ifndef POLYLINE_H @@ -16,6 +16,9 @@ #include +#define USE_GPC_POLY_LIB +//#define USE_GPL_POLY_LIB + #include "defs-macros.h" #include "GenericPolygonClipperLibrary.h" @@ -37,12 +40,12 @@ public: }; class CArc { -public: +public: enum{ MAX_STEP = 50*25400 }; // max step is 20 mils enum{ MIN_STEPS = 18 }; // min step is 5 degrees int style; int xi, yi, xf, yf; - int n_steps; // number of straight-line segments in gpc_poly + int n_steps; // number of straight-line segments in gpc_poly bool bFound; }; @@ -117,22 +120,32 @@ public: void SetEndContour( int ic, bool end_contour ); void SetSideStyle( int is, int style ); - // GPC functions - int MakeGpcPoly( int icontour=0, std::vector * arc_array=NULL ); - int FreeGpcPoly(); - gpc_polygon * GetGpcPoly(){ return m_gpc_poly; }; - int NormalizeWithGpc( std::vector * pa=NULL, bool bRetainArcs=FALSE ); int RestoreArcs( std::vector * arc_array, std::vector * pa=NULL ); CPolyLine * MakePolylineForPad( int type, int x, int y, int w, int l, int r, int angle ); - void AddContourForPadClearance( int type, int x, int y, int w, + void AddContourForPadClearance( int type, int x, int y, int w, int l, int r, int angle, int fill_clearance, int hole_w, int hole_clearance, bool bThermal=FALSE, int spoke_w=0 ); - void ClipGpcPolygon( gpc_op op, CPolyLine * poly ); + + int MakePolygonFromAreaOutlines( int icontour, std::vector * arc_array ); + void FreePolygon(); + int NormalizeAreaOutlines( std::vector * pa=NULL, bool bRetainArcs=FALSE ); + +#ifdef USE_GPC_POLY_LIB + // GPC functions + int MakeGpcPoly( int icontour=0, std::vector * arc_array=NULL ); + void FreeGpcPoly(); + gpc_polygon * GetGpcPoly(){ return m_gpc_poly; }; + int NormalizeWithGpc( std::vector * pa=NULL, bool bRetainArcs=FALSE ); +#endif + // PHP functions +#ifdef USE_GPL_POLY_LIB int MakePhpPoly(); void FreePhpPoly(); void ClipPhpPolygon( int php_op, CPolyLine * poly ); + polygon * GetPhpPoly(){ return m_php_poly; }; +#endif private: int m_layer; // layer to draw on diff --git a/polygon/php_polygon.cpp b/polygon/php_polygon.cpp index 22e553a98a..176498ed07 100644 --- a/polygon/php_polygon.cpp +++ b/polygon/php_polygon.cpp @@ -1,4 +1,5 @@ // file php_polygon.cpp + // This is a port of a php class written by Brenor Brophy (see below) /*------------------------------------------------------------------------------ @@ -44,6 +45,7 @@ ** 1.1 09/04/2005 Added Move(), Rotate(), isPolyInside() and bRect() methods. ** Added software license language to header comments */ + //#include "stdafx.h" #include @@ -57,108 +59,119 @@ #include "php_polygon.h" const double PT = 0.99999; -//const double eps = (1.0 - PT)/10.0; -const double eps = 0.0; -polygon::polygon( vertex * first ) -{ - m_first = first; - m_cnt = 0; +//const double eps = (1.0 - PT)/10.0; +const double eps = 0.0; + +polygon::polygon( vertex* first ) +{ + m_first = first; + m_cnt = 0; } + polygon::~polygon() { - while( m_cnt > 1 ) - { - vertex * v = getFirst(); - del( v->m_nextV ); - } - if( m_first ) - { - delete m_first; - } + while( m_cnt > 1 ) + { + vertex* v = getFirst(); + del( v->m_nextV ); + } + + if( m_first ) + { + delete m_first; + } } -vertex * polygon::getFirst() -{ - return m_first; + +vertex* polygon::getFirst() +{ + return m_first; } -polygon * polygon::NextPoly() -{ - return m_first->NextPoly(); + +polygon* polygon::NextPoly() +{ + return m_first->NextPoly(); } + /* ** Add a vertex object to the polygon (vertex is added at the "end" of the list) ** Which because polygons are closed lists means it is added just before the first ** vertex. */ -void polygon::add( vertex * nv ) +void polygon::add( vertex* nv ) { - if ( m_cnt == 0 ) // If this is the first vertex in the polygon - { - m_first = nv; // Save a reference to it in the polygon - m_first->setNext(nv); // Set its pointer to point to itself - m_first->setPrev(nv); // because it is the only vertex in the list - segment * ps = m_first->Nseg(); // Get ref to the Next segment object - m_first->setPseg(ps); // and save it as Prev segment as well - } - else // At least one other vertex already exists - { - // p <-> nv <-> n - // ps ns - vertex * n = m_first; // Get a ref to the first vertex in the list - vertex * p = n->Prev(); // Get ref to previous vertex - n->setPrev(nv); // Add at end of list (just before first) - nv->setNext(n); // link the new vertex to it - nv->setPrev(p); // link to the pervious EOL vertex - p->setNext(nv); // And finally link the previous EOL vertex - // Segments - segment * ns = nv->Nseg(); // Get ref to the new next segment - segment * ps = p->Nseg(); // Get ref to the previous segment - n->setPseg(ns); // Set new previous seg for m_first - nv->setPseg(ps); // Set previous seg of the new vertex - } - m_cnt++; // Increment the count of vertices + if( m_cnt == 0 ) // If this is the first vertex in the polygon + { + m_first = nv; // Save a reference to it in the polygon + m_first->setNext( nv ); // Set its pointer to point to itself + m_first->setPrev( nv ); // because it is the only vertex in the list + segment* ps = m_first->Nseg(); // Get ref to the Next segment object + m_first->setPseg( ps ); // and save it as Prev segment as well + } + else // At least one other vertex already exists + { + // p <-> nv <-> n + // ps ns + vertex* n = m_first; // Get a ref to the first vertex in the list + vertex* p = n->Prev(); // Get ref to previous vertex + n->setPrev( nv ); // Add at end of list (just before first) + nv->setNext( n ); // link the new vertex to it + nv->setPrev( p ); // link to the pervious EOL vertex + p->setNext( nv ); // And finally link the previous EOL vertex + // Segments + segment* ns = nv->Nseg(); // Get ref to the new next segment + segment* ps = p->Nseg(); // Get ref to the previous segment + n->setPseg( ns ); // Set new previous seg for m_first + nv->setPseg( ps ); // Set previous seg of the new vertex + } + m_cnt++; // Increment the count of vertices } + /* ** Create a vertex and then add it to the polygon */ -void polygon::addv ( double x, double y, - double xc, double yc, int d ) +void polygon::addv( double x, double y, + double xc, double yc, int d ) { - vertex * nv = new vertex( x, y, xc, yc, d ); - add( nv ); + vertex* nv = new vertex( x, y, xc, yc, d ); + + add( nv ); } + /* ** Delete a vertex object from the polygon. This is not used by the main algorithm ** but instead is used to clean-up a polygon so that a second boolean operation can ** be performed. */ -vertex * polygon::del( vertex * v ) +vertex* polygon::del( vertex* v ) { - // p <-> v <-> n Will delete v and ns - // ps ns - vertex * p = v->Prev(); // Get ref to previous vertex - vertex * n = v->Next(); // Get ref to next vertex - p->setNext(n); // Link previous forward to next - n->setPrev(p); // Link next back to previous - // Segments - segment * ps = p->Nseg(); // Get ref to previous segment - segment * ns = v->Nseg(); // Get ref to next segment - n->setPseg(ps); // Link next back to previous segment - delete ns; //AMW - v->m_nSeg = NULL; // AMW - delete v; //AMW -// ns = NULL; + // p <-> v <-> n Will delete v and ns + // ps ns + vertex* p = v->Prev(); // Get ref to previous vertex + vertex* n = v->Next(); // Get ref to next vertex + + p->setNext( n ); // Link previous forward to next + n->setPrev( p ); // Link next back to previous + // Segments + segment* ps = p->Nseg(); // Get ref to previous segment + segment* ns = v->Nseg(); // Get ref to next segment + n->setPseg( ps ); // Link next back to previous segment + delete ns; //AMW + v->m_nSeg = NULL; // AMW + delete v; //AMW +// ns = NULL; // v = NULL; // Free the memory - m_cnt--; // One less vertex - return n; // Return a ref to the next valid vertex + m_cnt--; // One less vertex + return n; // Return a ref to the next valid vertex } + /* ** Reset Polygon - Deletes all intersection vertices. This is used to ** restore a polygon that has been processed by the boolean method @@ -166,33 +179,36 @@ vertex * polygon::del( vertex * v ) */ void polygon::res() { - vertex * v = getFirst(); // Get the first vertex - do - { - v = v->Next(); // Get the next vertex in the polygon - while (v->isIntersect()) // Delete all intersection vertices - v = del(v); - } - while (v->id() != m_first->id()); + vertex* v = getFirst(); // Get the first vertex + + do + { + v = v->Next(); // Get the next vertex in the polygon + while( v->isIntersect() ) // Delete all intersection vertices + v = del( v ); + } while( v->id() != m_first->id() ); } + /* ** Copy Polygon - Returns a reference to a new copy of the poly object ** including all its vertices & their segments */ -polygon * polygon::copy_poly() +polygon* polygon::copy_poly() { - polygon * n = new polygon; // Create a new instance of this class - vertex * v = getFirst(); - do - { - n->addv(v->X(),v->Y(),v->Xc(),v->Yc(), (int) v->d()); - v = v->Next(); - } - while (v->id() != m_first->id()); - return n; + polygon* n = new polygon; // Create a new instance of this class + vertex* v = getFirst(); + + do + { + n->addv( v->X(), v->Y(), v->Xc(), v->Yc(), (int) v->d() ); + v = v->Next(); + } while( v->id() != m_first->id() ); + + return n; } + /* ** Insert and Sort a vertex between a specified pair of vertices (start and end) ** @@ -202,78 +218,92 @@ polygon * polygon::copy_poly() ** points between the two vertices then the new vertex is inserted based on its ** alpha value. */ -void polygon::insertSort( vertex * nv, vertex * s, vertex * e ) +void polygon::insertSort( vertex* nv, vertex* s, vertex* e ) { - vertex * c = s; // Set current to the starting vertex - // Move current past any intersections - // whose alpha is lower but don't go past - // the end vertex - while( c->id() != e->id() && c->Alpha() < nv->Alpha() ) - c = c->Next(); - // p <-> nv <-> c - nv->setNext(c); // Link new vertex forward to curent one - vertex * p = c->Prev(); // Get a link to the previous vertex - nv->setPrev(p); // Link the new vertex back to the previous one - p->setNext(nv); // Link previous vertex forward to new vertex - c->setPrev(nv); // Link current vertex back to the new vertex - // Segments - segment * ps = p->Nseg(); - nv->setPseg(ps); - segment * ns = nv->Nseg(); - c->setPseg(ns); - m_cnt++; // Just added a new vertex + vertex* c = s; // Set current to the starting vertex + + // Move current past any intersections + // whose alpha is lower but don't go past + // the end vertex + while( c->id() != e->id() && c->Alpha() < nv->Alpha() ) + c = c->Next(); + + // p <-> nv <-> c + nv->setNext( c ); // Link new vertex forward to curent one + vertex* p = c->Prev(); // Get a link to the previous vertex + nv->setPrev( p ); // Link the new vertex back to the previous one + p->setNext( nv ); // Link previous vertex forward to new vertex + c->setPrev( nv ); // Link current vertex back to the new vertex + // Segments + segment* ps = p->Nseg(); + nv->setPseg( ps ); + segment* ns = nv->Nseg(); + c->setPseg( ns ); + m_cnt++; // Just added a new vertex } + /* ** return the next non intersecting vertex after the one specified */ -vertex * polygon::nxt( vertex * v ) +vertex* polygon::nxt( vertex* v ) { - vertex * c = v; // Initialize current vertex - while (c && c->isIntersect()) // Move until a non-intersection - c = c->Next(); // vertex if found - return c; // return that vertex + vertex* c = v; // Initialize current vertex + + while( c && c->isIntersect() ) // Move until a non-intersection + c = c->Next(); // vertex if found + + return c; // return that vertex } + /* ** Check if any unchecked intersections remain in the polygon. The boolean ** method is complete when all intersections have been checked. */ BOOL polygon::unckd_remain() { - BOOL remain = FALSE; - vertex * v = m_first; - do - { - if (v->isIntersect() && !v->isChecked()) - remain = TRUE; // Set if an unchecked intersection is found - v = v->Next(); - } - while (v->id() != m_first->id()); - return remain; + BOOL remain = FALSE; + vertex* v = m_first; + + do + { + if( v->isIntersect() && !v->isChecked() ) + remain = TRUE; // Set if an unchecked intersection is found + v = v->Next(); + } while( v->id() != m_first->id() ); + + return remain; } + /* ** Return a ref to the first unchecked intersection point in the polygon. ** If none are found then just the first vertex is returned. */ -vertex * polygon::first_unckd_intersect() +vertex* polygon::first_unckd_intersect() { - vertex * v = m_first; - do // Do-While - { // Not yet reached end of the polygon - v = v->Next(); // AND the vertex if NOT an intersection - } // OR it IS an intersection, but has been checked already - while(v->id() != m_first->id() && ( !v->isIntersect() || ( v->isIntersect() && v->isChecked() ) ) ); - return v; + vertex* v = m_first; + + do // Do-While + { // Not yet reached end of the polygon + v = v->Next(); // AND the vertex if NOT an intersection + } // OR it IS an intersection, but has been checked already + while( v->id() != m_first->id() && ( !v->isIntersect() || ( v->isIntersect() && v->isChecked() ) ) ); + + return v; } + + /* ** Return the distance between two points */ double polygon::dist( double x1, double y1, double x2, double y2 ) { - return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); + return sqrt( (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) ); } + + /* ** Calculate the angle between 2 points, where Xc,Yc is the center of a circle ** and x,y is a point on its circumference. All angles are relative to @@ -281,46 +311,50 @@ double polygon::dist( double x1, double y1, double x2, double y2 ) */ double polygon::angle( double xc, double yc, double x1, double y1 ) { - double d = dist(xc, yc, x1, y1); // calc distance between two points - double a1; - if ( asin( (y1-yc)/d ) >= 0 ) - a1 = acos( (x1-xc)/d ); - else - a1 = 2*PI - acos( (x1-xc)/d ); - return a1; + double d = dist( xc, yc, x1, y1 ); // calc distance between two points + double a1; + + if( asin( (y1 - yc) / d ) >= 0 ) + a1 = acos( (x1 - xc) / d ); + else + a1 = 2 * PI - acos( (x1 - xc) / d ); + return a1; } + /* ** Return Alpha value for an Arc ** ** X1/Y1 & X2/Y2 are the end points of the arc, Xc/Yc is the center & Xi/Yi ** the intersection point on the arc. d is the direction of the arc */ -double polygon::aAlpha( double x1, double y1, double x2, double y2, - double xc, double yc, double xi, double yi, double d ) +double polygon::aAlpha( double x1, double y1, double x2, double y2, + double xc, double yc, double xi, double yi, double d ) { - double sa = angle(xc, yc, x1, y1); // Start Angle - double ea = angle(xc, yc, x2, y2); // End Angle - double ia = angle(xc, yc, xi, yi); // Intersection Angle - double arc, aint; - if (d == 1) // Anti-Clockwise - { - arc = ea - sa; - aint = ia - sa; - } - else // Clockwise - { - arc = sa - ea; - aint = sa - ia; - } - if (arc < 0) - arc += 2*PI; - if (aint < 0) - aint += 2*PI; - double a = aint/arc; - return a; + double sa = angle( xc, yc, x1, y1 ); // Start Angle + double ea = angle( xc, yc, x2, y2 ); // End Angle + double ia = angle( xc, yc, xi, yi ); // Intersection Angle + double arc, aint; + + if( d == 1 ) // Anti-Clockwise + { + arc = ea - sa; + aint = ia - sa; + } + else // Clockwise + { + arc = sa - ea; + aint = sa - ia; + } + if( arc < 0 ) + arc += 2 * PI; + if( aint < 0 ) + aint += 2 * PI; + double a = aint / arc; + return a; } + /* ** This function handles the degenerate case where a vertex of one ** polygon lies directly on an edge of the other. This case can @@ -328,35 +362,37 @@ double polygon::aAlpha( double x1, double y1, double x2, double y2, ** line exactly intersects with a vertex. The function works ** by shortening the line by a tiny amount. */ -void polygon::perturb( vertex * p1, vertex * p2, vertex * q1, vertex * q2, - double aP, double aQ ) +void polygon::perturb( vertex* p1, vertex* p2, vertex* q1, vertex* q2, + double aP, double aQ ) { // if (aP == 0) // Move vertex p1 closer to p2 - if( abs(aP) <= eps ) // Move vertex p1 closer to p2 - { - p1->setX(p1->X() + (1-PT) * (p2->X() - p1->X())); - p1->setY(p1->Y() + (1-PT) * (p2->Y() - p1->Y())); - } + if( abs( aP ) <= eps ) // Move vertex p1 closer to p2 + { + p1->setX( p1->X() + (1 - PT) * ( p2->X() - p1->X() ) ); + p1->setY( p1->Y() + (1 - PT) * ( p2->Y() - p1->Y() ) ); + } // else if (aP == 1) // Move vertex p2 closer to p1 - else if( abs(1-aP) <= eps ) // Move vertex p2 closer to p1 - { - p2->setX(p1->X() + PT * (p2->X() - p1->X())); - p2->setY(p1->Y() + PT * (p2->Y() - p1->Y())); - } + else if( abs( 1 - aP ) <= eps ) // Move vertex p2 closer to p1 + { + p2->setX( p1->X() + PT * ( p2->X() - p1->X() ) ); + p2->setY( p1->Y() + PT * ( p2->Y() - p1->Y() ) ); + } + //** else if (aQ == 0) // Move vertex q1 closer to q2 - if( abs(aQ) <= eps ) // Move vertex q1 closer to q2 - { - q1->setX(q1->X() + (1-PT) * (q2->X() - q1->X())); - q1->setY(q1->Y() + (1-PT) * (q2->Y() - q1->Y())); - } + if( abs( aQ ) <= eps ) // Move vertex q1 closer to q2 + { + q1->setX( q1->X() + (1 - PT) * ( q2->X() - q1->X() ) ); + q1->setY( q1->Y() + (1 - PT) * ( q2->Y() - q1->Y() ) ); + } //** else if (aQ == 1) // Move vertex q2 closer to q1 - else if( abs(1-aQ) <= eps ) // Move vertex q2 closer to q1 - { - q2->setX(q1->X() + PT * (q2->X() - q1->X())); - q2->setY(q1->Y() + PT * (q2->Y() - q1->Y())); - } + else if( abs( 1 - aQ ) <= eps ) // Move vertex q2 closer to q1 + { + q2->setX( q1->X() + PT * ( q2->X() - q1->X() ) ); + q2->setY( q1->Y() + PT * ( q2->Y() - q1->Y() ) ); + } } + /* ** Determine the intersection between two pairs of vertices p1/p2, q1/q2 ** @@ -372,270 +408,290 @@ void polygon::perturb( vertex * p1, vertex * p2, vertex * q1, vertex * q2, ** The arrays ix[], iy[], alphaP[] & alphaQ[] return the intersection points ** and their associated alpha values. */ -BOOL polygon::ints( vertex * p1, vertex * p2, vertex * q1, vertex * q2, - int * n, double ix[], double iy[], double alphaP[], double alphaQ[] ) +BOOL polygon::ints( vertex* p1, vertex* p2, vertex* q1, vertex* q2, + int* n, double ix[], double iy[], double alphaP[], double alphaQ[] ) { - BOOL found = FALSE; - *n = 0; // No intersections found yet - int pt = (int) p1->d(); - int qt = (int) q1->d(); // Do we have Arcs or Lines? + BOOL found = FALSE; - if (pt == 0 && qt == 0) // Is it line/Line ? - { - /* LINE/LINE - ** Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ - */ - double x1 = p1->X(); - double y1 = p1->Y(); - double x2 = p2->X(); - double y2 = p2->Y(); - double x3 = q1->X(); - double y3 = q1->Y(); - double x4 = q2->X(); - double y4 = q2->Y(); - double d = ((y4-y3)*(x2-x1)-(x4-x3)*(y2-y1)); - if (d != 0) - { // The lines intersect at a point somewhere - double ua = ((x4-x3)*(y1-y3)-(y4-y3)*(x1-x3))/d; - double ub = ((x2-x1)*(y1-y3)-(y2-y1)*(x1-x3))/d; - TRACE( " ints: ua = %.17f, ub = %.17f\n", ua, ub ); - // The values of $ua and $ub tell us where the intersection occurred. - // A value between 0 and 1 means the intersection occurred within the - // line segment. - // A value less than 0 or greater than 1 means the intersection occurred - // outside the line segment - // A value of exactly 0 or 1 means the intersection occurred right at the - // start or end of the line segment. For our purposes we will consider this - // NOT to be an intersection and we will move the vertex a tiny distance - // away from the intersecting line. + *n = 0; // No intersections found yet + int pt = (int) p1->d(); + int qt = (int) q1->d(); // Do we have Arcs or Lines? + + if( pt == 0 && qt == 0 ) // Is it line/Line ? + { + /* LINE/LINE + ** Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/lineline2d/ + */ + double x1 = p1->X(); + double y1 = p1->Y(); + double x2 = p2->X(); + double y2 = p2->Y(); + double x3 = q1->X(); + double y3 = q1->Y(); + double x4 = q2->X(); + double y4 = q2->Y(); + double d = ( (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) ); + if( d != 0 ) + { // The lines intersect at a point somewhere + double ua = ( (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) ) / d; + double ub = ( (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) ) / d; +// TRACE( " ints: ua = %.17f, ub = %.17f\n", ua, ub ); + + // The values of $ua and $ub tell us where the intersection occurred. + // A value between 0 and 1 means the intersection occurred within the + // line segment. + // A value less than 0 or greater than 1 means the intersection occurred + // outside the line segment + // A value of exactly 0 or 1 means the intersection occurred right at the + // start or end of the line segment. For our purposes we will consider this + // NOT to be an intersection and we will move the vertex a tiny distance + // away from the intersecting line. // if( ua == 0 || ua == 1 || ub == 0 || ub == 1 ) - if( abs(ua)<=eps || abs(1.0-ua)<=eps || abs(ub)<=eps || abs(1.0-ub)<=eps ) - { - // Degenerate case - vertex touches a line - perturb(p1, p2, q1, q2, ua, ub); - //** for testing, see if we have successfully resolved the degeneracy - { - double tx1 = p1->X(); - double ty1 = p1->Y(); - double tx2 = p2->X(); - double ty2 = p2->Y(); - double tx3 = q1->X(); - double ty3 = q1->Y(); - double tx4 = q2->X(); - double ty4 = q2->Y(); - double td = ((ty4-ty3)*(tx2-tx1)-(tx4-tx3)*(ty2-ty1)); - if (td != 0) - { - // The lines intersect at a point somewhere - double tua = ((tx4-tx3)*(ty1-ty3)-(ty4-ty3)*(tx1-tx3))/td; - double tub = ((tx2-tx1)*(ty1-ty3)-(ty2-ty1)*(tx1-tx3))/td; - if( abs(tua)<=eps || abs(1.0-tua)<=eps || abs(tub)<=eps || abs(1.0-tub)<=eps ) - wxASSERT(0); - else if( (tua > 0 && tua < 1) && (tub > 0 && tub < 1) ) - wxASSERT(0); - TRACE( " perturb:\n new s = (%f,%f) to (%f,%f)\n new c = (%f,%f) to (%f,%f)\n new ua = %.17f, ub = %.17f\n", - tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4, tua, tub ); - } - } - //** end test - found = FALSE; - } - else if ((ua > 0 && ua < 1) && (ub > 0 && ub < 1)) - { - // Intersection occurs on both line segments - double x = x1 + ua*(x2-x1); - double y = y1 + ua*(y2-y1); - iy[0] = y; - ix[0] = x; - alphaP[0] = ua; - alphaQ[0] = ub; - *n = 1; - found = TRUE; - } - else - { - // The lines do not intersect - found = FALSE; - } - } - else - { - // The lines do not intersect (they are parallel) - found = FALSE; - } - } // End of find Line/Line intersection - else if (pt != 0 && qt != 0) // Is it Arc/Arc? - { - /* ARC/ARC - ** Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/2circle/ - */ - double x0 = p1->Xc(); - double y0 = p1->Yc(); // Center of first Arc - double r0 = dist(x0,y0,p1->X(),p1->Y()); // Calc the radius - double x1 = q1->Xc(); - double y1 = q1->Yc(); // Center of second Arc - double r1 = dist(x1,y1,q1->X(),q1->Y()); // Calc the radius + if( abs( ua )<=eps || abs( 1.0 - ua )<=eps || abs( ub )<=eps || abs( 1.0 - ub )<=eps ) + { + // Degenerate case - vertex touches a line + perturb( p1, p2, q1, q2, ua, ub ); - double dx = x1 - x0; // dx and dy are the vertical and horizontal - double dy = y1 - y0; // distances between the circle centers. - double d = sqrt((dy*dy) + (dx*dx)); // Distance between the centers. + //** for testing, see if we have successfully resolved the degeneracy + { + double tx1 = p1->X(); + double ty1 = p1->Y(); + double tx2 = p2->X(); + double ty2 = p2->Y(); + double tx3 = q1->X(); + double ty3 = q1->Y(); + double tx4 = q2->X(); + double ty4 = q2->Y(); + double td = ( (ty4 - ty3) * (tx2 - tx1) - (tx4 - tx3) * (ty2 - ty1) ); + if( td != 0 ) + { + // The lines intersect at a point somewhere + double tua = + ( (tx4 - tx3) * (ty1 - ty3) - (ty4 - ty3) * (tx1 - tx3) ) / td; + double tub = + ( (tx2 - tx1) * (ty1 - ty3) - (ty2 - ty1) * (tx1 - tx3) ) / td; + if( abs( tua )<=eps || abs( 1.0 - tua )<=eps || abs( tub )<=eps || + abs( 1.0 - tub )<=eps ) + wxASSERT( 0 ); + else if( (tua > 0 && tua < 1) && (tub > 0 && tub < 1) ) + wxASSERT( 0 ); + TRACE( + " perturb:\n new s = (%f,%f) to (%f,%f)\n new c = (%f,%f) to (%f,%f)\n new ua = %.17f, ub = %.17f\n", + tx1, + ty1, + tx2, + ty2, + tx3, + ty3, + tx4, + ty4, + tua, + tub ); + } + } - if(d > (r0 + r1)) // Check for solvability. - { // no solution. circles do not intersect. - found = FALSE; - } - else if(d < abs(r0 - r1) ) - { // no solution. one circle inside the other - found = FALSE; - } - else - { - /* - ** 'xy2' is the point where the line through the circle intersection - ** points crosses the line between the circle centers. - */ - double a = ((r0*r0)-(r1*r1)+(d*d))/(2.0*d); // Calc the distance from xy0 to xy2. - double x2 = x0 + (dx * a/d); // Determine the coordinates of xy2. - double y2 = y0 + (dy * a/d); - if (d == (r0 + r1)) // Arcs touch at xy2 exactly (unlikely) - { - alphaP[0] = aAlpha(p1->X(), p1->Y(), p2->X(), p2->Y(), x0, y0, x2, y2, pt); - alphaQ[0] = aAlpha(q1->X(), q1->Y(), q2->X(), q2->Y(), x1, y1, x2, y2, qt); - if ((alphaP[0] >0 && alphaP[0] < 1) && (alphaQ[0] >0 && alphaQ[0] < 1)) - { - ix[0] = x2; - iy[0] = y2; - *n = 1; found = TRUE; - } - } - else // Arcs intersect at two points - { - double alP[2], alQ[2]; - double h = sqrt((r0*r0) - (a*a)); // Calc the distance from xy2 to either - // of the intersection points. - double rx = -dy * (h/d); // Now determine the offsets of the - double ry = dx * (h/d); - // intersection points from xy2 - double x[2], y[2]; - x[0] = x2 + rx; x[1] = x2 - rx; // Calc the absolute intersection points. - y[0] = y2 + ry; y[1] = y2 - ry; - alP[0] = aAlpha(p1->X(), p1->Y(), p2->X(), p2->Y(), x0, y0, x[0], y[0], pt); - alQ[0] = aAlpha(q1->X(), q1->Y(), q2->X(), q2->Y(), x1, y1, x[0], y[0], qt); - alP[1] = aAlpha(p1->X(), p1->Y(), p2->X(), p2->Y(), x0, y0, x[1], y[1], pt); - alQ[1] = aAlpha(q1->X(), q1->Y(), q2->X(), q2->Y(), x1, y1, x[1], y[1], qt); - for (int i=0; i<=1; i++) - if ((alP[i] >0 && alP[i] < 1) && (alQ[i] >0 && alQ[i] < 1)) - { - ix[*n] = x[i]; - iy[*n] = y[i]; - alphaP[*n] = alP[i]; - alphaQ[*n] = alQ[i]; - *n++; - found = TRUE; - } - } - } - } // End of find Arc/Arc intersection - else // It must be Arc/Line - { - /* ARC/LINE - ** Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/sphereline/ - */ - double d, x1, x2, xc, xs, xe; - double y1, y2, yc, ys, ye; - if (pt == 0) // Segment p1,p2 is the line - { // Segment q1,q2 is the arc - x1 = p1->X(); - y1 = p1->Y(); - x2 = p2->X(); - y2 = p2->Y(); - xc = q1->Xc(); - yc = q1->Yc(); - xs = q1->X(); - ys = q1->Y(); - xe = q2->X(); - ye = q2->Y(); - d = qt; - } - else // Segment q1,q2 is the line - { // Segment p1,p2 is the arc - x1 = q1->X(); y1 = q1->Y(); - x2 = q2->X(); y2 = q2->Y(); - xc = p1->Xc(); yc = p1->Yc(); - xs = p1->X(); ys = p1->Y(); - xe = p2->X(); ye = p2->Y(); - d = pt; - } - double r = dist(xc,yc,xs,ys); - double a = pow((x2 - x1),2)+pow((y2 - y1),2); - double b = 2* ( (x2 - x1)*(x1 - xc) - + (y2 - y1)*(y1 - yc) ); - double c = pow(xc,2) + pow(yc,2) + - pow(x1,2) + pow(y1,2) - - 2* ( xc*x1 + yc*y1) - pow(r,2); - double i = b * b - 4 * a * c; - if ( i < 0.0 ) // no intersection - { - found = FALSE; - } - else if ( i == 0.0 ) // one intersection - { - double mu = -b/(2*a); - double x = x1 + mu*(x2-x1); - double y = y1 + mu*(y2-y1); - double al = mu; // Line Alpha - double aa = this->aAlpha(xs, ys, xe, ye, xc, yc, x, y, d); // Arc Alpha - if ((al >0 && al <1)&&(aa >0 && aa <1)) - { - ix[0] = x; iy[0] = y; - *n = 1; - found = TRUE; - if (pt == 0) - { - alphaP[0] = al; alphaQ[0] = aa; - } - else - { - alphaP[0] = aa; alphaQ[0] = al; - } - } - } - else if ( i > 0.0 ) // two intersections - { - double mu[2], x[2], y[2], al[2], aa[2]; - mu[0] = (-b + sqrt( pow(b,2) - 4*a*c )) / (2*a); // first intersection - x[0] = x1 + mu[0]*(x2-x1); - y[0] = y1 + mu[0]*(y2-y1); - mu[1] = (-b - sqrt(pow(b,2) - 4*a*c )) / (2*a); // second intersection - x[1] = x1 + mu[1]*(x2-x1); - y[1] = y1 + mu[1]*(y2-y1); - al[0] = mu[0]; - aa[0] = aAlpha(xs, ys, xe, ye, xc, yc, x[0], y[0], d); - al[1] = mu[1]; - aa[1] = aAlpha(xs, ys, xe, ye, xc, yc, x[1], y[1], d); - for (int i=0; i<=1; i++) - if ((al[i] >0 && al[i] < 1) && (aa[i] >0 && aa[i] < 1)) - { - ix[*n] = x[i]; - iy[*n] = y[i]; - if (pt == 0) - { - alphaP[*n] = al[i]; - alphaQ[*n] = aa[i]; - } - else - { - alphaP[*n] = aa[i]; - alphaQ[*n] = al[i]; - } - *n++; - found = TRUE; - } - } - } // End of find Arc/Line intersection - return found; + //** end test + found = FALSE; + } + else if( (ua > 0 && ua < 1) && (ub > 0 && ub < 1) ) + { + // Intersection occurs on both line segments + double x = x1 + ua * (x2 - x1); + double y = y1 + ua * (y2 - y1); + iy[0] = y; + ix[0] = x; + alphaP[0] = ua; + alphaQ[0] = ub; + *n = 1; + found = TRUE; + } + else + { + // The lines do not intersect + found = FALSE; + } + } + else + { + // The lines do not intersect (they are parallel) + found = FALSE; + } + } // End of find Line/Line intersection + else if( pt != 0 && qt != 0 ) // Is it Arc/Arc? + { + /* ARC/ARC + ** Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/2circle/ + */ + double x0 = p1->Xc(); + double y0 = p1->Yc(); // Center of first Arc + double r0 = dist( x0, y0, p1->X(), p1->Y() ); // Calc the radius + double x1 = q1->Xc(); + double y1 = q1->Yc(); // Center of second Arc + double r1 = dist( x1, y1, q1->X(), q1->Y() ); // Calc the radius + + double dx = x1 - x0; // dx and dy are the vertical and horizontal + double dy = y1 - y0; // distances between the circle centers. + double d = sqrt( (dy * dy) + (dx * dx) ); // Distance between the centers. + + if( d > (r0 + r1) ) // Check for solvability. + { // no solution. circles do not intersect. + found = FALSE; + } + else if( d < abs( r0 - r1 ) ) + { // no solution. one circle inside the other + found = FALSE; + } + else + { + /* + ** 'xy2' is the point where the line through the circle intersection + ** points crosses the line between the circle centers. + */ + double a = ( (r0 * r0) - (r1 * r1) + (d * d) ) / (2.0 * d); // Calc the distance from xy0 to xy2. + double x2 = x0 + (dx * a / d); // Determine the coordinates of xy2. + double y2 = y0 + (dy * a / d); + if( d == (r0 + r1) ) // Arcs touch at xy2 exactly (unlikely) + { + alphaP[0] = aAlpha( p1->X(), p1->Y(), p2->X(), p2->Y(), x0, y0, x2, y2, pt ); + alphaQ[0] = aAlpha( q1->X(), q1->Y(), q2->X(), q2->Y(), x1, y1, x2, y2, qt ); + if( (alphaP[0] >0 && alphaP[0] < 1) && (alphaQ[0] >0 && alphaQ[0] < 1) ) + { + ix[0] = x2; + iy[0] = y2; + *n = 1; found = TRUE; + } + } + else // Arcs intersect at two points + { + double alP[2], alQ[2]; + double h = sqrt( (r0 * r0) - (a * a) ); // Calc the distance from xy2 to either + // of the intersection points. + double rx = -dy * (h / d); // Now determine the offsets of the + double ry = dx * (h / d); + + // intersection points from xy2 + double x[2], y[2]; + x[0] = x2 + rx; x[1] = x2 - rx; // Calc the absolute intersection points. + y[0] = y2 + ry; y[1] = y2 - ry; + alP[0] = aAlpha( p1->X(), p1->Y(), p2->X(), p2->Y(), x0, y0, x[0], y[0], pt ); + alQ[0] = aAlpha( q1->X(), q1->Y(), q2->X(), q2->Y(), x1, y1, x[0], y[0], qt ); + alP[1] = aAlpha( p1->X(), p1->Y(), p2->X(), p2->Y(), x0, y0, x[1], y[1], pt ); + alQ[1] = aAlpha( q1->X(), q1->Y(), q2->X(), q2->Y(), x1, y1, x[1], y[1], qt ); + for( int i = 0; i<=1; i++ ) + if( (alP[i] >0 && alP[i] < 1) && (alQ[i] >0 && alQ[i] < 1) ) + { + ix[*n] = x[i]; + iy[*n] = y[i]; + alphaP[*n] = alP[i]; + alphaQ[*n] = alQ[i]; + *n++; + found = TRUE; + } + } + } + } // End of find Arc/Arc intersection + else // It must be Arc/Line + { + /* ARC/LINE + ** Algorithm from: http://astronomy.swin.edu.au/~pbourke/geometry/sphereline/ + */ + double d, x1, x2, xc, xs, xe; + double y1, y2, yc, ys, ye; + if( pt == 0 ) // Segment p1,p2 is the line + { // Segment q1,q2 is the arc + x1 = p1->X(); + y1 = p1->Y(); + x2 = p2->X(); + y2 = p2->Y(); + xc = q1->Xc(); + yc = q1->Yc(); + xs = q1->X(); + ys = q1->Y(); + xe = q2->X(); + ye = q2->Y(); + d = qt; + } + else // Segment q1,q2 is the line + { + // Segment p1,p2 is the arc + x1 = q1->X(); y1 = q1->Y(); + x2 = q2->X(); y2 = q2->Y(); + xc = p1->Xc(); yc = p1->Yc(); + xs = p1->X(); ys = p1->Y(); + xe = p2->X(); ye = p2->Y(); + d = pt; + } + double r = dist( xc, yc, xs, ys ); + double a = pow( (x2 - x1), 2 ) + pow( (y2 - y1), 2 ); + double b = 2 * ( (x2 - x1) * (x1 - xc) + + (y2 - y1) * (y1 - yc) ); + double c = pow( xc, 2 ) + pow( yc, 2 ) + + pow( x1, 2 ) + pow( y1, 2 ) - + 2 * ( xc * x1 + yc * y1) - pow( r, 2 ); + double i = b * b - 4 * a * c; + if( i < 0.0 ) // no intersection + { + found = FALSE; + } + else if( i == 0.0 ) // one intersection + { + double mu = -b / (2 * a); + double x = x1 + mu * (x2 - x1); + double y = y1 + mu * (y2 - y1); + double al = mu; // Line Alpha + double aa = this->aAlpha( xs, ys, xe, ye, xc, yc, x, y, d ); // Arc Alpha + if( (al >0 && al <1)&&(aa >0 && aa <1) ) + { + ix[0] = x; iy[0] = y; + *n = 1; + found = TRUE; + if( pt == 0 ) + { + alphaP[0] = al; alphaQ[0] = aa; + } + else + { + alphaP[0] = aa; alphaQ[0] = al; + } + } + } + else if( i > 0.0 ) // two intersections + { + double mu[2], x[2], y[2], al[2], aa[2]; + mu[0] = ( -b + sqrt( pow( b, 2 ) - 4 * a * c ) ) / (2 * a); // first intersection + x[0] = x1 + mu[0] * (x2 - x1); + y[0] = y1 + mu[0] * (y2 - y1); + mu[1] = ( -b - sqrt( pow( b, 2 ) - 4 * a * c ) ) / (2 * a); // second intersection + x[1] = x1 + mu[1] * (x2 - x1); + y[1] = y1 + mu[1] * (y2 - y1); + al[0] = mu[0]; + aa[0] = aAlpha( xs, ys, xe, ye, xc, yc, x[0], y[0], d ); + al[1] = mu[1]; + aa[1] = aAlpha( xs, ys, xe, ye, xc, yc, x[1], y[1], d ); + for( int i = 0; i<=1; i++ ) + if( (al[i] >0 && al[i] < 1) && (aa[i] >0 && aa[i] < 1) ) + { + ix[*n] = x[i]; + iy[*n] = y[i]; + if( pt == 0 ) + { + alphaP[*n] = al[i]; + alphaQ[*n] = aa[i]; + } + else + { + alphaP[*n] = aa[i]; + alphaQ[*n] = al[i]; + } + *n++; + found = TRUE; + } + } + } // End of find Arc/Line intersection + return found; } // end of intersect function + /* ** Test if a vertex lies inside the polygon ** @@ -649,56 +705,60 @@ BOOL polygon::ints( vertex * p1, vertex * p2, vertex * q1, vertex * q2, ** ** Allan Wright 4/16/2006: I guess I have no life: I had to increase it to -1000000000 */ -BOOL polygon::isInside( vertex * v ) +BOOL polygon::isInside( vertex* v ) { - //** modified for testing - if( v->isIntersect() ) - wxASSERT(0); - int winding_number = 0; - int winding_number2 = 0; - int winding_number3 = 0; - int winding_number4 = 0; + //** modified for testing + if( v->isIntersect() ) + wxASSERT( 0 ); + int winding_number = 0; + int winding_number2 = 0; + int winding_number3 = 0; + int winding_number4 = 0; + //** vertex * point_at_infinity = new vertex(-10000000,v->Y()); // Create point at infinity + /* vertex * point_at_infinity = new vertex(-1000000000,-50000000); // Create point at infinity - vertex * point_at_infinity2 = new vertex(1000000000,+50000000); // Create point at infinity - vertex * point_at_infinity3 = new vertex(500000000,1000000000); // Create point at infinity - vertex * point_at_infinity4 = new vertex(-500000000,1000000000); // Create point at infinity -*/ - vertex point_at_infinity(-1000000000,-50000000); // Create point at infinity - vertex point_at_infinity2(1000000000,+50000000); // Create point at infinity - vertex point_at_infinity3(500000000,1000000000); // Create point at infinity - vertex point_at_infinity4(-500000000,1000000000); // Create point at infinity - vertex * q = m_first; // End vertex of a line segment in polygon - do - { - if (!q->isIntersect()) - { - int n; - double x[2], y[2], aP[2], aQ[2]; - if( ints( &point_at_infinity, v, q, nxt(q->Next()), &n, x, y, aP, aQ ) ) - winding_number += n; // Add number of intersections found - if( ints( &point_at_infinity2, v, q, nxt(q->Next()), &n, x, y, aP, aQ ) ) - winding_number2 += n; // Add number of intersections found - if( ints( &point_at_infinity3, v, q, nxt(q->Next()), &n, x, y, aP, aQ ) ) - winding_number3 += n; // Add number of intersections found - if( ints( &point_at_infinity4, v, q, nxt(q->Next()), &n, x, y, aP, aQ ) ) - winding_number4 += n; // Add number of intersections found - } - q = q->Next(); - } - while( q->id() != m_first->id() ); -// delete point_at_infinity; + * vertex * point_at_infinity2 = new vertex(1000000000,+50000000); // Create point at infinity + * vertex * point_at_infinity3 = new vertex(500000000,1000000000); // Create point at infinity + * vertex * point_at_infinity4 = new vertex(-500000000,1000000000); // Create point at infinity + */ + vertex point_at_infinity( -1000000000, -50000000 ); // Create point at infinity + vertex point_at_infinity2( 1000000000, +50000000 ); // Create point at infinity + vertex point_at_infinity3( 500000000, 1000000000 ); // Create point at infinity + vertex point_at_infinity4( -500000000, 1000000000 ); // Create point at infinity + vertex* q = m_first; // End vertex of a line segment in polygon + + do + { + if( !q->isIntersect() ) + { + int n; + double x[2], y[2], aP[2], aQ[2]; + if( ints( &point_at_infinity, v, q, nxt( q->Next() ), &n, x, y, aP, aQ ) ) + winding_number += n; // Add number of intersections found + if( ints( &point_at_infinity2, v, q, nxt( q->Next() ), &n, x, y, aP, aQ ) ) + winding_number2 += n; // Add number of intersections found + if( ints( &point_at_infinity3, v, q, nxt( q->Next() ), &n, x, y, aP, aQ ) ) + winding_number3 += n; // Add number of intersections found + if( ints( &point_at_infinity4, v, q, nxt( q->Next() ), &n, x, y, aP, aQ ) ) + winding_number4 += n; // Add number of intersections found + } + q = q->Next(); + } while( q->id() != m_first->id() ); + +// delete point_at_infinity; // delete point_at_infinity2; - if( winding_number%2 != winding_number2%2 - || winding_number3%2 != winding_number4%2 - || winding_number%2 != winding_number3%2 ) - wxASSERT(0); - if( winding_number%2 == 0 ) // Check even or odd - return FALSE; // even == outside - else - return TRUE; // odd == inside + if( winding_number % 2 != winding_number2 % 2 + || winding_number3 % 2 != winding_number4 % 2 + || winding_number % 2 != winding_number3 % 2 ) + wxASSERT( 0 ); + if( winding_number % 2 == 0 ) // Check even or odd + return FALSE; // even == outside + else + return TRUE; // odd == inside } + /* ** Execute a Boolean operation on a polygon ** @@ -714,236 +774,284 @@ BOOL polygon::isInside( vertex * v ) ** ** A is the object and B is the polygon passed to the method. */ -polygon * polygon::boolean( polygon * polyB, int oper ) +polygon* polygon::boolean( polygon* polyB, int oper ) { - polygon * last = NULL; + polygon* last = NULL; - vertex * s = m_first; // First vertex of the subject polygon - vertex * c = polyB->getFirst(); // First vertex of the "clip" polygon - /* - ** Phase 1 of the algoritm is to find all intersection points between the two - ** polygons. A new vertex is created for each intersection and it is added to - ** the linked lists for both polygons. The "neighbor" reference in each vertex - ** stores the link between the same intersection point in each polygon. - */ - TRACE( "boolean...phase 1\n" ); - do - { - TRACE( "s=(%f,%f) to (%f,%f) I=%d\n", - s->m_x, s->m_y, s->m_nextV->m_x, s->m_nextV->m_y, s->m_intersect ); - if (!s->isIntersect()) - { - do - { - TRACE( " c=(%f,%f) to (%f,%f) I=%d\n", - c->m_x, c->m_y, c->m_nextV->m_x, c->m_nextV->m_y, c->m_intersect ); - if (!c->isIntersect()) - { - int n; - double ix[2], iy[2], alphaS[2], alphaC[2]; - BOOL bInt = ints(s, nxt(s->Next()),c, polyB->nxt(c->Next()), &n, ix, iy, alphaS, alphaC); - if( bInt ) - { - TRACE( " int at (%f,%f) aS = %.17f, aC = %.17f\n", ix[0], iy[0], alphaS[0], alphaC[0] ); - for (int i=0; iXc(), s->Yc(), s->d(), NULL, NULL, NULL, TRUE, NULL, alphaS[i], FALSE, FALSE); - vertex * ic = new vertex(ix[i], iy[i], c->Xc(), c->Yc(), c->d(), NULL, NULL, NULL, TRUE, NULL, alphaC[i], FALSE, FALSE); - is->setNeighbor(ic); - ic->setNeighbor(is); - insertSort(is, s, this->nxt(s->Next())); - polyB->insertSort(ic, c, polyB->nxt(c->Next())); - } - } - } // end if c is not an intersect point - c = c->Next(); - } - while (c->id() != polyB->m_first->id()); - } // end if s not an intersect point - s = s->Next(); - } - while(s->id() != m_first->id()); + vertex* s = m_first; // First vertex of the subject polygon + vertex* c = polyB->getFirst(); // First vertex of the "clip" polygon - //** for testing...check number of intersections in each poly - TRACE( "boolean...phase 1 testing\n" ); - int n_ints = 0; - s = m_first; - do - { - if( s->isIntersect() ) - n_ints++; - s = s->Next(); - } while( s->id() != m_first->id() ); - int n_polyB_ints = 0; - s = polyB->m_first; - do - { - if( s->isIntersect() ) - n_polyB_ints++; - s = s->Next(); - } while( s->id() != polyB->m_first->id() ); - if( n_ints != n_polyB_ints ) - wxASSERT(0); - if( n_ints%2 != 0 ) - wxASSERT(0); - //** end test - - /* - ** Phase 2 of the algorithm is to identify every intersection point as an - ** entry or exit point to the other polygon. This will set the entry bits - ** in each vertex object. - ** - ** What is really stored in the entry record for each intersection is the - ** direction the algorithm should take when it arrives at that entry point. - ** Depending in the operation requested (A&B, A|B, A/B, B/A) the direction is - ** set as follows for entry points (f=foreward, b=Back), exit points are always set - ** to the opposite: - ** Enter Exit - ** A B A B - ** A|B b b f f - ** A&B f f b b - ** A\B b f f b - ** B\A f b b f - ** - ** f = TRUE, b = FALSE when stored in the entry record - */ - BOOL A, B; + /* + ** Phase 1 of the algoritm is to find all intersection points between the two + ** polygons. A new vertex is created for each intersection and it is added to + ** the linked lists for both polygons. The "neighbor" reference in each vertex + ** stores the link between the same intersection point in each polygon. + */ + TRACE( "boolean...phase 1\n" ); - switch (oper) - { - case A_OR_B: A = FALSE; B = FALSE; break; - case A_AND_B: A = TRUE; B = TRUE; break; - case A_MINUS_B: A = FALSE; B = TRUE; break; - case B_MINUS_A: A = TRUE; B = FALSE; break; - default: A = TRUE; B = TRUE; break; - } - s = m_first; - //** testing - if( s->isIntersect() ) - wxASSERT(0); - //** end test - BOOL entry; - if (polyB->isInside(s)) // if we are already inside - entry = !A; // next intersection must be an exit - else // otherwise - entry = A; // next intersection must be an entry - do - { - if (s->isIntersect()) - { - s->setEntry(entry); - entry = !entry; - } - s = s->Next(); - } - while (s->id() != m_first->id()); - /* - ** Repeat for other polygon - */ - c = polyB->m_first; - if (this->isInside(c)) // if we are already inside - entry = !B; // next intersection must be an exit - else // otherwise - entry = B; // next intersection must be an entry - do - { - if (c->isIntersect()) - { - c->setEntry(entry); - entry = !entry; - } - c = c->Next(); - } - while (c->id() != polyB->m_first->id()); - /* - ** Phase 3 of the algorithm is to scan the linked lists of the - ** two input polygons an construct a linked list of result - ** polygons. We start at the first intersection then depending - ** on whether it is an entry or exit point we continue building - ** our result polygon by following the source or clip polygon - ** either forwards or backwards. - */ - while (this->unckd_remain()) // Loop while unchecked intersections remain - { - vertex * v = first_unckd_intersect(); // Get the first unchecked intersect point - polygon * r = new polygon; // Create a new instance of that class - do - { - v->setChecked(); // Set checked flag true for this intersection - if (v->isEntry()) - { - do - { - v = v->Next(); - vertex * nv = new vertex(v->X(),v->Y(),v->Xc(),v->Yc(),v->d()); - r->add(nv); - } - while (!v->isIntersect()); - } - else - { - do - { - v = v->Prev(); - vertex * nv = new vertex(v->X(),v->Y(),v->Xc(FALSE),v->Yc(FALSE),v->d(FALSE)); - r->add(nv); - } - while (!v->isIntersect()); - } - v = v->Neighbor(); - } - while (!v->isChecked()); // until polygon closed - if (last) // Check in case first time thru the loop - r->m_first->setNextPoly(last); // Save ref to the last poly in the first vertex - // of this poly - last = r; // Save this polygon - } // end of while there is another intersection to check - /* - ** Clean up the input polygons by deleting the intersection points - */ - res(); - polyB->res(); - /* - ** It is possible that no intersection between the polygons was found and - ** there is no result to return. In this case we make function fail - ** gracefully as follows (depending on the requested operation): - ** - ** A|B : Return this with polyB in m_first->nextPoly - ** A&B : Return this - ** A\B : Return this - ** B\A : return polyB - */ - polygon * p; - if (!last) - { - switch (oper) - { - case A_OR_B: - last = copy_poly(); - p = polyB->copy_poly(); - last->m_first->setNextPoly(p); - break; - case A_AND_B: - last = copy_poly(); - break; - case A_MINUS_B: - last = copy_poly(); - break; - case B_MINUS_A: - last = polyB->copy_poly(); - break; - default: - last = copy_poly(); - break; - } - } - else if (m_first->m_nextPoly) - { - last->m_first->m_nextPoly = m_first->NextPoly(); - } - return last; + do + { + TRACE( "s=(%f,%f) to (%f,%f) I=%d\n", + s->m_x, s->m_y, s->m_nextV->m_x, s->m_nextV->m_y, s->m_intersect ); + if( !s->isIntersect() ) + { + do + { + TRACE( " c=(%f,%f) to (%f,%f) I=%d\n", + c->m_x, c->m_y, c->m_nextV->m_x, c->m_nextV->m_y, c->m_intersect ); + if( !c->isIntersect() ) + { + int n; + double ix[2], iy[2], alphaS[2], alphaC[2]; + BOOL bInt = ints( s, nxt( s->Next() ), c, polyB->nxt( + c->Next() ), &n, ix, iy, alphaS, alphaC ); + if( bInt ) + { + TRACE( " int at (%f,%f) aS = %.17f, aC = %.17f\n", + ix[0], + iy[0], + alphaS[0], + alphaC[0] ); + for( int i = 0; iXc(), s->Yc(), + s->d(), NULL, NULL, NULL, TRUE, NULL, alphaS[i], FALSE, FALSE ); + vertex* ic = new vertex( ix[i], iy[i], c->Xc(), c->Yc(), + c->d(), NULL, NULL, NULL, TRUE, NULL, alphaC[i], FALSE, FALSE ); + is->setNeighbor( ic ); + ic->setNeighbor( is ); + insertSort( is, s, this->nxt( s->Next() ) ); + polyB->insertSort( ic, c, polyB->nxt( c->Next() ) ); + } + } + } // end if c is not an intersect point + c = c->Next(); + } while( c->id() != polyB->m_first->id() ); + } // end if s not an intersect point + s = s->Next(); + } while( s->id() != m_first->id() ); + + //** for testing...check number of intersections in each poly + TRACE( "boolean...phase 1 testing\n" ); + int n_ints = 0; + s = m_first; + + do + { + if( s->isIntersect() ) + n_ints++; + s = s->Next(); + } while( s->id() != m_first->id() ); + + int n_polyB_ints = 0; + s = polyB->m_first; + + do + { + if( s->isIntersect() ) + n_polyB_ints++; + s = s->Next(); + } while( s->id() != polyB->m_first->id() ); + + if( n_ints != n_polyB_ints ) + wxASSERT( 0 ); + if( n_ints % 2 != 0 ) + wxASSERT( 0 ); + + //** end test + + /* + ** Phase 2 of the algorithm is to identify every intersection point as an + ** entry or exit point to the other polygon. This will set the entry bits + ** in each vertex object. + ** + ** What is really stored in the entry record for each intersection is the + ** direction the algorithm should take when it arrives at that entry point. + ** Depending in the operation requested (A&B, A|B, A/B, B/A) the direction is + ** set as follows for entry points (f=foreward, b=Back), exit points are always set + ** to the opposite: + ** Enter Exit + ** A B A B + ** A|B b b f f + ** A&B f f b b + ** A\B b f f b + ** B\A f b b f + ** + ** f = TRUE, b = FALSE when stored in the entry record + */ + TRACE( "boolean...phase 2\n" ); + + BOOL A, B; + + switch( oper ) + { + case A_OR_B: + A = FALSE; B = FALSE; break; + + case A_AND_B: + A = TRUE; B = TRUE; break; + + case A_MINUS_B: + A = FALSE; B = TRUE; break; + + case B_MINUS_A: + A = TRUE; B = FALSE; break; + + default: + A = TRUE; B = TRUE; break; + } + + s = m_first; + + //** testing + if( s->isIntersect() ) + wxASSERT( 0 ); + + //** end test + BOOL entry; + if( polyB->isInside( s ) ) // if we are already inside + entry = !A; // next intersection must be an exit + else // otherwise + entry = A; // next intersection must be an entry + + do + { + if( s->isIntersect() ) + { + s->setEntry( entry ); + entry = !entry; + } + s = s->Next(); + } while( s->id() != m_first->id() ); + + /* + ** Repeat for other polygon + */ + c = polyB->m_first; + if( this->isInside( c ) ) // if we are already inside + entry = !B; // next intersection must be an exit + else // otherwise + entry = B; // next intersection must be an entry + + do + { + if( c->isIntersect() ) + { + c->setEntry( entry ); + entry = !entry; + } + c = c->Next(); + } while( c->id() != polyB->m_first->id() ); + + /* + ** Phase 3 of the algorithm is to scan the linked lists of the + ** two input polygons an construct a linked list of result + ** polygons. We start at the first intersection then depending + ** on whether it is an entry or exit point we continue building + ** our result polygon by following the source or clip polygon + ** either forwards or backwards. + */ + TRACE( "boolean...phase 3\n" ); + while( this->unckd_remain() ) // Loop while unchecked intersections remain + { + vertex* v = first_unckd_intersect(); // Get the first unchecked intersect point + polygon* r = new polygon; // Create a new instance of that class + + do + { + v->setChecked(); // Set checked flag true for this intersection + if( v->isEntry() ) + { + do + { + v = v->Next(); + vertex* nv = new vertex( v->X(), v->Y(), v->Xc(), v->Yc(), v->d() ); + r->add( nv ); + } while( !v->isIntersect() ); + } + else + { + do + { + v = v->Prev(); + vertex* nv = + new vertex( v->X(), v->Y(), v->Xc( FALSE ), v->Yc( FALSE ), v->d( FALSE ) ); + r->add( nv ); + } while( !v->isIntersect() ); + } + v = v->Neighbor(); + } while( !v->isChecked() ); // until polygon closed + + if( last ) // Check in case first time thru the loop + r->m_first->setNextPoly( last ); // Save ref to the last poly in the first vertex + // of this poly + last = r; // Save this polygon + } // end of while there is another intersection to check + + /* + ** Clean up the input polygons by deleting the intersection points + */ + res(); + polyB->res(); + + /* + ** It is possible that no intersection between the polygons was found and + ** there is no result to return. In this case we make function fail + ** gracefully as follows (depending on the requested operation): + ** + ** A|B : Return this with polyB in m_first->nextPoly + ** A&B : Return this + ** A\B : Return this + ** B\A : return polyB + */ + polygon* p; + if( !last ) + { + TRACE( "boolean...end with no intersection\n" ); + switch( oper ) + { + case A_OR_B: + last = copy_poly(); + p = polyB->copy_poly(); + last->m_first->setNextPoly( p ); + break; + + case A_AND_B: + last = copy_poly(); + break; + + case A_MINUS_B: + last = copy_poly(); + break; + + case B_MINUS_A: + last = polyB->copy_poly(); + break; + + default: + last = copy_poly(); + break; + } + } + else if( m_first->m_nextPoly ) + { + TRACE( "boolean...end with nextPoly\n" ); + last->m_first->m_nextPoly = m_first->NextPoly(); + } + + vertex * curr_vertex = last->getFirst(); + for( int ii = 0; ii < last->m_cnt; ii++ ) + { + int x = (int) curr_vertex->X(); + int y = (int) curr_vertex->Y(); + TRACE( "point %d @ %.4f %.4f\n", ii, (float)x/10000, (float)y/10000 ); + curr_vertex = curr_vertex->Next(); + } + + return last; } // end of boolean function + /* ** Test if a polygon lies entirly inside this polygon ** @@ -955,155 +1063,164 @@ polygon * polygon::boolean( polygon * polyB, int oper ) */ #if 0 -function polygon::isPolyInside (p) +function polygon::isPolyInside( p ) { - inside = TRUE; - c = p->getFirst(); // Get the first vertex in polygon p - do - { - if (!this->isInside(c)) // If vertex is NOT inside this polygon - inside = FALSE; // then set flag to false - c = c->Next(); // Get the next vertex in polygon p - } - while (c->id() != p->first->id()); - if (inside) - { - c = p->getFirst(); // Get the first vertex in polygon p - s = getFirst(); // Get the first vertex in this polygon - do - { - do - { - if (this->ints(s, s->Next(),c, c->Next(), n, x, y, aS, aC)) - inside = FALSE; - c = c->Next(); - } - while (c->id() != p->first->id()); - s = s->Next(); - } - while (s->id() != m_first->id()); - } - return inside; + inside = TRUE; + c = p->getFirst(); // Get the first vertex in polygon p + + do + { + if( !this->isInside( c ) ) // If vertex is NOT inside this polygon + inside = FALSE; // then set flag to false + c = c->Next(); // Get the next vertex in polygon p + } while( c->id() != p->first->id() ); + + if( inside ) + { + c = p->getFirst(); // Get the first vertex in polygon p + s = getFirst(); // Get the first vertex in this polygon + + do + { + do + { + if( this->ints( s, s->Next(), c, c->Next(), n, x, y, aS, aC ) ) + inside = FALSE; + c = c->Next(); + } while( c->id() != p->first->id() ); + + s = s->Next(); + } while( s->id() != m_first->id() ); + } + return inside; } // end of isPolyInside + /* ** Move Polygon ** ** Translates polygon by delta X and delta Y */ -function polygon::move (dx, dy) +function polygon::move( dx, dy ) { - v = getFirst(); - do - { - v->setX(v->X() + dx); - v->setY(v->Y() + dy); - if (v->d() != 0) - { - v->setXc(v->Xc() + dx); - v->setYc(v->Yc() + dy); - } - v = v->Next(); - } - while(v->id() != m_first->id()); + v = getFirst(); + + do + { + v->setX( v->X() + dx ); + v->setY( v->Y() + dy ); + if( v->d() != 0 ) + { + v->setXc( v->Xc() + dx ); + v->setYc( v->Yc() + dy ); + } + v = v->Next(); + } while( v->id() != m_first->id() ); } // end of move polygon + /* ** Rotate Polygon ** ** Rotates a polgon about point xr/yr by a radians */ -function polygon::rotate (xr, yr, a) +function polygon::rotate( xr, yr, a ) { - this->move(-xr,-yr); // Move the polygon so that the point of - // rotation is at the origin (0,0) - if (a < 0) // We might be passed a negitive angle - a += 2*pi(); // make it positive - v = m_first; - do - { - x=v->X(); y=v->Y(); - v->setX(x*cos(a) - y*sin(a)); // x' = xCos(a)-ySin(a) - v->setY(x*sin(a) + y*cos(a)); // y' = xSin(a)+yCos(a) - if (v->d() != 0) - { - x=v->Xc(); y=v->Yc(); - v->setXc(x*cos(a) - y*sin(a)); - v->setYc(x*sin(a) + y*cos(a)); - } - v = v->Next(); - } - while(v->id() != m_first->id()); - this->move(xr,yr); // Move the rotated polygon back + this->move( -xr, -yr ); // Move the polygon so that the point of + // rotation is at the origin (0,0) + if( a < 0 ) // We might be passed a negitive angle + a += 2 * pi(); // make it positive + v = m_first; + + do + { + x = v->X(); y = v->Y(); + v->setX( x * cos( a ) - y * sin( a ) ); // x' = xCos(a)-ySin(a) + v->setY( x * sin( a ) + y * cos( a ) ); // y' = xSin(a)+yCos(a) + if( v->d() != 0 ) + { + x = v->Xc(); y = v->Yc(); + v->setXc( x * cos( a ) - y * sin( a ) ); + v->setYc( x * sin( a ) + y * cos( a ) ); + } + v = v->Next(); + } while( v->id() != m_first->id() ); + + this->move( xr, yr ); // Move the rotated polygon back } // end of rotate polygon + /* ** Return Bounding Rectangle for a Polygon ** ** returns a polygon object that represents the bounding rectangle ** for this polygon. Arc segments are correctly handled. */ -function polygon::&bRect () +function polygon::& bRect() { - minX = INF; minY = INF; maxX = -INF; maxY = -INF; - v = m_first; - do - { - if (v->d() != 0) // Is it an arc segment - { - vn = v->Next(); // end vertex of the arc segment - v1 = new vertex(v->Xc(), -infinity); // bottom point of vertical line thru arc center - v2 = new vertex(v->Xc(), +infinity); // top point of vertical line thru arc center - if (this->ints(v, vn, v1, v2, n, x, y, aS, aC)) // Does line intersect the arc ? - { - for (i=0; iY()); - maxY = max(maxY, y[i], v->Y()); - } - } - else // There was no intersection so bounding rect is determined - { // by the start point only, not teh edge of the arc - minY = min(minY, v->Y()); - maxY = max(maxY, v->Y()); - } - v1 = NULL; v2 = NULL; // Free the memory used - h1 = new vertex(-infinity, v->Yc()); // left point of horozontal line thru arc center - h2 = new vertex(+infinity, v->Yc()); // right point of horozontal line thru arc center - if (this->ints(v, vn, h1, h2, n, x, y, aS, aC)) // Does line intersect the arc ? - { - for (i=0; iX()); - maxX = max(maxX, x[i], v->X()); - } - } - else - { - minX = min(minX, v->X()); - maxX = max(maxX, v->X()); - } - h1 = NULL; h2 = NULL; - } - else // Straight segment so just check the vertex - { - minX = min(minX, v->X()); - minY = min(minY, v->Y()); - maxX = max(maxX, v->X()); - maxY = max(maxY, v->Y()); - } - v = v->Next(); - } - while(v->id() != m_first->id()); - // - // Now create an return a polygon with the bounding rectangle - // - this_class = get_class(this); // Findout the class I'm in (might be an extension of polygon) - p = new this_class; // Create a new instance of that class - p->addv(minX,minY); - p->addv(minX,maxY); - p->addv(maxX,maxY); - p->addv(maxX,minY); - return p; + minX = INF; minY = INF; maxX = -INF; maxY = -INF; + v = m_first; + + do + { + if( v->d() != 0 ) // Is it an arc segment + { + vn = v->Next(); // end vertex of the arc segment + v1 = new vertex( v->Xc(), -infinity ); // bottom point of vertical line thru arc center + v2 = new vertex( v->Xc(), +infinity ); // top point of vertical line thru arc center + if( this->ints( v, vn, v1, v2, n, x, y, aS, aC ) ) // Does line intersect the arc ? + { + for( i = 0; iY() ); + maxY = max( maxY, y[i], v->Y() ); + } + } + else // There was no intersection so bounding rect is determined + { + // by the start point only, not teh edge of the arc + minY = min( minY, v->Y() ); + maxY = max( maxY, v->Y() ); + } + v1 = NULL; v2 = NULL; // Free the memory used + h1 = new vertex( -infinity, v->Yc() ); // left point of horozontal line thru arc center + h2 = new vertex( +infinity, v->Yc() ); // right point of horozontal line thru arc center + if( this->ints( v, vn, h1, h2, n, x, y, aS, aC ) ) // Does line intersect the arc ? + { + for( i = 0; iX() ); + maxX = max( maxX, x[i], v->X() ); + } + } + else + { + minX = min( minX, v->X() ); + maxX = max( maxX, v->X() ); + } + h1 = NULL; h2 = NULL; + } + else // Straight segment so just check the vertex + { + minX = min( minX, v->X() ); + minY = min( minY, v->Y() ); + maxX = max( maxX, v->X() ); + maxY = max( maxY, v->Y() ); + } + v = v->Next(); + } while( v->id() != m_first->id() ); + + // + // Now create an return a polygon with the bounding rectangle + // + this_class = get_class( this ); // Findout the class I'm in (might be an extension of polygon) + p = new this_class; // Create a new instance of that class + p->addv( minX, minY ); + p->addv( minX, maxY ); + p->addv( maxX, maxY ); + p->addv( maxX, minY ); + return p; } // end of bounding rectangle + + #endif diff --git a/polygon/php_polygon.h b/polygon/php_polygon.h index 798b74830c..313279c13c 100644 --- a/polygon/php_polygon.h +++ b/polygon/php_polygon.h @@ -1,4 +1,5 @@ // file php_polygon.h + // See comments in php_polygon.cpp #ifndef PHP_POLYGON_H @@ -7,55 +8,65 @@ class vertex; class segment; -#define infinity 100000000 // for places that are far far away -#define PI 3.14159265359 +#define infinity 100000000 // for places that are far far away +#define PI 3.14159265359 -enum{ A_OR_B, A_AND_B, A_MINUS_B, B_MINUS_A }; +enum { + A_OR_B, + A_AND_B, + A_MINUS_B, + B_MINUS_A +}; -class polygon -{ -public: /*------------------------------------------------------------------------------ ** This class manages a doubly linked list of vertex objects that represents ** a polygon. The class consists of basic methods to manage the list ** and methods to implement boolean operations between polygon objects. */ - vertex * m_first; // Reference to first vertex in the linked list - int m_cnt; // Tracks number of vertices in the polygon - polygon( vertex * first = NULL ); - ~polygon(); - vertex * getFirst(); - polygon * NextPoly(); +class polygon +{ +public: + vertex* m_first; // Reference to first vertex in the linked list + int m_cnt; // Tracks number of vertices in the polygon + +public: + polygon( vertex* first = NULL ); + ~polygon(); + vertex* getFirst(); + polygon* NextPoly(); + + void add( vertex* nv ); + void addv( double x, double y, + double xc = 0, double yc = 0, int d = 0 ); + vertex* del( vertex* v ); + void res(); + polygon* copy_poly(); + void insertSort( vertex* nv, vertex* s, vertex* e ); + vertex* nxt( vertex* v ); + BOOL unckd_remain(); + vertex* first_unckd_intersect(); + double dist( double x1, double y1, double x2, double y2 ); + double angle( double xc, double yc, double x1, double y1 ); + double aAlpha( double x1, double y1, double x2, double y2, + double xc, double yc, double xi, double yi, double d ); + void perturb( vertex* p1, vertex* p2, vertex* q1, vertex* q2, + double aP, double aQ ); + + BOOL ints( vertex * p1, vertex * p2, vertex * q1, vertex * q2, + int* n, double ix[], double iy[], double alphaP[], double alphaQ[] ); + BOOL isInside( vertex* v ); + polygon* boolean( polygon* polyB, int oper ); - void add( vertex * nv ); - void addv( double x, double y, - double xc=0, double yc=0, int d=0); - vertex * del( vertex * v ); - void res(); - polygon * copy_poly(); - void insertSort( vertex * nv, vertex * s, vertex * e ); - vertex * nxt( vertex * v ); - BOOL unckd_remain(); - vertex * first_unckd_intersect(); - double dist( double x1, double y1, double x2, double y2 ); - double angle( double xc, double yc, double x1, double y1 ); - double aAlpha( double x1, double y1, double x2, double y2, - double xc, double yc, double xi, double yi, double d ); - void perturb( vertex * p1, vertex * p2, vertex * q1, vertex * q2, - double aP, double aQ ); - BOOL ints( vertex * p1, vertex * p2, vertex * q1, vertex * q2, - int * n, double ix[], double iy[], double alphaP[], double alphaQ[] ); - BOOL isInside ( vertex * v ); - polygon * boolean( polygon * polyB, int oper ); #if 0 - function isPolyInside (p); - function move (dx, dy); - function rotate (xr, yr, a); - function &bRect (); + function isPolyInside( p ); + function move( dx, dy ); + function rotate( xr, yr, a ); + function& bRect(); + #endif }; //end of class polygon -#endif // ifndef PHP_POLYGON_H +#endif // ifndef PHP_POLYGON_H