diff --git a/3d-viewer/CMakeLists.txt b/3d-viewer/CMakeLists.txt index 50ff80d3a0..179f4c17ed 100644 --- a/3d-viewer/CMakeLists.txt +++ b/3d-viewer/CMakeLists.txt @@ -1,6 +1,6 @@ ADD_DEFINITIONS(-DPCBNEW) -INCLUDE_DIRECTORIES(../pcbnew) +INCLUDE_DIRECTORIES(../pcbnew ../polygon) SET(3D-VIEWER_SRCS 3d_aux.cpp diff --git a/3d-viewer/makefile.include b/3d-viewer/makefile.include index 3796ac68ee..db3bb7e56e 100644 --- a/3d-viewer/makefile.include +++ b/3d-viewer/makefile.include @@ -1,5 +1,5 @@ EXTRALIBS = -EXTRACPPFLAGS= -I./ -I../include -I../common -I../pcbnew +EXTRACPPFLAGS= -I./ -I../include -I../common -I../polygon -I../pcbnew CPPFLAGS += $(EXTRACPPFLAGS) diff --git a/change_log.txt b/change_log.txt index 399433f0e6..46a15f4723 100644 --- a/change_log.txt +++ b/change_log.txt @@ -4,6 +4,14 @@ Started 2007-June-11 Please add newer entries at the top, list the date and your name with email address. +2007-Dec-29 UPDATE Jean-Pierre Charras +================================================================================ ++pcbnew: + First draft (and code..) about new zone handling, using polygons to define an outline. + Now currently not useable because the fill function (and many other important functions) is not implemented. + Many functions are not yet implemented: merging zones, cutout, DRC ... + Nevertheless, one can create, modify edit and save zone outlines + 2007-Dec-23 UPDATE Dick Hollenbeck ================================================================================ diff --git a/cvpcb/CMakeLists.txt b/cvpcb/CMakeLists.txt index 030021274e..ea9b5b6d85 100644 --- a/cvpcb/CMakeLists.txt +++ b/cvpcb/CMakeLists.txt @@ -4,6 +4,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} bitmaps ../3d-viewer + ../polygon ../pcbnew) SET(CVPCB_SRCS @@ -35,6 +36,7 @@ SET(CVPCB_SRCS SET(CVPCB_EXTRA_SRCS ../pcbnew/basepcbframe.cpp ../pcbnew/class_board.cpp + ../pcbnew/class_zone.cpp ../pcbnew/class_cotation.cpp ../pcbnew/class_edge_mod.cpp ../pcbnew/class_equipot.cpp diff --git a/cvpcb/makefile.include b/cvpcb/makefile.include index 5a8cb829d3..096d5e9f9f 100644 --- a/cvpcb/makefile.include +++ b/cvpcb/makefile.include @@ -1,8 +1,12 @@ # makefile pour cvpcb (mingw) OBJSUFF = o -EXTRACPPFLAGS += -DCVPCB -fno-strict-aliasing -I./ -I../cvpcb -I../include -Ibitmaps -I../pcbnew -I../3d-viewer -EXTRALIBS = ../common/common.a +EXTRACPPFLAGS += -DCVPCB -fno-strict-aliasing\ + -I./ -I../cvpcb -I../include -Ibitmaps\ + -I../pcbnew -I../3d-viewer\ + -I../polygon + +EXTRALIBS = ../common/common.a ../polygon/lib_polygon.a LIBVIEWER3D = ../3d-viewer/3d-viewer.a @@ -10,6 +14,7 @@ LIBVIEWER3D = ../3d-viewer/3d-viewer.a OBJECTS = $(TARGET).o \ class_cvpcb.o\ + class_zone.o\ memoire.o \ cvframe.o\ listboxes.o\ @@ -69,6 +74,9 @@ classpcb.o: ../pcbnew/classpcb.cpp $(DEPEND) class_mire.o: ../pcbnew/class_mire.cpp ../pcbnew/class_mire.h $(COMMON) $(CXX) -c $(EDACPPFLAGS) -o $@ ../pcbnew/$*.cpp +class_zone.o: ../pcbnew/class_zone.cpp ../pcbnew/class_zone.h $(COMMON) + $(CXX) -c $(EDACPPFLAGS) -o $@ ../pcbnew/$*.cpp + class_cotation.o: ../pcbnew/class_cotation.cpp ../pcbnew/class_cotation.h $(COMMON) $(CXX) -c $(EDACPPFLAGS) -o $@ ../pcbnew/$*.cpp diff --git a/gerbview/CMakeLists.txt b/gerbview/CMakeLists.txt index 5864e62a20..0355977ca1 100644 --- a/gerbview/CMakeLists.txt +++ b/gerbview/CMakeLists.txt @@ -4,7 +4,8 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR} ../3d-viewer ../cvpcb - ../pcbnew) + ../pcbnew + ../polygon) SET(GERBVIEW_SRCS affiche.cpp @@ -41,6 +42,7 @@ SET(GERBVIEW_SRCS SET(GERBVIEW_EXTRA_SRCS ../pcbnew/basepcbframe.cpp ../pcbnew/class_board.cpp + ../pcbnew/class_zone.cpp ../pcbnew/class_drc_item.cpp ../pcbnew/class_marker.cpp ../pcbnew/class_pcb_text.cpp @@ -74,6 +76,6 @@ ENDIF(APPLE) ADD_EXECUTABLE(gerbview WIN32 MACOSX_BUNDLE ${GERBVIEW_SRCS} ${GERBVIEW_EXTRA_SRCS} ${GERBVIEW_RESOURCES}) -TARGET_LINK_LIBRARIES(gerbview common 3d-viewer ${wxWidgets_LIBRARIES}) +TARGET_LINK_LIBRARIES(gerbview common 3d-viewer lib_polygon ${wxWidgets_LIBRARIES}) INSTALL(TARGETS gerbview RUNTIME DESTINATION ${KICAD_BIN}) diff --git a/gerbview/makefile.include b/gerbview/makefile.include index ad31a95858..26624287ee 100644 --- a/gerbview/makefile.include +++ b/gerbview/makefile.include @@ -1,12 +1,17 @@ -EXTRALIBS = ../common/common.a -EXTRACPPFLAGS= -DGERBVIEW -DPCBNEW -fno-strict-aliasing -I./ -I../gerbview -I../include\ - -I../share -I../pcbnew -I../3d-viewer +EXTRALIBS = ../common/common.a ../polygon/lib_polygon.a + +EXTRACPPFLAGS= -DGERBVIEW -DPCBNEW -fno-strict-aliasing\ + -I./ -I../gerbview -I../include\ + -I../share -I../pcbnew -I../3d-viewer\ + -I../polygon + #COMMON = pcbnew.h struct.h OBJECTS= \ $(TARGET).o\ classpcb.o\ + class_zone.o\ select_layers_to_pcb.o\ sel_layer.o\ lay2plot.o\ @@ -69,6 +74,9 @@ files.o: files.cpp $(COMMON) class_marker.o: ../pcbnew/class_marker.cpp ../pcbnew/class_marker.h $(COMMON) $(CXX) -c $(EDACPPFLAGS) -o $@ ../pcbnew/$*.cpp +class_zone.o: ../pcbnew/class_zone.cpp ../pcbnew/class_zone.h $(COMMON) + $(CXX) -c $(EDACPPFLAGS) -o $@ ../pcbnew/$*.cpp + class_drc_item.o: ../pcbnew/class_drc_item.cpp $(COMMON) $(CXX) -c $(EDACPPFLAGS) -o $@ ../pcbnew/$*.cpp diff --git a/include/base_struct.h b/include/base_struct.h index 5f392e6675..aa4e1ba689 100644 --- a/include/base_struct.h +++ b/include/base_struct.h @@ -41,7 +41,7 @@ enum KICAD_T { TYPESCREEN, TYPEBLOCK, TYPEEDGEZONE, - TYPEZONE_POLYEDGE, + TYPEZONE_EDGE_CORNER, TYPEZONE_CONTAINER, // Draw Items in schematic diff --git a/include/build_version.h b/include/build_version.h index 4687e6097a..17c2b6767c 100644 --- a/include/build_version.h +++ b/include/build_version.h @@ -5,7 +5,7 @@ COMMON_GLOBL wxString g_BuildVersion #ifdef EDA_BASE - (wxT("(2007-12-17)")) + (wxT("(2007-12-22)")) #endif ; diff --git a/include/id.h b/include/id.h index aa9bc119a5..3a8d2422ea 100644 --- a/include/id.h +++ b/include/id.h @@ -546,8 +546,21 @@ enum main_id { ID_POPUP_PCB_DELETE_TRACK, ID_POPUP_PCB_DELETE_TRACKNET, ID_POPUP_PCB_DELETE_TRACK_MNU, - ID_POPUP_PCB_EDIT_ZONE, + + ID_POPUP_PCB_EDIT_ZONE_PARAMS, ID_POPUP_PCB_DELETE_ZONE, + ID_POPUP_PCB_MOVE_ZONE_CORNER, + ID_POPUP_PCB_ADD_ZONE_CORNER, + ID_POPUP_PCB_DELETE_ZONE_CORNER, + ID_POPUP_PCB_STOP_CURRENT_EDGE_ZONE, + ID_POPUP_PCB_DELETE_EDGE_ZONE, + ID_POPUP_PCB_DELETE_ZONE_LIMIT, + ID_POPUP_PCB_FILL_ZONE, + ID_POPUP_PCB_DELETE_ZONE_CONTAINER, + ID_POPUP_PCB_PLACE_ZONE_CORNER, + ID_POPUP_ZONE_UNUSED2, + ID_POPUP_ZONE_UNUSED3, + ID_POPUP_ZONE_UNUSED4, ID_POPUP_PCB_DELETE_MARKER, ID_POPUP_PCB_DELETE_COTATION, @@ -565,12 +578,6 @@ enum main_id { ID_POPUP_PCB_IMPORT_PAD_SETTINGS, ID_POPUP_PCB_EXPORT_PAD_SETTINGS, - ID_POPUP_PCB_STOP_CURRENT_EDGE_ZONE, - ID_POPUP_PCB_DELETE_EDGE_ZONE, - ID_POPUP_PCB_DELETE_ZONE_LIMIT, - ID_POPUP_PCB_FILL_ZONE, - ID_POPUP_PCB_SELECT_NET_ZONE, - ID_POPUP_PCB_SELECT_WIDTH, ID_POPUP_PCB_SELECT_WIDTH1, ID_POPUP_PCB_SELECT_WIDTH2, diff --git a/include/wxstruct.h b/include/wxstruct.h index 65b0477c4f..72de15b449 100644 --- a/include/wxstruct.h +++ b/include/wxstruct.h @@ -102,6 +102,7 @@ class Ki_HotkeyInfo; class GENERAL_COLLECTOR; class GENERAL_COLLECTORS_GUIDE; class DRC; +class ZONE_CONTAINER; enum id_librarytype { @@ -728,10 +729,8 @@ public: TRACK* Delete_Segment( wxDC* DC, TRACK* Track ); void Delete_Track( wxDC* DC, TRACK* Track ); void Delete_net( wxDC* DC, TRACK* Track ); - void Delete_Zone( wxDC* DC, SEGZONE* Track ); void Supprime_Une_Piste( wxDC* DC, TRACK* pt_segm ); bool Resize_Pistes_Vias( wxDC* DC, bool Track, bool Via ); - void Edit_Zone_Width( wxDC* DC, SEGZONE* pt_ref ); void Edit_Net_Width( wxDC* DC, int Netcode ); void Edit_Track_Width( wxDC* DC, TRACK* Track ); int Edit_TrackSegm_Width( wxDC* DC, TRACK* segm ); @@ -749,8 +748,13 @@ public: bool Genere_Pad_Connexion( wxDC* DC, int layer ); // zone handling + void Delete_Zone( wxDC* DC, SEGZONE* Track ); EDGE_ZONE* Del_SegmEdgeZone( wxDC* DC, EDGE_ZONE* edge_zone ); - void CaptureNetName( wxDC* DC ); + /** + * Function Begin_Zone + * initiates a zone edge creation process, + * or terminates the current zone edge and creates a new zone edge stub + */ EDGE_ZONE* Begin_Zone( wxDC* DC ); /** @@ -761,14 +765,30 @@ public: /** * Function Fill_Zone - * creates a number zone segments by using a flood fill algorithm. The - * "high-lighted" net is used to determine the netcode of all the zone - * segments and what can be connected to and what must be avoided on the - * current layer as the flooding occurs. + * Fills an outline. */ - void Fill_Zone( wxDC* DC ); + void Fill_Zone( wxDC* DC, ZONE_CONTAINER * zone_container ); - // Target handling + /** + * Function Edit_Zone_Params + * Edit params (layer, clearance, ...) for a zone outline + */ + void Edit_Zone_Params( wxDC* DC , ZONE_CONTAINER * zone_container ); + + /** + * Function Start_Move_Zone_Corner + * Prepares a move corner in a zone outline, + * called from a move corner command (IsNewCorner = false), + * or a create new cornet command (IsNewCorner = true ) + */ + void Start_Move_Zone_Corner( wxDC* DC , ZONE_CONTAINER * zone_container, int corner_id, bool IsNewCorner ); + /** + * Function End_Move_Zone_Corner + * Terminates a move corner in a zone outline + */ + void End_Move_Zone_Corner( wxDC* DC , ZONE_CONTAINER * zone_container ); + + // Target handling MIREPCB* Create_Mire( wxDC* DC ); void Delete_Mire( MIREPCB* MirePcb, wxDC* DC ); void StartMove_Mire( MIREPCB* MirePcb, wxDC* DC ); diff --git a/makefile.g95 b/makefile.g95 index 9c7cf68847..fda7410654 100644 --- a/makefile.g95 +++ b/makefile.g95 @@ -1,4 +1,4 @@ -KICAD_SUBDIRS = common 3d-viewer eeschema eeschema/plugins pcbnew kicad cvpcb gerbview +KICAD_SUBDIRS = common 3d-viewer polygon eeschema eeschema/plugins pcbnew kicad cvpcb gerbview KICAD_SUBDIRS_BIN = eeschema eeschema/plugins pcbnew cvpcb kicad gerbview # How to invoke make: MAKE = make -k -f makefile.g95 diff --git a/makefile.gtk b/makefile.gtk index cbec5064c7..1d5bc8d278 100644 --- a/makefile.gtk +++ b/makefile.gtk @@ -1,6 +1,6 @@ MAKEGTK = $(MAKE) -f makefile.gtk -KICAD_SUBDIRS = common 3d-viewer pcbnew eeschema eeschema/plugins cvpcb kicad gerbview +KICAD_SUBDIRS = common 3d-viewer polygon pcbnew eeschema eeschema/plugins cvpcb kicad gerbview KICAD_SUBDIRS_BIN = eeschema eeschema/plugins pcbnew cvpcb kicad gerbview KICAD_SUBDIRS_RES = internat modules template library KICAD_SUBDIRS_HELP = help diff --git a/makefile.macosx b/makefile.macosx index 30e6f4a1c1..e5e1a8136e 100644 --- a/makefile.macosx +++ b/makefile.macosx @@ -1,4 +1,4 @@ -KICAD_SUBDIRS = common 3d-viewer eeschema pcbnew cvpcb kicad gerbview +KICAD_SUBDIRS = common 3d-viewer polygon eeschema pcbnew cvpcb kicad gerbview KICAD_SUBDIRS_BIN = eeschema pcbnew cvpcb kicad gerbview # How to invoke make: MAKE = make -f makefile.macosx diff --git a/pcbnew/class_board.cpp b/pcbnew/class_board.cpp index 4828cd2186..b3849ee363 100644 --- a/pcbnew/class_board.cpp +++ b/pcbnew/class_board.cpp @@ -6,8 +6,6 @@ #include "pcbnew.h" -//#include "bitmaps.h" - /*****************/ /* Class BOARD: */ @@ -76,6 +74,7 @@ BOARD::~BOARD() m_LocalRatsnest = 0; DeleteMARKERs(); + DeleteZONEOutlines(); } @@ -128,11 +127,11 @@ void BOARD::Add( BOARD_ITEM* aBoardItem, int aControl ) void BOARD::Delete( BOARD_ITEM* aBoardItem ) { + if ( aBoardItem == NULL ) return; + switch( aBoardItem->Type() ) { - // this one uses a vector - case TYPEMARKER: - + case TYPEMARKER: // this one uses a vector // find the item in the vector, then delete then erase it. for( unsigned i=0; iVisit( inspector, testData, p ); + if( result == SEARCH_QUIT ) + break; + } + ++p; + break; + case PCB_EQUIPOT_STRUCT_TYPE: result = IterateForward( m_Equipots, inspector, testData, p ); ++p; @@ -782,16 +814,12 @@ bool BOARD::Save( FILE* aFile ) const fprintf( aFile, "$EndZONE\n" ); // save the zone edges -/* - if( m_CurrentLimitZone ) - { - fprintf( aFile, "$ZONE_EDGE\n" ); - for( item = m_CurrentLimitZone; item; item=item->Next() ) - if( !item->Save( aFile ) ) - goto out; - fprintf( aFile, "$EndZONE_EDGE\n" ); - } -*/ + for( unsigned ii = 0; ii < m_ZoneDescriptorList.size(); ii++ ) + { + ZONE_CONTAINER* edge_zone = m_ZoneDescriptorList[ii]; + edge_zone->Save( aFile ); + } + if( fprintf( aFile, "$EndBOARD\n" ) != sizeof("$EndBOARD\n")-1 ) goto out; diff --git a/pcbnew/class_board.h b/pcbnew/class_board.h index 840798684b..3b0b5d729b 100644 --- a/pcbnew/class_board.h +++ b/pcbnew/class_board.h @@ -20,7 +20,6 @@ class BOARD : public BOARD_ITEM private: std::vector m_markers; ///< MARKERs for clearance problems, owned by pointer -// std::vector m_markersUnconnected; ///< MARKERs for unconnected problems, owned by pointer std::vector m_ZoneDescriptorList; ///< edge zone descriptors, owned by pointer @@ -86,6 +85,12 @@ public: */ void DeleteMARKERs(); + /** + * Function DeleteZONEOutlines + * deletes ALL zone outlines from the board. + */ + void DeleteZONEOutlines(); + /** * Function DeleteMARKER diff --git a/pcbnew/class_board_item.cpp b/pcbnew/class_board_item.cpp index f5f23eab61..9adf40b362 100644 --- a/pcbnew/class_board_item.cpp +++ b/pcbnew/class_board_item.cpp @@ -149,8 +149,25 @@ wxString BOARD_ITEM::MenuText( const BOARD* aPcb ) const } break; - case TYPEZONE: - text << _( "Zone" ) << wxT( " " ); + case TYPEZONE_CONTAINER: + text = _( "Zone Outline" ); + text << wxT( " " ); + { + wxString TimeStampText; + TimeStampText.Printf( wxT( "(%8.8X)" ), item->m_TimeStamp ); + text << TimeStampText; + } + net = aPcb->FindNet( ( (ZONE_CONTAINER*) item )->GetNet() ); + if( net ) + { + text << wxT( " [" ) << net->m_Netname << wxT( "]" ); + } + text << _( " on " ) << ReturnPcbLayerName( item->GetLayer() ).Trim(); + break; + + case TYPEZONE: + text = _( "Zone" ); + text << wxT( " " ); { wxString TimeStampText; TimeStampText.Printf( wxT( "(%8.8X)" ), item->m_TimeStamp ); @@ -268,6 +285,7 @@ const char** BOARD_ITEM::MenuIcon() const xpm = showtrack_xpm; break; + case TYPEZONE_CONTAINER: case TYPEZONE: xpm = add_zone_xpm; break; diff --git a/pcbnew/class_equipot.cpp b/pcbnew/class_equipot.cpp index 423269e0af..f8f4bb4141 100644 --- a/pcbnew/class_equipot.cpp +++ b/pcbnew/class_equipot.cpp @@ -44,6 +44,7 @@ EQUIPOT::~EQUIPOT() } + wxPoint& EQUIPOT::GetPosition() { static wxPoint dummy; @@ -172,3 +173,5 @@ void EQUIPOT::Show( int nestLevel, std::ostream& os ) " netcode=\"" << GetNet() << "\"/>\n"; } #endif + + diff --git a/pcbnew/class_equipot.h b/pcbnew/class_equipot.h index b15e43c9e4..e9e4bc9d4c 100644 --- a/pcbnew/class_equipot.h +++ b/pcbnew/class_equipot.h @@ -61,7 +61,7 @@ public: */ int GetNet() const { return m_NetCode; } void SetNet( int aNetCode ) { m_NetCode = aNetCode; } - + /** * Function GetClass @@ -86,3 +86,4 @@ public: #endif }; + diff --git a/pcbnew/class_mire.cpp b/pcbnew/class_mire.cpp index 41a6accb7a..672a1a12ba 100644 --- a/pcbnew/class_mire.cpp +++ b/pcbnew/class_mire.cpp @@ -93,27 +93,9 @@ bool MIREPCB::ReadMirePcbDescr( FILE* File, int* LineNum ) return FALSE; } - -#if 0 // replaced by Save() -/************************************************/ -bool MIREPCB::WriteMirePcbDescr( FILE* File ) -/************************************************/ -{ - if( GetState( DELETED ) ) - return FALSE; - - fprintf( File, "$MIREPCB\n" ); - fprintf( File, "Po %X %d %d %d %d %d %8.8lX\n", - m_Shape, m_Layer, - m_Pos.x, m_Pos.y, - m_Size, m_Width, m_TimeStamp ); - fprintf( File, "$EndMIREPCB\n" ); - return TRUE; -} -#endif - - +/**************************************/ bool MIREPCB::Save( FILE* aFile ) const +/**************************************/ { if( GetState( DELETED ) ) return true; diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index 30c1103f57..bbbdce6ea7 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -14,10 +14,13 @@ /* class ZONE_CONTAINER */ /************************/ -ZONE_CONTAINER::ZONE_CONTAINER (BOARD * parent): - BOARD_ITEM (parent, TYPEZONE_CONTAINER) +ZONE_CONTAINER::ZONE_CONTAINER( BOARD* parent ) : + BOARD_ITEM( parent, TYPEZONE_CONTAINER ) + , CPolyLine( NULL ) + { m_NetCode = -1; // Net number for fast comparisons + m_CornerSelection = -1; } @@ -25,11 +28,6 @@ ZONE_CONTAINER::~ZONE_CONTAINER() { } -bool ZONE_CONTAINER::Save( FILE* aFile ) const -{ - return true; -} - /**********************/ /* Class EDGE_ZONE */ @@ -49,19 +47,367 @@ EDGE_ZONE:: ~EDGE_ZONE() } +/****************************************/ bool EDGE_ZONE::Save( FILE* aFile ) const +/****************************************/ +{ + return true; +} + +/********************************************/ +bool ZONE_CONTAINER::Save( FILE* aFile ) const +/********************************************/ { if( GetState( DELETED ) ) return true; - int ret = fprintf( aFile, "ZE %d %d %d %d %d %lX %X\n", - m_Start.x, m_Start.y, - m_End.x, m_End.y, - m_Angle, - m_TimeStamp, - ReturnStatus() - ); - - return (ret > 14 ); + unsigned item_pos; + int ret; + unsigned corners_count = corner.size(); + int outline_hatch; + + fprintf( aFile, "$CZONE_OUTLINE\n"); + // Save the outline main info + ret = fprintf( aFile, "ZInfo %8.8lX %d \"%s\"\n", + m_TimeStamp, m_NetCode, + CONV_TO_UTF8(m_Netname) ); + if ( ret < 3 ) return false; + // Save the ouline layer info + ret = fprintf( aFile, "ZLayer %d\n", m_Layer ); + if ( ret < 1 ) return false; + // Save the ouline aux info + switch ( m_HatchStyle ) + { + default: + case NO_HATCH: + outline_hatch = 'N'; + break; + case DIAGONAL_EDGE: + outline_hatch = 'E'; + break; + case DIAGONAL_FULL: + outline_hatch = 'F'; + break; + } + + ret = fprintf( aFile, "ZAux %d %c\n", corners_count, outline_hatch ); + if ( ret < 2 ) return false; + // Save the corner list + for ( item_pos = 0; item_pos < corners_count; item_pos++ ) + { + ret = fprintf( aFile, "ZCorner %d %d %d \n", + corner[item_pos].x, corner[item_pos].y, + corner[item_pos].end_contour ); + if ( ret < 3 ) return false; + } + fprintf( aFile, "$endCZONE_OUTLINE\n"); + + return true; } + +/**********************************************************/ +int ZONE_CONTAINER::ReadDescr( FILE* aFile, int* aLineNum ) +/**********************************************************/ +/** Function ReadDescr + * @param aFile = opened file + * @param aLineNum = pointer on a line number counter (can be NULL or missing) + * @return 0 if ok or NULL + */ +{ + char Line[1024], * text; + char netname_buffer[1024]; + int ret; + int n_corner_item = 0; + int outline_hatch = NO_HATCH; + bool error = false, has_corner = false; + + netname_buffer[0] = 0; + while( GetLine( aFile, Line, aLineNum, sizeof(Line) - 1 ) != NULL ) + { + if( strnicmp(Line, "ZCorner", 7 ) == 0 ) // new corner found + { + int x = 0, y = 0, flag = 0; + text = Line + 7; + ret = sscanf( text, "%d %d %d", &x, &y, &flag); + if (ret < 3 ) error = true; + else + { + if ( ! has_corner ) + Start( m_Layer, 0, 0, + x, y, + outline_hatch ); + else + AppendCorner( x, y ); + has_corner = true; + if ( flag ) Close(); + } + } + if( strnicmp(Line, "ZInfo", 5 ) == 0 ) // general info found + { + int ts = 0, netcode = 0; + text = Line + 5; + ret = sscanf( text, "%X %d %s", &ts, &netcode, netname_buffer); + if (ret < 3 ) error = true; + else + { + m_TimeStamp = ts; + m_NetCode = netcode; + ReadDelimitedText( netname_buffer, netname_buffer, 1024 ); + m_Netname = CONV_FROM_UTF8(netname_buffer); + } + } + if( strnicmp(Line, "ZLayer", 6 ) == 0 ) // layer found + { + int x = 0; + text = Line + 6; + ret = sscanf( text, "%d", &x); + if (ret < 1 ) error = true; + else m_Layer = x; + } + if( strnicmp(Line, "ZAux", 4 ) == 0 ) // aux info found + { + int x = 0; + char hopt[10]; + text = Line + 4; + ret = sscanf( text, "%d %c", &x, hopt ); + if (ret < 2 ) error = true; + else + { + n_corner_item = x; + switch ( hopt[0] ) + { + case 'n': + case 'N': + outline_hatch = NO_HATCH; + break; + case 'e': + case 'E': + outline_hatch = DIAGONAL_EDGE; + break; + case 'f': + case 'F': + outline_hatch = DIAGONAL_FULL; + break; + } + } + } + if( strnicmp(Line, "$end", 4 ) == 0 ) // end of description + { + break; + } + } + + return error ? 0 : 1; +} + + +/****************************************************************************************************/ +void ZONE_CONTAINER::Draw( WinEDA_DrawPanel* panel, wxDC* DC, const wxPoint& offset, int draw_mode ) +/****************************************************************************************************/ + +/** Function Draw + * @param panel = current Draw Panel + * @param DC = current Device Context + * @param offset = Draw offset (usually wxPoint(0,0)) + * @param draw_mode = draw mode: OR, XOR .. + */ +{ + int curr_layer = ( (PCB_SCREEN*) panel->GetScreen() )->m_Active_Layer; + int color = g_DesignSettings.m_LayerColor[m_Layer]; + + if( ( color & (ITEM_NOT_SHOW | HIGHT_LIGHT_FLAG) ) == ITEM_NOT_SHOW ) + return; + + GRSetDrawMode( DC, draw_mode ); + + if( DisplayOpt.ContrastModeDisplay ) + { + if( !IsOnLayer( curr_layer ) ) + { + color &= ~MASKCOLOR; + color |= DARKDARKGRAY; + } + } + + if( draw_mode & GR_SURBRILL ) + { + if( draw_mode & GR_AND ) + color &= ~HIGHT_LIGHT_FLAG; + else + color |= HIGHT_LIGHT_FLAG; + } + if( color & HIGHT_LIGHT_FLAG ) + color = ColorRefs[color & MASKCOLOR].m_LightColor; + + // draw the lines + int i_start_contour = 0; + for( unsigned ic = 0; ic < corner.size(); ic++ ) + { + int xi = corner[ic].x + offset.x; + int yi = corner[ic].y + offset.y; + int xf, yf; + if( corner[ic].end_contour == FALSE && ic < corner.size() - 1 ) + { + xf = corner[ic + 1].x + offset.x; + yf = corner[ic + 1].y + offset.y; + } + else + { + xf = corner[i_start_contour].x + offset.x; + yf = corner[i_start_contour].y + offset.y; + i_start_contour = ic + 1; + } + GRLine( &panel->m_ClipBox, DC, xi, yi, xf, yf, 0, color ); + } + + // draw hatches + for( unsigned ic = 0; ic < m_HatchLines.size(); ic++ ) + { + int xi = m_HatchLines[ic].xi + offset.x; + int yi = m_HatchLines[ic].yi + offset.y; + int xf = m_HatchLines[ic].xf + offset.x; + int yf =m_HatchLines[ic].yf + offset.y; + GRLine( &panel->m_ClipBox, DC, xi, yi, xf, yf, 0, color ); + } + +} + + +/** + * Function HitTest + * tests if the given wxPoint is within the bounds of this object. + * @param refPos A wxPoint to test + * @return bool - true if a hit, else false + * return true if refPos is near a corner or an edge + */ +bool ZONE_CONTAINER::HitTest( const wxPoint& refPos ) +{ + if( HitTestForCorner( refPos ) >= 0 ) + return true; + if( HitTestForEdge( refPos ) >= 0 ) + return true; + + return false; +} + + +/** + * Function HitTestForCorner + * tests if the given wxPoint near a corner, or near the segment define by 2 corners. + * "near" means MIN_DIST_IN_PIXELS pixels + * @return -1 if none, corner index in .corner + * @param refPos : A wxPoint to test + */ +int ZONE_CONTAINER::HitTestForCorner( const wxPoint& refPos ) +{ + #define MIN_DIST_IN_PIXELS 5 + int dist; + unsigned item_pos, lim; + lim = corner.size(); + + // Min distance to hit = MIN_DIST_IN_PIXELS pixels : + WinEDA_BasePcbFrame* frame = ((BOARD*)GetParent())->m_PcbFrame; + int min_dist = frame ? frame->GetZoom() * MIN_DIST_IN_PIXELS : 3; + + for ( item_pos = 0; item_pos < lim; item_pos++ ) + { + dist = abs( corner[item_pos].x - refPos.x ) + abs( corner[item_pos].y - refPos.y ); + if( dist <= min_dist ) + return item_pos; + } + + return -1; +} + +/** + * Function HitTestForEdge + * tests if the given wxPoint near a corner, or near the segment define by 2 corners. + * "near" means MIN_DIST_IN_PIXELS pixels + * @return -1 if none, or index of the starting corner in .corner + * @param refPos : A wxPoint to test + */ +int ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos ) +{ + #define MIN_DIST_IN_PIXELS 5 + int dist; + unsigned item_pos, lim; + lim = corner.size(); + + // Min distance to hit = MIN_DIST_IN_PIXELS pixels : + WinEDA_BasePcbFrame* frame = ((BOARD*)GetParent())->m_PcbFrame; + int min_dist = frame ? frame->GetZoom() * MIN_DIST_IN_PIXELS : 3; + + /* Test for an entire segment */ + unsigned first_corner_pos = 0, end_segm; + + for ( item_pos = 0; item_pos < lim; item_pos++ ) + { + end_segm = item_pos+1; + /* the last corner of the current outline is tested + * the last segment of the current outline starts at current corner, and ends + * at the first corner of the outline + */ + if( corner[item_pos].end_contour || end_segm >= lim) + { + unsigned tmp = first_corner_pos; + first_corner_pos = end_segm; // first_corner_pos is now the beginning of the next outline + end_segm = tmp; // end_segm is the beginning of the current outline + } + + /* test the dist between segment and ref point */ + dist = (int) GetPointToLineSegmentDistance( refPos.x, + refPos.y, + corner[item_pos].x, + corner[item_pos].y, + corner[end_segm].x, + corner[end_segm].y ); + if( dist <= min_dist ) + return item_pos; + } + + return -1; +} + + +/************************************************************/ +void ZONE_CONTAINER::Display_Infos( WinEDA_DrawFrame* frame ) +/************************************************************/ +{ + wxString msg; + int text_pos; + + frame->MsgPanel->EraseMsgBox(); + + msg = _( "Zone Outline" ); + + text_pos = 1; + Affiche_1_Parametre( frame, text_pos, _( "Type" ), msg, DARKCYAN ); + + text_pos += 15; + EQUIPOT* equipot = ( (WinEDA_PcbFrame*) frame )->m_Pcb->FindNet( GetNet() ); + + if( equipot ) + msg = equipot->m_Netname; + else + msg = wxT( "" ); + + Affiche_1_Parametre( frame, text_pos, _( "NetName" ), msg, RED ); + + /* Display net code : (usefull in test or debug) */ + text_pos += 18; + msg.Printf( wxT( "%d" ), GetNet()); + Affiche_1_Parametre( frame, text_pos, _( "NetCode" ), msg, RED ); + + text_pos += 8; + msg = ReturnPcbLayerName( m_Layer ); + Affiche_1_Parametre( frame, text_pos, _( "Layer" ), msg, BROWN ); + + text_pos += 8; + msg.Printf( wxT( "%d" ), corner.size() ); + Affiche_1_Parametre( frame, text_pos, _( "Corners" ), msg, BLUE ); + + text_pos += 8; + msg.Printf( wxT( "%d" ), m_HatchLines.size() ); + Affiche_1_Parametre( frame, text_pos, _( "Hatch lines" ), msg, BLUE ); +} diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index 8bc746a854..9b9f71e032 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -5,6 +5,8 @@ #ifndef CLASS_ZONE_H #define CLASS_ZONE_H +#include "PolyLine.h" + /************************/ /* class ZONE_CONTAINER */ /************************/ @@ -13,10 +15,11 @@ * others polygons inside this main polygon are holes. */ -class ZONE_CONTAINER : public BOARD_ITEM // Not sure BOARD_ITEM is better than EDA_BaseStruct +class ZONE_CONTAINER : public BOARD_ITEM, public CPolyLine { public: wxString m_Netname; /* Net Name */ + int m_CornerSelection; // For corner moving, corner index to drag, or -1 if no selection private: int m_NetCode; // Net number for fast comparisons @@ -26,7 +29,47 @@ public: ~ZONE_CONTAINER(); bool Save( FILE* aFile ) const; + int ReadDescr( FILE* aFile, int* aLineNum = NULL ); + + wxPoint & GetPosition( ) { static wxPoint pos ;return pos; } + void UnLink(void) {}; + + void Display_Infos( WinEDA_DrawFrame* frame ); + + /** Function Draw + * Draws the zone outline. + * @param panel = current Draw Panel + * @param DC = current Device Context + * @param offset = Draw offset (usually wxPoint(0,0)) + * @param draw_mode = draw mode: OR, XOR .. + */ + void Draw( WinEDA_DrawPanel* panel, wxDC* DC, + const wxPoint& offset, int draw_mode ); + int GetNet( void ) { return m_NetCode; } + void SetNet( int anet_code ) { m_NetCode = anet_code; } + /** + * Function HitTest + * tests if the given wxPoint is within the bounds of this object. + * @param refPos A wxPoint to test + * @return bool - true if a hit, else false + */ + bool HitTest( const wxPoint& refPos ); + + /** + * Function HitTestForCorner + * tests if the given wxPoint near a corner, or near the segment define by 2 corners. + * @return -1 if none, corner index in .corner + * @param refPos : A wxPoint to test + */ + int HitTestForCorner( const wxPoint& refPos ); + /** + * Function HitTestForEdge + * tests if the given wxPoint near a corner, or near the segment define by 2 corners. + * @return -1 if none, or index of the starting corner in .corner + * @param refPos : A wxPoint to test + */ + int HitTestForEdge( const wxPoint& refPos ); }; /*******************/ diff --git a/pcbnew/collectors.cpp b/pcbnew/collectors.cpp index 6c08704bdb..d2a9534477 100644 --- a/pcbnew/collectors.cpp +++ b/pcbnew/collectors.cpp @@ -47,6 +47,7 @@ const KICAD_T GENERAL_COLLECTOR::AllBoardItems[] = { TYPETEXTEMODULE, TYPEMODULE, TYPEZONE, + TYPEZONE_CONTAINER, EOT }; @@ -212,6 +213,8 @@ SEARCH_RESULT GENERAL_COLLECTOR::Inspect( EDA_BaseStruct* testItem, const void* break; case TYPEZONE: break; + case TYPEZONE_CONTAINER: + break; case TYPETEXTE: break; case TYPEDRAWSEGMENT: diff --git a/pcbnew/dialog_zones_by_polygon.cpp b/pcbnew/dialog_zones_by_polygon.cpp index 761402b778..9130943403 100644 --- a/pcbnew/dialog_zones_by_polygon.cpp +++ b/pcbnew/dialog_zones_by_polygon.cpp @@ -68,6 +68,7 @@ WinEDA_ZoneFrame::WinEDA_ZoneFrame() WinEDA_ZoneFrame::WinEDA_ZoneFrame( WinEDA_PcbFrame* parent, + ZONE_CONTAINER * zone_container, wxWindowID id, const wxString& caption, const wxPoint& pos, @@ -75,6 +76,7 @@ WinEDA_ZoneFrame::WinEDA_ZoneFrame( WinEDA_PcbFrame* parent, long style ) { m_Parent = parent; + m_Zone_Container = zone_container; if( m_Parent->m_Parent->m_EDA_Config ) { @@ -98,7 +100,6 @@ bool WinEDA_ZoneFrame::Create( wxWindow* parent, long style ) { ////@begin WinEDA_ZoneFrame member initialisation - m_OutlinesOpt = 0; m_GridCtrl = NULL; m_ClearanceValueTitle = NULL; m_ZoneClearanceCtrl = NULL; @@ -135,7 +136,7 @@ void WinEDA_ZoneFrame::CreateControls() SetFont( *g_DialogFont ); ////@begin WinEDA_ZoneFrame content construction - // Generated by DialogBlocks, 20/12/2007 15:46:22 (unregistered) + // Generated by DialogBlocks, 29/12/2007 14:29:53 (unregistered) WinEDA_ZoneFrame* itemDialog1 = this; @@ -231,10 +232,11 @@ void WinEDA_ZoneFrame::CreateControls() itemBoxSizer20->Add(m_LayerSelectionCtrl, 0, wxGROW|wxALL, 5); // Set validators - m_OutlineAppearanceCtrl->SetValidator( wxGenericValidator(& m_OutlinesOpt) ); m_NetSortingOption->SetValidator( wxGenericValidator(& m_NetSorting) ); ////@end WinEDA_ZoneFrame content construction - wxString title = _( "Zone clearance value:" ) + ReturnUnitSymbol( g_UnitMetric ); + + // Initialise options + wxString title = _( "Zone clearance value:" ) + ReturnUnitSymbol( g_UnitMetric ); m_ClearanceValueTitle->SetLabel( title ); title = _( "Grid :" ) + ReturnUnitSymbol( g_UnitMetric );; @@ -250,7 +252,7 @@ void WinEDA_ZoneFrame::CreateControls() if( Zone_45_Only ) m_OrientEdgesOpt->SetSelection( 1 ); - static const int GridList[4] = { 50, 100, 250 }; + static const int GridList[3] = { 50, 100, 250 }; int selection = 0; for( unsigned ii = 0; ii < (unsigned) m_GridCtrl->GetCount(); ii++ ) @@ -263,7 +265,6 @@ void WinEDA_ZoneFrame::CreateControls() selection = ii; } - // Initialise options m_GridCtrl->SetSelection( selection ); if( Zone_Exclude_Pads ) @@ -274,6 +275,21 @@ void WinEDA_ZoneFrame::CreateControls() m_FillOpt->SetSelection( 2 ); } + switch( s_Zone_Hatching ) + { + case CPolyLine::NO_HATCH: + m_OutlineAppearanceCtrl->SetSelection(0); + break; + + case CPolyLine::DIAGONAL_EDGE: + m_OutlineAppearanceCtrl->SetSelection(1); + break; + + case CPolyLine::DIAGONAL_FULL: + m_OutlineAppearanceCtrl->SetSelection(2); + break; + } + m_NetSortingOption->SetSelection(m_NetSorting == 0 ? 0 : 1 ); int layer_cnt = g_DesignSettings.m_CopperLayerCount; @@ -288,8 +304,12 @@ void WinEDA_ZoneFrame::CreateControls() m_LayerId[ii] = layer_number; msg = ReturnPcbLayerName( layer_number ).Trim(); m_LayerSelectionCtrl->InsertItems( 1, &msg, ii ); - if( m_Parent->GetScreen()->m_Active_Layer == layer_number ) - m_LayerSelectionCtrl->SetSelection( ii ); + if ( m_Zone_Container ) + if( m_Zone_Container->GetLayer() == layer_number ) + m_LayerSelectionCtrl->SetSelection( ii ); + else + if( m_Parent->GetScreen()->m_Active_Layer == layer_number ) + m_LayerSelectionCtrl->SetSelection( ii ); } wxArrayString ListNetName; @@ -298,9 +318,12 @@ void WinEDA_ZoneFrame::CreateControls() m_ListNetNameSelection->InsertItems( ListNetName, 0 ); // Select net: - if( g_HightLigth_NetCode > 0 ) + int net_select = g_HightLigth_NetCode; + if ( m_Zone_Container ) + net_select = m_Zone_Container->GetNet(); + if( net_select > 0 ) { - EQUIPOT* equipot = m_Parent->m_Pcb->FindNet( g_HightLigth_NetCode ); + EQUIPOT* equipot = m_Parent->m_Pcb->FindNet( net_select ); if( equipot ) // Search net in list and select it { for( unsigned ii = 0; ii < ListNetName.GetCount(); ii++ ) @@ -389,6 +412,21 @@ bool WinEDA_ZoneFrame::AcceptOptions(bool aPromptForErrors) break; } + switch( m_OutlineAppearanceCtrl->GetSelection() ) + { + case 0: + s_Zone_Hatching = CPolyLine::NO_HATCH; + break; + + case 1: + s_Zone_Hatching = CPolyLine::DIAGONAL_EDGE; + break; + + case 2: + s_Zone_Hatching = CPolyLine::DIAGONAL_FULL; + break; + } + switch( m_GridCtrl->GetSelection() ) { case 0: diff --git a/pcbnew/dialog_zones_by_polygon.h b/pcbnew/dialog_zones_by_polygon.h index c5cce26e9b..21e213d154 100644 --- a/pcbnew/dialog_zones_by_polygon.h +++ b/pcbnew/dialog_zones_by_polygon.h @@ -75,7 +75,13 @@ class WinEDA_ZoneFrame: public wxDialog public: /// Constructors WinEDA_ZoneFrame( ); - WinEDA_ZoneFrame( WinEDA_PcbFrame* parent, wxWindowID id = SYMBOL_WINEDA_ZONEFRAME_IDNAME, const wxString& caption = SYMBOL_WINEDA_ZONEFRAME_TITLE, const wxPoint& pos = SYMBOL_WINEDA_ZONEFRAME_POSITION, const wxSize& size = SYMBOL_WINEDA_ZONEFRAME_SIZE, long style = SYMBOL_WINEDA_ZONEFRAME_STYLE ); + WinEDA_ZoneFrame( WinEDA_PcbFrame* parent, + ZONE_CONTAINER * zone_container = NULL, + wxWindowID id = SYMBOL_WINEDA_ZONEFRAME_IDNAME, + const wxString& caption = SYMBOL_WINEDA_ZONEFRAME_TITLE, + const wxPoint& pos = SYMBOL_WINEDA_ZONEFRAME_POSITION, + const wxSize& size = SYMBOL_WINEDA_ZONEFRAME_SIZE, + long style = SYMBOL_WINEDA_ZONEFRAME_STYLE ); /// Creation bool Create( wxWindow* parent, wxWindowID id = SYMBOL_WINEDA_ZONEFRAME_IDNAME, const wxString& caption = SYMBOL_WINEDA_ZONEFRAME_TITLE, const wxPoint& pos = SYMBOL_WINEDA_ZONEFRAME_POSITION, const wxSize& size = SYMBOL_WINEDA_ZONEFRAME_SIZE, long style = SYMBOL_WINEDA_ZONEFRAME_STYLE ); @@ -98,9 +104,6 @@ public: ////@begin WinEDA_ZoneFrame member function declarations - int GetOutlinesOpt() const { return m_OutlinesOpt ; } - void SetOutlinesOpt(int value) { m_OutlinesOpt = value ; } - /// Retrieves bitmap resources wxBitmap GetBitmapResource( const wxString& name ); @@ -125,13 +128,13 @@ public: wxRadioBox* m_NetSortingOption; wxListBox* m_ListNetNameSelection; wxListBox* m_LayerSelectionCtrl; - int m_OutlinesOpt; ////@end WinEDA_ZoneFrame member variables WinEDA_PcbFrame * m_Parent; int m_NetSorting; int m_LayerId[LAYER_COUNT]; // Handle the real layer number from layer name position in m_LayerSelectionCtrl + ZONE_CONTAINER * m_Zone_Container; }; #endif // DIALOG_ZONES_H_ diff --git a/pcbnew/dialog_zones_by_polygon.pjd b/pcbnew/dialog_zones_by_polygon.pjd index 54bd36a53e..6aa04e9034 100644 --- a/pcbnew/dialog_zones_by_polygon.pjd +++ b/pcbnew/dialog_zones_by_polygon.pjd @@ -218,7 +218,6 @@ "" 0 0 - "m_OutlinesOpt|int|OutlinesOpt|0|0|0|" "ID_DIALOG" 10000 "WinEDA_ZoneFrame" @@ -595,8 +594,8 @@ 0 1 "<Any platform>" - "m_OutlinesOpt" - "wxGenericValidator(& %VARIABLE%)" + "" + "" "" "" "" diff --git a/pcbnew/edit.cpp b/pcbnew/edit.cpp index af99f6cc31..76fd0086d0 100644 --- a/pcbnew/edit.cpp +++ b/pcbnew/edit.cpp @@ -67,13 +67,13 @@ void WinEDA_PcbFrame::Process_Special_Functions( wxCommandEvent& event ) case ID_POPUP_PCB_STOP_CURRENT_EDGE_ZONE: case ID_POPUP_PCB_DELETE_EDGE_ZONE: case ID_POPUP_PCB_DELETE_ZONE_LIMIT: - case ID_POPUP_PCB_EDIT_ZONE: + case ID_POPUP_PCB_PLACE_ZONE_CORNER: + case ID_POPUP_PCB_EDIT_ZONE_PARAMS: case ID_POPUP_PCB_DELETE_ZONE: case ID_POPUP_PCB_DELETE_TRACKSEG: case ID_POPUP_PCB_DELETE_TRACK: case ID_POPUP_PCB_DELETE_TRACKNET: case ID_POPUP_PCB_FILL_ZONE: - case ID_POPUP_PCB_SELECT_NET_ZONE: case ID_POPUP_PCB_SELECT_LAYER: case ID_POPUP_PCB_SELECT_CU_LAYER: case ID_POPUP_PCB_SELECT_LAYER_PAIR: @@ -445,18 +445,71 @@ void WinEDA_PcbFrame::Process_Special_Functions( wxCommandEvent& event ) SetCurItem( NULL ); break; - case ID_POPUP_PCB_EDIT_ZONE: - DrawPanel->MouseToCursorSchema(); - if( GetCurItem() == NULL ) - break; - Edit_Zone_Width( &dc, (SEGZONE*) GetCurItem() ); + case ID_POPUP_PCB_EDIT_ZONE_PARAMS: + Edit_Zone_Params( &dc, (ZONE_CONTAINER*) GetCurItem() ); break; + case ID_POPUP_PCB_DELETE_ZONE_CONTAINER: + DrawPanel->MouseToCursorSchema(); + ((ZONE_CONTAINER*)GetCurItem())->Draw(DrawPanel,&dc, wxPoint(0,0), GR_XOR); + m_Pcb->Delete( GetCurItem() ); + SetCurItem( NULL ); + break; + + case ID_POPUP_PCB_DELETE_ZONE_CORNER: + { + DrawPanel->MouseToCursorSchema(); + ZONE_CONTAINER * zone_cont = (ZONE_CONTAINER*)GetCurItem(); + zone_cont->Draw(DrawPanel,&dc, wxPoint(0,0), GR_XOR); + zone_cont->DeleteCorner(zone_cont->m_CornerSelection); + zone_cont->Draw(DrawPanel,&dc, wxPoint(0,0), GR_XOR); + SetCurItem( NULL ); + break; + } + + case ID_POPUP_PCB_MOVE_ZONE_CORNER: + { + DrawPanel->MouseToCursorSchema(); + ZONE_CONTAINER * zone_cont = (ZONE_CONTAINER*)GetCurItem(); + Start_Move_Zone_Corner(&dc, zone_cont, zone_cont->m_CornerSelection, false); + break; + } + + case ID_POPUP_PCB_ADD_ZONE_CORNER: + { + DrawPanel->MouseToCursorSchema(); + ZONE_CONTAINER * zone_cont = (ZONE_CONTAINER*)GetCurItem(); + wxPoint pos = GetScreen()->m_Curseur; + /* add corner between zone_cont->m_CornerSelection + * and zone_cont->m_CornerSelection+1 + * and start move the new corner + */ + zone_cont->Draw(DrawPanel, &dc, wxPoint(0,0), GR_XOR); + zone_cont->InsertCorner( zone_cont->m_CornerSelection, pos.x, pos.y ); + zone_cont->m_CornerSelection++; + zone_cont->Draw(DrawPanel, &dc, wxPoint(0,0), GR_XOR); + Start_Move_Zone_Corner(&dc, zone_cont, zone_cont->m_CornerSelection, true); + break; + } + + case ID_POPUP_PCB_PLACE_ZONE_CORNER: + { + DrawPanel->MouseToCursorSchema(); + ZONE_CONTAINER * zone_cont = (ZONE_CONTAINER*)GetCurItem(); + End_Move_Zone_Corner(&dc, zone_cont); + break; + } + case ID_POPUP_PCB_DELETE_ZONE_LIMIT: DrawPanel->MouseToCursorSchema(); DelLimitesZone( &dc, TRUE ); break; + case ID_POPUP_PCB_FILL_ZONE: + DrawPanel->MouseToCursorSchema(); + Fill_Zone( &dc, (ZONE_CONTAINER*)GetCurItem() ); + break; + case ID_PCB_DELETE_ITEM_BUTT: SetToolID( id, wxCURSOR_BULLSEYE, _( "Delete item" ) ); break; @@ -745,16 +798,6 @@ void WinEDA_PcbFrame::Process_Special_Functions( wxCommandEvent& event ) } break; - case ID_POPUP_PCB_FILL_ZONE: - DrawPanel->MouseToCursorSchema(); - Fill_Zone( &dc ); - break; - - case ID_POPUP_PCB_SELECT_NET_ZONE: - DrawPanel->MouseToCursorSchema(); - CaptureNetName( &dc ); - break; - case ID_POPUP_PCB_SELECT_WIDTH: break; diff --git a/pcbnew/initpcb.cpp b/pcbnew/initpcb.cpp index 53e287a1c4..b442b28fa8 100644 --- a/pcbnew/initpcb.cpp +++ b/pcbnew/initpcb.cpp @@ -1,7 +1,6 @@ -/**********************************************/ -/* PCBNEW : Routines d'initialisation globale */ -/******* Fichier INITPCB.C ********************/ -/**********************************************/ +/*********************************************/ +/******* file initpcb.cpp ********************/ +/*********************************************/ #include "fctsys.h" @@ -148,6 +147,9 @@ bool WinEDA_BasePcbFrame::Clear_Pcb( bool query ) DelLimitesZone( NULL, FALSE ); + m_Pcb->DeleteMARKERs(); + m_Pcb->DeleteZONEOutlines(); + for( ; g_UnDeleteStackPtr != 0; ) { g_UnDeleteStackPtr--; @@ -221,6 +223,7 @@ void WinEDA_PcbFrame::Erase_Zones( bool query ) } DelLimitesZone( NULL, FALSE ); + m_Pcb->DeleteZONEOutlines(); GetScreen()->SetModify(); } diff --git a/pcbnew/ioascii.cpp b/pcbnew/ioascii.cpp index ad3f96ba90..0c06a800e4 100644 --- a/pcbnew/ioascii.cpp +++ b/pcbnew/ioascii.cpp @@ -815,6 +815,13 @@ int WinEDA_PcbFrame::ReadPcbFile( wxDC* DC, FILE* File, bool Append ) continue; } + if( strnicmp( Line, "$CZONE_OUTLINE", 7 ) == 0 ) + { + ZONE_CONTAINER * zone_descr = new ZONE_CONTAINER(m_Pcb); + zone_descr->ReadDescr( File, &LineNum ); + m_Pcb->m_ZoneDescriptorList.push_back(zone_descr); + } + if( strnicmp( Line, "$MODULE", 7 ) == 0 ) { Module = new MODULE( m_Pcb ); diff --git a/pcbnew/makefile.include b/pcbnew/makefile.include index 6ce77e1da8..b036c45396 100644 --- a/pcbnew/makefile.include +++ b/pcbnew/makefile.include @@ -1,4 +1,4 @@ -EXTRALIBS = ../common/common.a +EXTRALIBS = ../common/common.a ../polygon/lib_polygon.a EXTRACPPFLAGS += -DPCBNEW -fno-strict-aliasing -I./ -Ibitmaps -I../include -I../share\ -I../pcbnew -I../3d-viewer -I../polygon diff --git a/pcbnew/onleftclick.cpp b/pcbnew/onleftclick.cpp index a632da9216..d6c767a4f3 100644 --- a/pcbnew/onleftclick.cpp +++ b/pcbnew/onleftclick.cpp @@ -38,6 +38,11 @@ void WinEDA_PcbFrame::OnLeftClick( wxDC* DC, const wxPoint& MousePos ) switch( DrawStruct->Type() ) { + case TYPEZONE_CONTAINER: + End_Move_Zone_Corner( DC, (ZONE_CONTAINER *) DrawStruct ); + exit = true; + break; + case TYPETRACK: case TYPEVIA: if( DrawStruct->m_Flags & IS_DRAGGED ) diff --git a/pcbnew/onrightclick.cpp b/pcbnew/onrightclick.cpp index 53aece2f9b..d87ff81001 100644 --- a/pcbnew/onrightclick.cpp +++ b/pcbnew/onrightclick.cpp @@ -275,6 +275,41 @@ bool WinEDA_PcbFrame::OnRightClick( const wxPoint& aMousePos, wxMenu* aPopMenu ) _( "Delete edge zone" ), delete_xpm ); break; + case TYPEZONE_CONTAINER: + { + ZONE_CONTAINER * edge_zone = (ZONE_CONTAINER *) item; + if ( edge_zone->m_Flags ) + { + ADD_MENUITEM( aPopMenu, ID_POPUP_PCB_PLACE_ZONE_CORNER, + _( "Place Corner" ), apply_xpm ); + } + else + { + edge_zone->m_CornerSelection = -1; + int index; + if ( (index = edge_zone->HitTestForCorner( GetScreen()->m_Curseur )) >= 0 ) + { + edge_zone->m_CornerSelection = index; + ADD_MENUITEM( aPopMenu, ID_POPUP_PCB_MOVE_ZONE_CORNER, + _( "Move Corner" ), move_xpm ); + ADD_MENUITEM( aPopMenu, ID_POPUP_PCB_DELETE_ZONE_CORNER, + _( "Delete Corner" ), delete_xpm ); + } + else if ( (index = edge_zone->HitTestForEdge( GetScreen()->m_Curseur )) >= 0 ) + { + edge_zone->m_CornerSelection = index; + ADD_MENUITEM( aPopMenu, ID_POPUP_PCB_ADD_ZONE_CORNER, + _( "Create Corner" ), move_xpm ); + } + aPopMenu->AppendSeparator(); + ADD_MENUITEM( aPopMenu, ID_POPUP_PCB_EDIT_ZONE_PARAMS, + _( "Edit Zone Params" ), edit_xpm ); + ADD_MENUITEM( aPopMenu, ID_POPUP_PCB_DELETE_ZONE_CONTAINER, + _( "Delete Zone Outline" ), delete_xpm ); + } + } + break; + case TYPETEXTE: createPopUpMenuForTexts( (TEXTE_PCB*) item, aPopMenu ); break; @@ -286,8 +321,6 @@ bool WinEDA_PcbFrame::OnRightClick( const wxPoint& aMousePos, wxMenu* aPopMenu ) break; case TYPEZONE: - ADD_MENUITEM( aPopMenu, ID_POPUP_PCB_EDIT_ZONE, - _( "Edit Zone" ), edit_xpm ); ADD_MENUITEM( aPopMenu, ID_POPUP_PCB_DELETE_ZONE, _( "Delete Zone" ), delete_xpm ); break; @@ -358,14 +391,6 @@ bool WinEDA_PcbFrame::OnRightClick( const wxPoint& aMousePos, wxMenu* aPopMenu ) ADD_MENUITEM( aPopMenu, ID_POPUP_PCB_FILL_ZONE, _( "Fill zone" ), fill_zone_xpm ); - if( item - && ( (item->Type() == TYPEPAD) - || (item->Type() == TYPETRACK) - || (item->Type() == TYPEVIA) ) ) - { - add_separator = TRUE; - aPopMenu->Append( ID_POPUP_PCB_SELECT_NET_ZONE, _( "Select Net" ) ); - } if( m_Pcb->m_CurrentLimitZone ) { add_separator = TRUE; diff --git a/pcbnew/tracepcb.cpp b/pcbnew/tracepcb.cpp index e56da923d3..6cae3d0636 100644 --- a/pcbnew/tracepcb.cpp +++ b/pcbnew/tracepcb.cpp @@ -8,6 +8,8 @@ * Routines d'affichage grille, Boite de coordonnees, Curseurs, marqueurs ... */ +#include + #include "fctsys.h" #include "gr_basic.h" @@ -18,9 +20,7 @@ #include "protos.h" -/* Routines Locales : */ - -/* Variables Locales */ +using namespace std; /**********************************************************************/ @@ -185,6 +185,12 @@ void WinEDA_PcbFrame::Trace_Pcb( wxDC* DC, int mode ) Trace_DrawSegmentPcb( DrawPanel, DC, segment, mode ); } + for( unsigned ii = 0; ii < m_Pcb->m_ZoneDescriptorList.size(); ii++ ) + { + ZONE_CONTAINER* edge_zone = m_Pcb->m_ZoneDescriptorList[ii]; + edge_zone->Draw( DrawPanel, DC, wxPoint(0,0), mode); + } + DrawGeneralRatsnest( DC ); m_CurrentScreen->ClrRefreshReq(); diff --git a/pcbnew/zones_by_polygon.cpp b/pcbnew/zones_by_polygon.cpp index 58cf883c7e..4eebd190c7 100644 --- a/pcbnew/zones_by_polygon.cpp +++ b/pcbnew/zones_by_polygon.cpp @@ -1,21 +1,27 @@ ///////////////////////////////////////////////////////////////////////////// // Name: zones_by_polygon.cpp -// Purpose: -// Author: jean-pierre Charras -// Modified by: -// Created: 25/01/2006 11:35:19 -// RCS-ID: -// Copyright: GNU License // Licence: GNU License ///////////////////////////////////////////////////////////////////////////// -// Generated by DialogBlocks (unregistered), 25/01/2006 11:35:19 - #if defined (__GNUG__) && !defined (NO_GCC_PRAGMA) #pragma implementation "dialog_zones_by_polygon.h" #endif +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +using namespace std; + #include "fctsys.h" #include "gr_basic.h" @@ -28,40 +34,29 @@ #include "protos.h" -// For compilers that support precompilation, includes "wx/wx.h". -#include "wx/wxprec.h" - -#ifdef __BORLANDC__ -#pragma hdrstop -#endif - -#ifndef WX_PRECOMP -#include "wx/wx.h" -#endif - -////@begin includes -////@end includes - - -////@begin XPM images -////@end XPM images - /* Imported functions */ void Build_Zone( WinEDA_PcbFrame* frame, wxDC* DC, int net_code, bool Zone_Exclude_Pads, bool Zone_Create_Thermal_Relief ); /* Local functions */ -static void Display_Zone_Netname( WinEDA_PcbFrame* frame ); -static void Exit_Zones( WinEDA_DrawPanel* Panel, wxDC* DC ); -static void Show_Zone_Edge_While_MoveMouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase ); +// Outile creation: +static void Abort_Zone_Create_Outline( WinEDA_DrawPanel* Panel, wxDC* DC ); +static void Show_New_Zone_Edge_While_Move_Mouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase ); +// Corner moving +static void Abort_Zone_Move_Corner( WinEDA_DrawPanel* Panel, wxDC* DC ); +static void Show_Zone_Corner_While_Move_Mouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase ); /* Local variables */ static bool Zone_45_Only = FALSE; static bool Zone_Exclude_Pads = TRUE; static bool s_Zone_Create_Thermal_Relief = TRUE; -static int s_Zone_Layer; // Layer used to put the current zone -static int s_NetcodeSelection; // Net code selection for the current zone +static int s_Zone_Layer; // Layer used to create the current zone +static int s_Zone_Hatching; // Option to show the zone area (outlines only, short hatches or full hatches +static int s_NetcodeSelection; // Net code selection for the current zone +static wxPoint s_CornerInitialPosition; // Used to abort a move corner command +static bool s_CornerIsNew; // Used to abort a move corner command (if it is a new corner, it must be deleted) +// key used to store net sort option in config file : #define ZONE_NET_SORT_OPTION_KEY wxT("Zone_NetSort_Opt") enum zone_cmd { @@ -72,55 +67,6 @@ enum zone_cmd { #include "dialog_zones_by_polygon.cpp" -/**************************************************************/ -void WinEDA_PcbFrame::Edit_Zone_Width( wxDC* DC, SEGZONE* aZone ) -/**************************************************************/ - -/* Edite (change la largeur des segments) la zone Zone. - * La zone est constituee des segments zones de meme TimeStamp - */ -{ - bool modify = FALSE; - double f_new_width; - int w_tmp; - wxString Line; - wxString Msg( _( "New zone segment width: " ) ); - - if( aZone == NULL ) - return; - - f_new_width = To_User_Unit( g_UnitMetric, aZone->m_Width, GetScreen()->GetInternalUnits() ); - - Line.Printf( wxT( "%.4f" ), f_new_width ); - - Msg += g_UnitMetric ? wxT( "(mm)" ) : wxT( "(\")" ); - if( Get_Message( Msg, Line, this ) != 0 ) - return; - - w_tmp = g_DesignSettings.m_CurrentTrackWidth; - Line.ToDouble( &f_new_width ); - - g_DesignSettings.m_CurrentTrackWidth = From_User_Unit( g_UnitMetric, - f_new_width, GetScreen( - )->GetInternalUnits() ); - - for( SEGZONE* zone = m_Pcb->m_Zone; zone; zone = zone->Next() ) - { - if( zone->m_TimeStamp == aZone->m_TimeStamp ) - { - modify = TRUE; - Edit_TrackSegm_Width( DC, zone ); - } - } - - g_DesignSettings.m_CurrentTrackWidth = w_tmp; - if( modify ) - { - GetScreen()->SetModify(); - DrawPanel->Refresh(); - } -} - /**********************************************************/ void WinEDA_PcbFrame::Delete_Zone( wxDC* DC, SEGZONE* aZone ) @@ -199,103 +145,12 @@ EDGE_ZONE* WinEDA_PcbFrame::Del_SegmEdgeZone( wxDC* DC, EDGE_ZONE* edge_zone ) } -/*********************************************/ -void WinEDA_PcbFrame::CaptureNetName( wxDC* DC ) -/*********************************************/ - -/* routine permettant de capturer le nom net net (netcode) d'un pad - * ou d'une piste pour l'utiliser comme netcode de zone - */ -{ - D_PAD* pt_pad = 0; - TRACK* adrpiste; - MODULE* Module; - int masquelayer = g_TabOneLayerMask[GetScreen()->m_Active_Layer]; - int netcode; - - netcode = -1; - MsgPanel->EraseMsgBox(); - adrpiste = Locate_Pistes( m_Pcb->m_Track, masquelayer, CURSEUR_OFF_GRILLE ); - if( adrpiste == NULL ) - { - pt_pad = Locate_Any_Pad( m_Pcb, CURSEUR_OFF_GRILLE ); - - if( pt_pad ) /* Verif qu'il est bien sur la couche active */ - { - Module = (MODULE*) pt_pad->m_Parent; - pt_pad = Locate_Pads( Module, g_TabOneLayerMask[GetScreen()->m_Active_Layer], - CURSEUR_OFF_GRILLE ); - } - if( pt_pad ) - { - pt_pad->Display_Infos( this ); - netcode = pt_pad->GetNet(); - } - } - else - { - adrpiste->Display_Infos( this ); - netcode = adrpiste->GetNet(); - } - - // Mise en surbrillance du net - if( g_HightLigt_Status ) - Hight_Light( DC ); - - g_HightLigth_NetCode = netcode; - if( g_HightLigth_NetCode >= 0 ) - { - Hight_Light( DC ); - } - - /* Affichage du net selectionne pour la zone a tracer */ - Display_Zone_Netname( this ); -} - - -/*******************************************************/ -static void Display_Zone_Netname( WinEDA_PcbFrame* frame ) -/*******************************************************/ - -/* - * Affiche le net_code et le nom de net couramment selectionne - */ -{ - EQUIPOT* pt_equipot; - wxString line; - - pt_equipot = frame->m_Pcb->m_Equipots; - - if( g_HightLigth_NetCode > 0 ) - { - for( ; pt_equipot != NULL; pt_equipot = (EQUIPOT*) pt_equipot->Pnext ) - { - if( pt_equipot->GetNet() == g_HightLigth_NetCode ) - break; - } - - if( pt_equipot ) - { - line.Printf( wxT( "Zone: Net[%d] <%s>" ), g_HightLigth_NetCode, - pt_equipot->m_Netname.GetData() ); - } - else - line.Printf( wxT( "Zone: NetCode[%d], Equipot not found" ), - g_HightLigth_NetCode ); - } - - line = _( "Zone: No net selected" ); - - frame->Affiche_Message( line ); -} - - -/********************************************************/ -static void Exit_Zones( WinEDA_DrawPanel* Panel, wxDC* DC ) -/********************************************************/ +/*************************************************************************/ +static void Abort_Zone_Create_Outline( WinEDA_DrawPanel* Panel, wxDC* DC ) +/*************************************************************************/ /** - * Function Exit_Zones + * Function Abort_Zone_Create_Outline * cancels the Begin_Zone state if at least one EDGE_ZONE has been created. */ { @@ -326,9 +181,6 @@ void WinEDA_BasePcbFrame::DelLimitesZone( wxDC* DC, bool Redraw ) if( m_Pcb->m_CurrentLimitZone == NULL ) return; - if( !IsOK( this, _( "Delete Current Zone Edges" ) ) ) - return; - // erase the old zone border, one segment at a time for( segment = m_Pcb->m_CurrentLimitZone; segment; segment = next ) { @@ -345,13 +197,109 @@ void WinEDA_BasePcbFrame::DelLimitesZone( wxDC* DC, bool Redraw ) SetCurItem( NULL ); } +/*******************************************************************************************************/ +void WinEDA_PcbFrame::Start_Move_Zone_Corner( wxDC* DC , ZONE_CONTAINER * zone_container, + int corner_id, bool IsNewCorner ) +/*******************************************************************************************************/ +/** + * Function Start_Move_Zone_Corner + * Initialise parametres to move an existing corner of a zone. + * if IsNewCorner is true, the Abort_Zone_Move_Corner will remove this corner, if called + */ +{ + /* Show the Net */ + if( (g_HightLigth_NetCode > 0) && (g_HightLigth_NetCode != s_NetcodeSelection) ) + { + Hight_Light( DC ); // Remove old hightlight selection + } + + g_HightLigth_NetCode = s_NetcodeSelection; + if ( ! g_HightLigt_Status ) + Hight_Light( DC ); + zone_container->m_Flags = IN_EDIT; + DrawPanel->ManageCurseur = Show_Zone_Corner_While_Move_Mouse; + DrawPanel->ForceCloseManageCurseur = Abort_Zone_Move_Corner; + s_CornerInitialPosition.x = zone_container->GetX(corner_id); + s_CornerInitialPosition.y = zone_container->GetY(corner_id); + s_CornerIsNew = IsNewCorner; +} + +/***************************************************************************************/ +void WinEDA_PcbFrame::End_Move_Zone_Corner( wxDC* DC , ZONE_CONTAINER * zone_container ) +/****************************************************************************************/ +/** + * Function End_Move_Zone_Corner + * Terminates a move corner in a zone outline + */ +{ + zone_container->m_Flags = 0; + DrawPanel->ManageCurseur = NULL; + DrawPanel->ForceCloseManageCurseur = NULL; + zone_container->Draw(DrawPanel, DC, wxPoint(0,0), GR_OR); +} + + +/**************************************************************/ +void Abort_Zone_Move_Corner( WinEDA_DrawPanel* Panel, wxDC* DC ) +/**************************************************************/ +/** + * Function Abort_Zone_Move_Corner + * cancels the Begin_Zone state if at least one EDGE_ZONE has been created. + */ +{ + WinEDA_PcbFrame* pcbframe = (WinEDA_PcbFrame*) Panel->m_Parent; + ZONE_CONTAINER* zone_container = (ZONE_CONTAINER*) pcbframe->GetCurItem(); + + zone_container->Draw(Panel, DC, wxPoint(0,0), GR_XOR); + + if ( s_CornerIsNew ) + { + zone_container->DeleteCorner( zone_container->m_CornerSelection ); + } + else + { + wxPoint pos = s_CornerInitialPosition; + zone_container->MoveCorner( zone_container->m_CornerSelection, pos.x, pos.y ); + } + zone_container->Draw(Panel, DC, wxPoint(0,0), GR_XOR); + + Panel->ManageCurseur = NULL; + Panel->ForceCloseManageCurseur = NULL; + pcbframe->SetCurItem( NULL ); + zone_container->m_Flags = 0; +} + + +/**************************************************************************************/ +void Show_Zone_Corner_While_Move_Mouse( WinEDA_DrawPanel* Panel, wxDC* DC, bool erase ) +/**************************************************************************************/ +/* Redraws the zone outline when moving a corner according to the cursor position + */ +{ + WinEDA_PcbFrame* pcbframe = (WinEDA_PcbFrame*) Panel->m_Parent; + ZONE_CONTAINER* zone_container = (ZONE_CONTAINER*) pcbframe->GetCurItem(); + +// if( erase ) /* Undraw edge in old position*/ + { + zone_container->Draw(Panel, DC, wxPoint(0,0), GR_XOR); + } + + wxPoint pos = pcbframe->GetScreen()->m_Curseur; + zone_container->MoveCorner( zone_container->m_CornerSelection, pos.x, pos.y ); + zone_container->Draw(Panel, DC, wxPoint(0,0), GR_XOR); +} + + + +/*************************************************/ +EDGE_ZONE* WinEDA_PcbFrame::Begin_Zone( wxDC* DC ) +/*************************************************/ /** * Function Begin_Zone * either initializes the first segment of a new zone, or adds an * intermediate segment. */ -EDGE_ZONE* WinEDA_PcbFrame::Begin_Zone( wxDC* DC ) { EDGE_ZONE* oldedge; EDGE_ZONE* newedge = NULL; @@ -401,13 +349,12 @@ EDGE_ZONE* WinEDA_PcbFrame::Begin_Zone( wxDC* DC ) m_Pcb->m_CurrentLimitZone = newedge; - DrawPanel->ManageCurseur = Show_Zone_Edge_While_MoveMouse; - DrawPanel->ForceCloseManageCurseur = Exit_Zones; + DrawPanel->ManageCurseur = Show_New_Zone_Edge_While_Move_Mouse; + DrawPanel->ForceCloseManageCurseur = Abort_Zone_Create_Outline; } // edge in progress: - else /* piste en cours : les coord du point d'arrivee ont ete mises - * a jour par la routine Show_Zone_Edge_While_MoveMouse*/ - { + else + { /* edge in progress : the ending point coordinate was set by Show_New_Zone_Edge_While_Move_Mouse */ if( oldedge->m_Start != oldedge->m_End ) { oldedge->m_Flags &= ~(IS_NEW | IS_MOVED); @@ -433,7 +380,9 @@ void WinEDA_PcbFrame::End_Zone( wxDC* DC ) /*********************************************/ /* - * Routine de fin de trace d'une zone (succession de segments) + * Terminates an edge zone creation + * Close the current edge zone considered as a polygon + * put it in the main list m_Pcb->m_ZoneDescriptorList (a vector) */ { EDGE_ZONE* edge; @@ -442,8 +391,9 @@ void WinEDA_PcbFrame::End_Zone( wxDC* DC ) { Begin_Zone( DC ); - /* le dernier point genere est de longueur tj nulle donc inutile. */ - /* il sera raccorde au point de depart */ + /* The last segment is a stub: its lenght is 0. + * Use it to close the polygon by setting its ending point coordinate = start point of first segment + */ edge = m_Pcb->m_CurrentLimitZone; edge->m_Flags &= ~(IS_NEW | IS_MOVED); @@ -466,14 +416,46 @@ void WinEDA_PcbFrame::End_Zone( wxDC* DC ) DrawPanel->ManageCurseur = NULL; DrawPanel->ForceCloseManageCurseur = NULL; + + /* Put edges in list */ + ZONE_CONTAINER * polygon = new ZONE_CONTAINER( m_Pcb ); + polygon->SetLayer( GetScreen()->m_Active_Layer ); + polygon->SetNet( g_HightLigth_NetCode ); + polygon->m_TimeStamp = GetTimeStamp(); + + EQUIPOT* net = m_Pcb->FindNet( g_HightLigth_NetCode ); + if ( net ) polygon->m_Netname = net->m_Netname; + edge = m_Pcb->m_CurrentLimitZone; + polygon->Start( GetScreen()->m_Active_Layer, 0, NULL, + edge->m_Start.x, edge->m_Start.y, + s_Zone_Hatching ); + edge = edge->Next(); + while( edge ) + { + polygon->AppendCorner( edge->m_Start.x, edge->m_Start.y ); + edge = edge->Next(); + } + polygon->Close(); // Close the current corner list + polygon->Hatch(); + + m_Pcb->m_ZoneDescriptorList.push_back(polygon); + + /* Remove the current temporary list */ + DelLimitesZone( DC, TRUE ); + + /* Redraw the real edge zone */ + polygon->CPolyLine::Draw( ); // Build the line list + polygon->Draw( DrawPanel, DC, wxPoint(0,0), GR_OR ); + + GetScreen()->SetModify(); } /******************************************************************************************/ -static void Show_Zone_Edge_While_MoveMouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase ) +static void Show_New_Zone_Edge_While_Move_Mouse( WinEDA_DrawPanel* panel, wxDC* DC, bool erase ) /******************************************************************************************/ -/* redessin du contour de la piste lors des deplacements de la souris +/* Redraws the edge zone when moving mouse */ { EDGE_ZONE* edge; @@ -483,8 +465,7 @@ static void Show_Zone_Edge_While_MoveMouse( WinEDA_DrawPanel* panel, wxDC* DC, b if( pcbframe->m_Pcb->m_CurrentLimitZone == NULL ) return; - /* efface ancienne position si elle a ete deja dessinee */ - if( erase ) + if( erase ) /* Undraw edge in old position*/ { edge = pcbframe->m_Pcb->m_CurrentLimitZone; @@ -494,23 +475,22 @@ static void Show_Zone_Edge_While_MoveMouse( WinEDA_DrawPanel* panel, wxDC* DC, b } } - /* mise a jour de la couche */ + /* Reinit layer (which can be changed) */ for( edge = pcbframe->m_Pcb->m_CurrentLimitZone; edge; edge = edge->Next() ) { edge->SetLayer( pcbframe->GetScreen()->m_Active_Layer ); } - /* dessin de la nouvelle piste : mise a jour du point d'arrivee */ + /* Redraw the curent edge in its new position */ currentEdge = pcbframe->m_Pcb->m_CurrentLimitZone; if( Zone_45_Only ) { - // Calcul de l'extremite de la piste pour orientations permises: - // horiz,vertical ou 45 degre + // calculate the new position as allowed currentEdge->m_End = pcbframe->GetScreen()->m_Curseur; Calcule_Coord_Extremite_45( currentEdge->m_Start.x, currentEdge->m_Start.y, ¤tEdge->m_End.x, ¤tEdge->m_End.y ); } - else /* ici l'angle d'inclinaison est quelconque */ + else /* all orientations are allowed */ { currentEdge->m_End = pcbframe->GetScreen()->m_Curseur; } @@ -522,22 +502,44 @@ static void Show_Zone_Edge_While_MoveMouse( WinEDA_DrawPanel* panel, wxDC* DC, b } -/**********************************************/ -void WinEDA_PcbFrame::Fill_Zone( wxDC* DC ) -/**********************************************/ - -/** Function Fill_Zone() - * Init the zone filling - * If a zone edge is found, it is used. - * Otherwise the whole board is filled by the zone - * The zone edge is a frontier, and can be complex. So non filled zones can be achieved - * The zone is put on the active layer - * If a net is hightlighted, the zone will be attached to this net - * The filling start from a starting point. - * If a net is selected, all tracks attached to this net are also starting points +/***********************************************************************************/ +void WinEDA_PcbFrame::Edit_Zone_Params( wxDC* DC , ZONE_CONTAINER * zone_container ) +/***********************************************************************************/ +/** + * Function Edit_Zone_Params + * Edit params (layer, clearance, ...) for a zone outline + */ +{ + DrawPanel->m_IgnoreMouseEvents = TRUE; + WinEDA_ZoneFrame* frame = new WinEDA_ZoneFrame( this, zone_container ); + + int diag = frame->ShowModal(); + frame->Destroy(); + DrawPanel->MouseToCursorSchema(); + DrawPanel->m_IgnoreMouseEvents = FALSE; + + if( diag == ZONE_ABORT ) + return; + + zone_container->Draw( DrawPanel, DC, wxPoint(0,0), GR_XOR ); + + zone_container->SetLayer( s_Zone_Layer ); + zone_container->SetNet( s_NetcodeSelection ); + EQUIPOT* net = m_Pcb->FindNet( s_NetcodeSelection ); + if ( net ) zone_container->m_Netname = net->m_Netname; + zone_container->SetHatch(s_Zone_Hatching); + + zone_container->Draw( DrawPanel, DC, wxPoint(0,0), GR_OR ); +} + +/***************************************************************************/ +void WinEDA_PcbFrame::Fill_Zone( wxDC* DC, ZONE_CONTAINER * zone_container ) +/***************************************************************************/ + +/** Function Fill_Zone() + * Fillst the zone defined in zone_container */ { - EQUIPOT* pt_equipot; wxPoint ZoneStartFill; wxString msg; @@ -548,32 +550,10 @@ void WinEDA_PcbFrame::Fill_Zone( wxDC* DC ) return; } - DrawPanel->m_IgnoreMouseEvents = TRUE; - WinEDA_ZoneFrame* frame = new WinEDA_ZoneFrame( this ); - - int diag = frame->ShowModal(); - frame->Destroy(); - DrawPanel->MouseToCursorSchema(); - DrawPanel->m_IgnoreMouseEvents = FALSE; - - if( diag == ZONE_ABORT ) - return; - - // set all the EDGE_ZONEs to the currently active layer and redraw them - // on that layer. - GetScreen()->m_Active_Layer = s_Zone_Layer; - EDGE_ZONE* PtLim = m_Pcb->m_CurrentLimitZone; - for( ; PtLim != NULL; PtLim = PtLim->Next() ) - { - Trace_DrawSegmentPcb( DrawPanel, DC, PtLim, GR_XOR ); - PtLim->SetLayer( s_Zone_Layer ); - Trace_DrawSegmentPcb( DrawPanel, DC, PtLim, GR_XOR ); - } - /* Show the Net */ if( (g_HightLigth_NetCode > 0) && (g_HightLigth_NetCode != s_NetcodeSelection) ) { - Hight_Light( DC ); // Remoive old hightlight selection + Hight_Light( DC ); // Remove old hightlight selection } g_HightLigth_NetCode = s_NetcodeSelection; @@ -582,14 +562,17 @@ void WinEDA_PcbFrame::Fill_Zone( wxDC* DC ) if( g_HightLigth_NetCode > 0 ) { - pt_equipot = m_Pcb->FindNet( g_HightLigth_NetCode ); - if( pt_equipot == NULL ) + EQUIPOT* net = m_Pcb->FindNet( g_HightLigth_NetCode ); + if( net == NULL ) { if( g_HightLigth_NetCode > 0 ) + { DisplayError( this, wxT( "Unable to find Net name" ) ); + return; + } } else - msg = pt_equipot->m_Netname; + msg = net->m_Netname; } else msg = _( "No Net" ); diff --git a/polygon/PolyLine.cpp b/polygon/PolyLine.cpp new file mode 100644 index 0000000000..52a5027646 --- /dev/null +++ b/polygon/PolyLine.cpp @@ -0,0 +1,1888 @@ +// PolyLine.cpp ... implementation of CPolyLine class + +// from FreePCB. +// Adaptation for kicad +// +using namespace std; + +#include +#include + +#include "PolyLine.h" + +#define to_int(x) (int)round((x)) + +/* Stuff to compile PolyLine.cpp, used in std::vector as CArray. does not work. must be redesigned, only for test */ +#define SetSize reserve + + +#define pi 3.14159265359 +#define DENOM 10 // to use mils for php clipping +//#define DENOM 1 // to use internal units for php clipping + +// dl is a pointer to CDisplayList for drawing graphic elements +// if dl = NULL, doesn't draw anything but can still hold data +// +CPolyLine::CPolyLine( CDisplayList * dl ) +{ + m_dlist = dl; + m_HatchStyle = 0; + m_sel_box = 0; + m_gpc_poly = new gpc_polygon; + m_gpc_poly->num_contours = 0; + m_php_poly = new polygon; +} + +CPolyLine::CPolyLine() +{ + m_dlist = NULL; + m_HatchStyle = 0; + m_sel_box = 0; + m_gpc_poly = new gpc_polygon; + m_gpc_poly->num_contours = 0; + m_php_poly = new polygon; +} + +// destructor, removes display elements +// +CPolyLine::~CPolyLine() +{ + Undraw(); + FreeGpcPoly(); + delete m_gpc_poly; + delete m_php_poly; +} + +// 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 +// Returns number of external contours, or -1 if error +// +int CPolyLine::NormalizeWithGpc( std::vector * pa, BOOL bRetainArcs ) +{ + std::vector arc_array; + + if( bRetainArcs ) + MakeGpcPoly( -1, &arc_array ); + else + MakeGpcPoly( -1, NULL ); + + 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, m_Width, m_sel_box, 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->SetSize(n_ext_cont); // put in array + (*pa)[n_ext_cont-1] = poly; + 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, m_Width, m_sel_box, x, y, m_HatchStyle ); + else + poly->AppendCorner( x, y, STRAIGHT, FALSE ); + } + poly->Close( STRAIGHT, FALSE ); + 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 ) + ASSERT(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; +} + +// 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; +} + +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; +} + +// Use the php clipping lib to clip this poly against 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(); + + if( p ) + { + // now screw with the PolyLine + corner.clear(); + side_style.clear(); + do + { + vertex * v = p->getFirst(); + Start( m_layer, m_Width, m_sel_box, + 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(); +} + +// 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 +// +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 + + // 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->SetSize(0); + 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 ) + { + arc_array->SetSize(iarc+1); + (*arc_array)[iarc].style = style; + (*arc_array)[iarc].n_steps = n; + (*arc_array)[iarc].xi = x1; + (*arc_array)[iarc].yi = y1; + (*arc_array)[iarc].xf = x2; + (*arc_array)[iarc].yf = y2; + iarc++; + } + for( int is=0; isvertex[ivtx].x = x; + g_v_list->vertex[ivtx].y = y; + ivtx++; + } + } + } + if( n_vertices != ivtx ) + ASSERT(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() +{ + 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; +} + + +// 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; + + // 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 + } + + // find arcs and replace them + BOOL bFound; + int arc_start; + int arc_end; + 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 +// +// if sel_box = 0, don't create selection elements at all +// +// if polyline is board outline, enter with: +// id.type = ID_BOARD +// id.st = ID_BOARD_OUTLINE +// id.i = 0 +// ptr = NULL +// +// if polyline is copper area, enter with: +// id.type = ID_NET; +// id.st = ID_AREA +// id.i = index to area +// ptr = pointer to net +// +void CPolyLine::Start( int layer, int w, int sel_box, int x, int y, + int hatch ) +{ + m_layer = layer; + m_Width = w; + m_sel_box = sel_box; + m_HatchStyle = hatch; + CPolyPt poly_pt( x, y ); + poly_pt.end_contour = FALSE; + + corner.push_back(poly_pt); + side_style.push_back(0); +} + +// add a corner to unclosed polyline +// +void CPolyLine::AppendCorner( int x, int y, int style, BOOL bDraw ) +{ + Undraw(); + CPolyPt poly_pt( x, y ); + poly_pt.end_contour = FALSE; + + // 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 + ASSERT(0); + if( bDraw ) + Draw(); +} + +// close last polyline contour +// +void CPolyLine::Close( int style, BOOL bDraw ) +{ + if( GetClosed() ) + ASSERT(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(); +} + +// 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(); + + 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(); +} + +void CPolyLine::RemoveContour( int icont ) +{ + Undraw(); + int istart = GetContourStart( icont ); + int iend = GetContourEnd( icont ); + + if( icont == 0 && GetNumContours() == 1 ) + { + // remove the only contour + ASSERT(0); + } + else if( icont == GetNumContours()-1 ) + { + // remove last contour + corner.erase( corner.begin() + icont, corner.end() ); + side_style.erase( side_style.begin() + icont, 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(); +} + +/** Function InsertCorner + * insert a new corner between two existing corners + * @param ic = index for the insertion point: the corner is inserted AFTER ic + * @param x, y = coordinates corner to insert + */ +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 polyline by removing all graphic elements from display list +// +void CPolyLine::Undraw() +{ + if( m_dlist && bDrawn ) + { + // remove display elements, if present + for( unsigned i=0; iRemove( dl_side[i] ); + for( unsigned i=0; iRemove( dl_side_sel[i] ); + for( unsigned i=0; iRemove( dl_corner_sel[i] ); + + // remove pointers + dl_side.clear(); + dl_side_sel.clear(); + dl_corner_sel.clear(); + } + + 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( CDisplayList * dl ) +{ + + // first, undraw if necessary + if( bDrawn ) + Undraw(); + + // use new display list if provided + if( dl ) + m_dlist = dl; + +#if 0 + int i_start_contour = 0; + if( m_dlist ) + { + // set up std::vectors + dl_side.SetSize( corner.size() ); + if( m_sel_box ) + { + dl_side_sel.SetSize( corner.size() ); + dl_corner_sel.SetSize( corner.size() ); + } + else + { + dl_side_sel.clear(); + dl_corner_sel.clear(); + } + // now draw elements + for( int ic=0; icAddSelector( m_id, m_ptr, m_layer, DL_HOLLOW_RECT, + 1, 0, 0, xi-m_sel_box, yi-m_sel_box, + xi+m_sel_box, yi+m_sel_box, 0, 0 ); + } + if( ic<(corner.size()-1) || corner[ic].end_contour ) + { + // draw side + if( xi == xf || yi == yf ) + { + // if endpoints not angled, make side STRAIGHT + side_style[ic] = STRAIGHT; + } + int g_type = DL_LINE; + if( side_style[ic] == STRAIGHT ) + g_type = DL_LINE; + else if( side_style[ic] == ARC_CW ) + g_type = DL_ARC_CW; + else if( side_style[ic] == ARC_CCW ) + g_type = DL_ARC_CCW; + m_id.sst = ID_SIDE; + dl_side[ic] = m_dlist->Add( m_id, m_ptr, m_layer, g_type, + 1, m_w, 0, xi, yi, xf, yf, 0, 0 ); + if( m_sel_box ) + { + m_id.sst = ID_SEL_SIDE; + dl_side_sel[ic] = m_dlist->AddSelector( m_id, m_ptr, m_layer, g_type, + 1, m_w, 0, xi, yi, xf, yf, 0, 0 ); + } + } + } +// if( m_HatchStyle ) +// Hatch(); + } +#endif + Hatch(); + bDrawn = TRUE; +} + + +// start dragging new corner to be inserted into side, make side and hatching invisible +// +void CPolyLine::StartDraggingToInsertCorner( CDC * pDC, int ic, int x, int y ) +{ + if( !m_dlist ) + ASSERT(0); + + int icont = GetContour( ic ); + int istart = GetContourStart( icont ); + int iend = GetContourEnd( icont ); + int post_c; + + if( ic == iend ) + post_c = istart; + else + post_c = ic + 1; + int xi = corner[ic].x; + int yi = corner[ic].y; + int xf = corner[post_c].x; + int yf = corner[post_c].y; + m_dlist->StartDraggingLineVertex( pDC, x, y, xi, yi, xf, yf, + LAY_SELECTION, LAY_SELECTION, 1, 1, DSS_STRAIGHT, DSS_STRAIGHT, + 0, 0, 0, 0, 1 ); + m_dlist->CancelHighLight(); + m_dlist->Set_visible( dl_side[ic], 0 ); +/* for( int ih=0; ihSet_visible( dl_hatch[ih], 0 ); +*/ +} + +// cancel dragging inserted corner, make side and hatching visible again +// +void CPolyLine::CancelDraggingToInsertCorner( int ic ) +{ + if( !m_dlist ) + ASSERT(0); + + int post_c; + if( ic == (int)(corner.size()-1) ) + post_c = 0; + else + post_c = ic + 1; + m_dlist->StopDragging(); +/* m_dlist->Set_visible( dl_side[ic], 1 ); + for( int ih=0; ihSet_visible( dl_hatch[ih], 1 ); +*/ +} + +// start dragging corner to new position, make adjacent sides and hatching invisible +// +void CPolyLine::StartDraggingToMoveCorner( CDC * pDC, int ic, int x, int y ) +{ + if( !m_dlist ) + ASSERT(0); + + // see if corner is the first or last corner of an open contour + int icont = GetContour( ic ); + int istart = GetContourStart( icont ); + int iend = GetContourEnd( icont ); + if( !GetClosed() + && icont == GetNumContours() - 1 + && (ic == istart || ic == iend) ) + { + // yes + int style, xi, yi, iside; + if( ic == istart ) + { + // first corner + iside = ic; + xi = GetX( ic+1 ); + yi = GetY( ic+1 ); + style = GetSideStyle( iside ); + // reverse arc since we are drawing from corner 1 to 0 + if( style == CPolyLine::ARC_CW ) + style = CPolyLine::ARC_CCW; + else if( style == CPolyLine::ARC_CCW ) + style = CPolyLine::ARC_CW; + } + else + { + // last corner + iside = ic - 1; + xi = GetX( ic-1 ); + yi = GetY( ic-1); + style = GetSideStyle( iside ); + } + m_dlist->StartDraggingArc( pDC, style, GetX(ic), GetY(ic), xi, yi, LAY_SELECTION, 1, 1 ); + m_dlist->CancelHighLight(); + m_dlist->Set_visible( dl_side[iside], 0 ); +/* for( int ih=0; ihSet_visible( dl_hatch[ih], 0 ); +*/ + } + else + { + // no + // get indexes for preceding and following corners + int pre_c, post_c; + int poly_side_style1, poly_side_style2; + int style1, style2; + if( ic == istart ) + { + pre_c = iend; + post_c = istart+1; + poly_side_style1 = side_style[iend]; + poly_side_style2 = side_style[istart]; + } + else if( ic == iend ) + { + // last side + pre_c = ic-1; + post_c = istart; + poly_side_style1 = side_style[ic-1]; + poly_side_style2 = side_style[ic]; + } + else + { + pre_c = ic-1; + post_c = ic+1; + poly_side_style1 = side_style[ic-1]; + poly_side_style2 = side_style[ic]; + } + if( poly_side_style1 == STRAIGHT ) + style1 = DSS_STRAIGHT; + else if( poly_side_style1 == ARC_CW ) + style1 = DSS_ARC_CW; + else if( poly_side_style1 == ARC_CCW ) + style1 = DSS_ARC_CCW; + if( poly_side_style2 == STRAIGHT ) + style2 = DSS_STRAIGHT; + else if( poly_side_style2 == ARC_CW ) + style2 = DSS_ARC_CW; + else if( poly_side_style2 == ARC_CCW ) + style2 = DSS_ARC_CCW; + int xi = corner[pre_c].x; + int yi = corner[pre_c].y; + int xf = corner[post_c].x; + int yf = corner[post_c].y; + m_dlist->StartDraggingLineVertex( pDC, x, y, xi, yi, xf, yf, + LAY_SELECTION, LAY_SELECTION, 1, 1, style1, style2, + 0, 0, 0, 0, 1 ); + m_dlist->CancelHighLight(); + m_dlist->Set_visible( dl_side[pre_c], 0 ); + m_dlist->Set_visible( dl_side[ic], 0 ); +/* for( int ih=0; ihSet_visible( dl_hatch[ih], 0 ); +*/ } +} + +// cancel dragging corner to new position, make sides and hatching visible again +// + +// highlight side by drawing line over it +// +void CPolyLine::HighlightSide( int is ) +{ + if( !m_dlist ) + ASSERT(0); + if( GetClosed() && is >= (int)corner.size() ) + return; + if( !GetClosed() && is >= (int)(corner.size()-1) ) + return; + + int style; + if( side_style[is] == CPolyLine::STRAIGHT ) + style = DL_LINE; + else if( side_style[is] == CPolyLine::ARC_CW ) + style = DL_ARC_CW; + else if( side_style[is] == CPolyLine::ARC_CCW ) + style = DL_ARC_CCW; + m_dlist->HighLight( style, + m_dlist->Get_x( dl_side_sel[is] ), + m_dlist->Get_y( dl_side_sel[is] ), + m_dlist->Get_xf( dl_side_sel[is] ), + m_dlist->Get_yf( dl_side_sel[is] ), + m_dlist->Get_w( dl_side_sel[is]) ); +} + + +int CPolyLine::GetX( int ic ) +{ + return corner[ic].x; +} + +int CPolyLine::GetY( int ic ) +{ + return corner[ic].y; +} + +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 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; + int offset; + if( layer < (LAY_TOP_COPPER+2) ) + offset = 0; + else if( layer < (LAY_TOP_COPPER+4) ) + offset = spacing/2; + else if( layer < (LAY_TOP_COPPER+6) ) + offset = spacing/4; + else if( layer < (LAY_TOP_COPPER+8) ) + offset = 3*spacing/4; + else if( layer < (LAY_TOP_COPPER+10) ) + offset = 1*spacing/8; + else if( layer < (LAY_TOP_COPPER+12) ) + offset = 3*spacing/8; + else if( layer < (LAY_TOP_COPPER+14) ) + offset = 5*spacing/8; + else if( layer < (LAY_TOP_COPPER+16) ) + offset = 7*spacing/8; + else + ASSERT(0); + 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; + 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; + } + } + + // 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() ) + ASSERT(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; +} + +// test to see if a point is inside polyline contour +// +BOOL CPolyLine::TestPointInsideContour( int icont, int x, int y ) +{ + if( icont >= GetNumContours() ) + return FALSE; + + enum { MAXPTS = 100 }; + if( !GetClosed() ) + ASSERT(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++; + ASSERT( npts x ) + ncount++; + } + if( ncount%2 ) + return TRUE; + else + return FALSE; +} + +// Test for intersection of sides +// +int CPolyLine::TestIntersection( CPolyLine * poly ) +{ + if( !GetClosed() ) + ASSERT(0); + if( !poly->GetClosed() ) + ASSERT(0); + for( int ic=0; icGetNumContours(); ic2++ ) + { + int istart2 = poly->GetContourStart(ic2); + int iend2 = poly->GetContourEnd(ic2); + for( int is2=istart2; is2<=iend2; is2++ ) + { + int xf2, yf2; + if( is2 < poly->GetContourEnd(ic2) ) + { + xf2 = poly->GetX(is2+1); + yf2 = poly->GetY(is2+1); + } + else + { + xf2 = poly->GetX(istart2); + yf2 = poly->GetY(istart2); + } + // test for intersection between side and side2 + } + } + } + } + return 0; +} + +// set selection box size +// +void CPolyLine::SetSelBoxSize( int sel_box ) +{ +// Undraw(); + m_sel_box = sel_box; +// Draw(); +} + +// set pointer to display list, and draw into display list +// +void CPolyLine::SetDisplayList( CDisplayList * dl ) +{ + if( m_dlist ) + Undraw(); + m_dlist = dl; + if( m_dlist ) + Draw(); +} + +// copy data from another poly, but don't draw it +// +void CPolyLine::Copy( CPolyLine * src ) +{ + Undraw(); + m_dlist = src->m_dlist; + m_sel_box = src->m_sel_box; + // copy corners + for( unsigned i=0; i< src->corner.size(); i++ ) + corner.push_back(src->corner[i]); + // copy side styles + int nsides = src->GetNumSides(); + side_style.SetSize(nsides); + for( int i=0; iside_style[i]; + // don't copy the Gpc_poly, just clear the old one + FreeGpcPoly(); +} + +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(); +} + + +// Set various parameters: +// 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::SetEndContour( int ic, BOOL 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 * 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, 0, 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 ) +{ + 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; + 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 new file mode 100644 index 0000000000..863196073f --- /dev/null +++ b/polygon/PolyLine.h @@ -0,0 +1,177 @@ +// PolyLine.h ... definition of CPolyLine class +// +// A polyline contains one or more contours, where each contour +// is defined by a list of corners and side-styles +// 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 +// each contour. +// +// When used for copper areas, the first contour is the outer edge +// of the area, subsequent ones are "holes" in the copper. +// +// If a CDisplayList pointer is provided, the polyline can draw itself + +#ifndef POLYLINE_H +#define POLYLINE_H + +#include + +#include "defs-macros.h" + +#include "GenericPolygonClipperLibrary.h" +#include "php_polygon.h" +#include "php_polygon_vertex.h" + +#include "PolyLine2Kicad.h" + + +#include "freepcb_ids.h" +#include "freepcbDisplayList.h" +#include "math_for_graphics.h" + +class CSegment { +public: + int xi, yi, xf, yf; + CSegment() {}; + CSegment(int x0, int y0, int x1, int y1) { + xi = x0; yi = y0; xf = x1; yf = y1; } +}; + +class CArc { +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 + BOOL bFound; +}; + +class CPolyPt +{ +public: + CPolyPt( int qx=0, int qy=0, BOOL qf=FALSE ) + { x=qx; y=qy; end_contour=qf; utility = 0; }; + int x; + int y; + BOOL end_contour; + int utility; +}; + +class CPolyLine +{ +public: + enum { STRAIGHT, ARC_CW, ARC_CCW }; // side styles + enum { NO_HATCH, DIAGONAL_FULL, DIAGONAL_EDGE }; // hatch styles + enum { DEF_SIZE = 50, DEF_ADD = 50 }; // number of array elements to add at a time + + // constructors/destructor + CPolyLine( CDisplayList * dl ); + CPolyLine(); + ~CPolyLine(); + + // functions for modifying polyline + void Start( int layer, int w, int sel_box, int x, int y, + int hatch ); + void AppendCorner( int x, int y, int style = STRAIGHT, BOOL bDraw=TRUE ); + void InsertCorner( int ic, int x, int y ); + void DeleteCorner( int ic, BOOL bDraw=TRUE ); + void MoveCorner( int ic, int x, int y ); + void Close( int style = STRAIGHT, BOOL bDraw=TRUE ); + void RemoveContour( int icont ); + + // drawing functions + void HighlightSide( int is ); + void HighlightCorner( int ic ); + void StartDraggingToInsertCorner( CDC * pDC, int ic, int x, int y); + void StartDraggingToMoveCorner( CDC * pDC, int ic, int x, int y); + void CancelDraggingToInsertCorner( int ic ); + void CancelDraggingToMoveCorner( int ic ); + void Undraw(); + void Draw( CDisplayList * dl = NULL ); + void Hatch(); + void MakeVisible( BOOL visible = TRUE ); + void MoveOrigin( int x_off, int y_off ); + + // misc. functions + CRect GetBounds(); + CRect GetCornerBounds(); + CRect GetCornerBounds( int icont ); + void Copy( CPolyLine * src ); + BOOL TestPointInside( int x, int y ); + BOOL TestPointInsideContour( int icont, int x, int y ); + int TestIntersection( CPolyLine * poly ); + void AppendArc( int xi, int yi, int xf, int yf, int xc, int yc, int num ); + + + // access functions + int GetNumCorners(); + int GetNumSides(); + int GetClosed(); + int GetNumContours(); + int GetContour( int ic ); + int GetContourStart( int icont ); + int GetContourEnd( int icont ); + int GetContourSize( int icont ); + int GetX( int ic ); + int GetY( int ic ); + int GetEndContour( int ic ); + int GetUtility( int ic ){ return corner[ic].utility; }; + void SetUtility( int ic, int utility ){ corner[ic].utility = utility; }; + int GetW(); + int GetSideStyle( int is ); + id GetId(); + int GetSelBoxSize(); + CDisplayList * GetDisplayList(){ return m_dlist; }; + int GetHatch(){ return m_HatchStyle; } + void SetHatch( int hatch ){ Undraw(); m_HatchStyle = hatch; Draw(); }; + void SetX( int ic, int x ); + void SetY( int ic, int y ); + void SetEndContour( int ic, BOOL end_contour ); +// void SetLayer( int layer ); + void SetW( int w ); + void SetSideStyle( int is, int style ); + void SetSelBoxSize( int sel_box ); + void SetDisplayList( CDisplayList * dl ); + + // 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, + 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 ); + + // PHP functions + int MakePhpPoly(); + void FreePhpPoly(); + void ClipPhpPolygon( int php_op, CPolyLine * poly ); + +private: + CDisplayList * m_dlist; // display list + int m_layer; // layer to draw on + int m_Width; // line width + int m_sel_box; // corner selection box width/2 +public: + std::vector corner; // array of points for corners + std::vector side_style; // array of styles for sides +private: + std::vector dl_side; // graphic elements + std::vector dl_side_sel; + std::vector dl_corner_sel; +public: + int m_HatchStyle; // hatch style, see enum above + std::vector m_HatchLines; // hatch lines +private: + gpc_polygon * m_gpc_poly; // polygon in gpc format + polygon * m_php_poly; + BOOL bDrawn; +}; + +#endif // #ifndef POLYLINE_H diff --git a/polygon/PolyLine2Kicad.h b/polygon/PolyLine2Kicad.h new file mode 100644 index 0000000000..c606612771 --- /dev/null +++ b/polygon/PolyLine2Kicad.h @@ -0,0 +1,94 @@ +// PolyLine.h ... definition of CPolyLine class +// +// A polyline contains one or more contours, where each contour +// is defined by a list of corners and side-styles +// 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 +// each contour. +// +// When used for copper areas, the first contour is the outer edge +// of the area, subsequent ones are "holes" in the copper. +// +// If a CDisplayList pointer is provided, the polyline can draw itself + +#ifndef POLYLINE2KICAD_H +#define POLYLINE2KICAD_H + +#define PCBU_PER_MIL 10 +#define MAX_LAYERS 32 +#define NM_PER_MIL 10 // 25400 +// pad shapes +enum +{ + PAD_NONE = 0, + PAD_ROUND, + PAD_SQUARE, + PAD_RECT, + PAD_RRECT, + PAD_OVAL, + PAD_OCTAGON +}; + +/* +enum +{ + // visible layers + LAY_SELECTION = 0, + LAY_BACKGND, + LAY_VISIBLE_GRID, + LAY_HILITE, + LAY_DRC_ERROR, + LAY_BOARD_OUTLINE, + LAY_RAT_LINE, + LAY_SILK_TOP, + LAY_SILK_BOTTOM, + LAY_SM_TOP, + LAY_SM_BOTTOM, + LAY_PAD_THRU, + LAY_TOP_COPPER, + LAY_BOTTOM_COPPER, + // invisible layers + LAY_MASK_TOP = -100, + LAY_MASK_BOTTOM = -101, + LAY_PASTE_TOP = -102, + LAY_PASTE_BOTTOM = -103 +}; +*/ + +#define LAY_SELECTION 0 +#define LAY_TOP_COPPER 0 + +#define CDC wxDC +class wxDC; + +#if 0 +class dl_element; +class CDisplayList { +public: + void Set_visible(void*, int) {}; + int Get_x(void) { return 0;}; + int Get_y(void) { return 0;}; + void StopDragging(void) {}; + void CancelHighLight(void) {}; + void StartDraggingLineVertex(...) {}; + void Add() {}; +}; +#endif + + +class CRect { +public: + int left, right, top, bottom; +}; + +class CPoint { +public: + int x, y; +public: + CPoint(void) { x = y = 0;}; + CPoint(int i, int j) { x = i; y = j;}; +}; + +#endif // #ifndef POLYLINE2KICAD_H diff --git a/polygon/cdisplaylist_stuff.cpp b/polygon/cdisplaylist_stuff.cpp new file mode 100644 index 0000000000..2270be057e --- /dev/null +++ b/polygon/cdisplaylist_stuff.cpp @@ -0,0 +1,85 @@ +/* stuff for class CDisplayList */ + +#include "PolyLine.h" + +dl_element * CDisplayList::Add( id id, void * ptr, int glayer, int gtype, int visible, + int w, int holew, int x, int y, int xf, int yf, int xo, int yo, + int radius, int orig_layer ) +{ + return NULL; +} + +dl_element * CDisplayList::AddSelector( id id, void * ptr, int glayer, int gtype, int visible, + int w, int holew, int x, int y, int xf, int yf, int xo, int yo, int radius ) +{ + return NULL; +} + + +void CDisplayList::Set_visible( dl_element * el, int visible ) +{ +} + + +int CDisplayList::StopDragging() +{ + return 0; +} + +int CDisplayList::CancelHighLight() +{ + return 0; +} + +void CDisplayList::Set_id( dl_element * el, id * id ) +{ +} + +id CDisplayList::Remove( dl_element * element ) +{ + return 0; +} + +int CDisplayList::Get_w( dl_element * el ) +{ + return 0; +} + +int CDisplayList::Get_x( dl_element * el ) +{ + return 0; +} +int CDisplayList::Get_y( dl_element * el ) +{ + return 0; +} + +int CDisplayList::Get_xf( dl_element * el ) +{ + return 0; +} +int CDisplayList::Get_yf( dl_element * el ) +{ + return 0; +} + +int CDisplayList::HighLight( int gtype, int x, int y, int xf, int yf, int w, int orig_layer ) +{ + return 0; +} + +int CDisplayList::StartDraggingLineVertex( CDC * pDC, int x, int y, int xi, int yi, + int xf, int yf, + int layer1, int layer2, int w1, int w2, + int style1, int style2, + int layer_no_via, int via_w, int via_holew, int dir, + int crosshair ) +{ + return 0; +} + +int CDisplayList::StartDraggingArc( CDC * pDC, int style, int x, int y, int xi, int yi, + int layer, int w, int crosshair ) +{ + return 0; +} diff --git a/polygon/freepcbDisplayList.h b/polygon/freepcbDisplayList.h new file mode 100644 index 0000000000..206c45f62f --- /dev/null +++ b/polygon/freepcbDisplayList.h @@ -0,0 +1,269 @@ +// DisplayList.h : header file for CDisplayList class +// + +#ifndef FP_DISPLAY_LIST_H +#define FP_DISPLAY_LIST_H + +//#define DL_MAX_LAYERS 32 +#define DL_MAGIC 2674 + +#define PCBU_PER_WU 25400 // conversion from PCB units to world units + +// graphics element types +enum +{ + DL_NONE = 0, + DL_LINE, // line segment with round end-caps + DL_CIRC, // filled circle + DL_HOLLOW_CIRC, // circle outline + DL_DONUT, // annulus + DL_SQUARE, // filled square + DL_RECT, // filled rectangle + DL_RRECT, // filled rounded rectangle + DL_OVAL, // filled oval + DL_OCTAGON, // filled octagon + DL_HOLE, // hole, shown as circle + DL_HOLLOW_RECT, // rectangle outline + DL_RECT_X, // rectangle outline with X + DL_POINT, // shape to highlight a point + DL_ARC_CW, // arc with clockwise curve + DL_ARC_CCW, // arc with counter-clockwise curve + DL_X // X +}; + +// dragging line shapes +enum +{ + DS_NONE = 0, + DS_LINE_VERTEX, // vertex between two lines + DS_LINE, // line + DS_ARC_STRAIGHT, // straight line (used when drawing polylines) + DS_ARC_CW, // clockwise arc (used when drawing polylines) + DS_ARC_CCW // counterclockwise arc (used when drawing polylines) +}; + +// styles of line segment when dragging line or line vertex +enum +{ + DSS_STRAIGHT = 100, // straight line + DSS_ARC_CW, // clockwise arc + DSS_ARC_CCW // counterclockwise arc +}; + +// inflection modes for DS_LINE and DS_LINE_VERTEX +enum +{ + IM_NONE = 0, + IM_90_45, + IM_45_90, + IM_90 +}; + + +class CDisplayList; + +// this structure contains an element of the display list +class dl_element +{ + friend class CDisplayList; +public: + CDisplayList * dlist; + int magic; + dl_element * prev; + dl_element * next; + id m_id; // identifier (see ids.h) + void * ptr; // pointer to object drawing this element + int gtype; // type of primitive + int visible; // 0 to hide +//private: + int sel_vert; // for selection rectangles, 1 if part is vertical + int w; // width (for round or square shapes) + int holew; // hole width (for round holes) + int x_org, y_org; // x origin (for rotation, reflection, etc.) + int x, y; // starting or center position of element + int xf, yf; // opposite corner (for rectangle or line) + int radius; // radius of corners for DL_RRECT + int layer; // layer to draw on + int orig_layer; // for elements on highlight layer, + // the original layer, the highlight will + // only be drawn if this layer is visible +}; + +class CDisplayList +{ +private: + // display-list parameters for each layer + dl_element m_start[MAX_LAYERS], m_end[MAX_LAYERS]; + int m_rgb[MAX_LAYERS][3]; // layer colors + BOOL m_vis[MAX_LAYERS]; // layer visibility flags + int m_layer_in_order[MAX_LAYERS]; // array of layers in draw order + int m_order_for_layer[MAX_LAYERS]; // draw order for each layer + + // window parameters + int m_pcbu_per_wu; // i.e. nm per world unit + CRect m_client_r; // client rect (pixels) + CRect m_screen_r; // client rect (screen coords) + int m_pane_org_x; // left border of drawing pane (pixels) + int m_pane_org_y; // bottom border of drawing pane (pixels) + int m_bottom_pane_h; // height of bottom pane + CDC * memDC; // pointer to memory DC + + double m_scale; // world units per pixel + int m_org_x; // world x-coord of left side of screen (world units) + int m_org_y; // world y-coord of bottom of screen (world units) + int m_max_x; // world x_coord of right side of screen (world units) + int m_max_y; // world y_coord of top of screen (world units) + + int w_ext_x, w_ext_y; // window extents (world units) + int v_ext_x, v_ext_y; // viewport extents (pixels) + double m_wu_per_pixel_x; // ratio w_ext_x/v_ext_x (world units per pixel) + double m_wu_per_pixel_y; // ratio w_ext_y/v_ext_y (world units per pixel) + double m_pcbu_per_pixel_x; + double m_pcbu_per_pixel_y; + + // general dragging parameters + int m_drag_angle; // angle of rotation of selection rectangle (starts at 0) + int m_drag_side; // 0 = no change, 1 = switch to opposite + int m_drag_vert; // 1 if item being dragged is a vertical part + + // parameters for dragging polyline sides and trace segments + // that can be modified while dragging + int m_drag_flag; // 1 if dragging something + int m_drag_shape; // shape + int m_last_drag_shape; // last shape drawn + int m_drag_x; // last cursor position for dragged shape + int m_drag_y; + int m_drag_xi; // start of rubberband drag line + int m_drag_yi; + int m_drag_xf; // end of rubberband drag line + int m_drag_yf; + int m_drag_layer_1; // line layer + int m_drag_w1; // line width + int m_drag_style1; // line style + int m_inflection_mode; // inflection mode + int m_last_inflection_mode; // last mode drawn + // extra parameters when dragging vertex between 2 line segments + int m_drag_style2; + int m_drag_layer_2; + int m_drag_w2; + // parameters used to draw leading via if necessary + int m_drag_layer_no_via; + int m_drag_via_w; + int m_drag_via_holew; + int m_drag_via_drawn; + + // arrays of lines and ratlines being dragged + // these can be rotated and flipped while being dragged + int m_drag_layer; // layer + int m_drag_max_lines; // max size of array for line segments + int m_drag_num_lines; // number of line segments to drag + CPoint * m_drag_line_pt; // array of relative coords for line endpoints + int m_drag_max_ratlines; // max size of ratline array + int m_drag_num_ratlines; // number of ratlines to drag + CPoint * m_drag_ratline_start_pt; // absolute coords for ratline start points + CPoint * m_drag_ratline_end_pt; // relative coords for ratline endpoints + int m_drag_ratline_width; + + // cursor parameters + int m_cross_hairs; // 0 = none, 1 = cross-hairs, 2 = diagonals + CPoint m_cross_left, m_cross_right, m_cross_top, m_cross_bottom; // end-points + CPoint m_cross_topleft, m_cross_topright, m_cross_botleft, m_cross_botright; + + // grid + int m_visual_grid_on; + double m_visual_grid_spacing; // in world units + +public: + CDisplayList( int pcbu_per_wu ); + ~CDisplayList(); + void SetVisibleGrid( BOOL on, double grid ); + void SetMapping( CRect *client_r, CRect *screen_r, int pane_org_x, int pane_bottom_h, double scale, int org_x, int org_y ); + void SetDCToWorldCoords( CDC * pDC, CDC * mDC, int pcbu_org_x, int pcbu_org_y ); + void SetLayerRGB( int layer, int r, int g, int b ); + void SetLayerVisible( int layer, BOOL vis ); + void SetLayerDrawOrder( int layer, int order ) + { m_layer_in_order[order] = layer; m_order_for_layer[layer] = order; }; + dl_element * Add( id id, void * ptr, int glayer, int gtype, int visible, + int w, int holew, int x, int y, int xf, int yf, int xo, int yo, + int radius=0, int orig_layer=LAY_SELECTION ); + dl_element * AddSelector( id id, void * ptr, int glayer, int gtype, int visible, + int w, int holew, int x, int y, int xf, int yf, int xo, int yo, int radius=0 ); + void RemoveAll(); + void RemoveAllFromLayer( int layer ); + id Remove( dl_element * element ); + void Draw( CDC * pDC ); + int HighLight( int gtype, int x, int y, int xf, int yf, int w, int orig_layer=LAY_SELECTION ); + int CancelHighLight(); + void * TestSelect( int x, int y, id * sel_id, int * layer, + id * exclude_id = NULL, void * exclude_ptr = NULL, id * include_id = NULL, + int n_include_ids=1 ); + int StartDraggingArray( CDC * pDC, int x, int y, int vert, int layer, int crosshair = 1 ); + int StartDraggingRatLine( CDC * pDC, int x, int y, int xf, int yf, int layer, + int w, int crosshair = 1 ); + int StartDraggingRectangle( CDC * pDC, int x, int y, int xi, int yi, + int xf, int yf, int vert, int layer ); + int StartDraggingLineVertex( CDC * pDC, int x, int y, int xi, int yi, + int xf, int yf, + int layer1, int layer2, int w1, int w2, + int style1, int style2, + int layer_no_via, int via_w, int via_holew, int dir, + int crosshair ); + int StartDraggingLine( CDC * pDC, int x, int y, int xi, int yi, int layer1, int w, + int layer_no_via, int via_w, int via_holew, + int crosshair, int style, int inflection_mode ); + int StartDraggingArc( CDC * pDC, int style, int x, int y, int xi, int yi, + int layer, int w, int crosshair ); + void SetDragArcStyle( int style ); + void Drag( CDC * pDC, int x, int y ); + int StopDragging(); + void ChangeRoutingLayer( CDC * pDC, int layer1, int layer2, int w ); + void IncrementDragAngle( CDC * pDC ); + int MakeDragLineArray( int num_lines ); + int MakeDragRatlineArray( int num_ratlines, int width ); + int AddDragLine( CPoint pi, CPoint pf ); + int AddDragRatline( CPoint pi, CPoint pf ); + int GetDragAngle(); + void FlipDragSide( CDC * pDC ); + int GetDragSide(); + void SetUpCrosshairs( int type, int x, int y ); + void SetInflectionMode( int mode ){ m_inflection_mode = mode; }; + CPoint ScreenToPCB( CPoint point ); + CPoint PCBToScreen( CPoint point ); + CPoint WindowToPCB( CPoint point ); + + // set element parameters + void Set_gtype( dl_element * el, int gtype ); + void Set_visible( dl_element * el, int visible ); + void Set_sel_vert( dl_element * el, int sel_vert ); + void Set_w( dl_element * el, int w ); + void Set_holew( dl_element * el, int holew ); + void Set_x_org( dl_element * el, int x_org ); + void Set_y_org( dl_element * el, int y_org ); + void Set_x( dl_element * el, int x ); + void Set_y( dl_element * el, int y ); + void Set_xf( dl_element * el, int xf ); + void Set_yf( dl_element * el, int yf ); + void Set_id( dl_element * el, id * id ); + void Set_layer( dl_element * el, int layer ); + void Set_radius( dl_element * el, int radius ); + void Move( dl_element * el, int dx, int dy ); + + // get element parameters + void * Get_ptr( dl_element * el ); + int Get_gtype( dl_element * el ); + int Get_visible( dl_element * el ); + int Get_sel_vert( dl_element * el ); + int Get_w( dl_element * el ); + int Get_holew( dl_element * el ); + int Get_x_org( dl_element * el ); + int Get_y_org( dl_element * el ); + int Get_x( dl_element * el ); + int Get_y( dl_element * el ); + int Get_xf( dl_element * el ); + int Get_yf( dl_element * el ); + int Get_radius( dl_element * el ); + int Get_layer( dl_element * el ); + id Get_id( dl_element * el ); +}; + +#endif // #ifndef FP_DISPLAY_LIST_H diff --git a/polygon/freepcb_ids.h b/polygon/freepcb_ids.h new file mode 100644 index 0000000000..8204f175eb --- /dev/null +++ b/polygon/freepcb_ids.h @@ -0,0 +1,118 @@ +// definition of ID structure used by FreePCB +// +#pragma once + +// struct id : this structure is used to identify PCB design elements +// such as instances of parts or nets, and their subelements +// Each element will have its own id. +// An id is attached to each item of the Display List so that it can +// be linked back to the PCB design element which drew it. +// These are mainly used to identify items selected by clicking the mouse +// +// In general: +// id.type = type of PCB element (e.g. part, net, text) +// id.st = subelement type (e.g. part pad, net connection) +// id.i = subelement index (zero-based) +// id.sst = subelement of subelement (e.g. net connection segment) +// id.ii = subsubelement index (zero-based) +// +// For example, the id for segment 0 of connection 4 of net 12 would be +// id = { ID_NET, 12, ID_CONNECT, 4, ID_SEG, 0 }; +// +// +class id { +public: + // constructor + id( int qt=0, int qst=0, int qis=0, int qsst=0, int qiis=0 ) + { type=qt; st=qst; i=qis; sst=qsst; ii=qiis; } + // operators + friend int operator ==(id id1, id id2) + { return (id1.type==id2.type + && id1.st==id2.st + && id1.sst==id2.sst + && id1.i==id2.i + && id1.ii==id2.ii ); + } + // member functions + void Clear() + { type=0; st=0; i=0; sst=0; ii=0; } + void Set( int qt, int qst=0, int qis=0, int qsst=0, int qiis=0 ) + { type=qt; st=qst; i=qis; sst=qsst; ii=qiis; } + // member variables + unsigned int type; // type of element + unsigned int st; // type of subelement + unsigned int i; // index of subelement + unsigned int sst; // type of subsubelement + unsigned int ii; // index of subsubelement +}; + + +// these are constants used in ids +// root types +enum { + ID_NONE = 0, // an undefined type or st (or an error) + ID_BOARD, // board outline + ID_PART, // part + ID_NET, // net + ID_TEXT, // free-standing text + ID_DRC, // DRC error + ID_SM_CUTOUT, // cutout for solder mask + ID_MULTI // if multiple selections +}; + +// subtypes of ID_PART +enum { + ID_PAD = 1, // pad_stack in a part + ID_SEL_PAD, // selection rectangle for pad_stack in a part + ID_OUTLINE, // part outline + ID_REF_TXT, // text showing ref num for part + ID_ORIG, // part origin + ID_SEL_RECT, // selection rectangle for part + ID_SEL_REF_TXT // selection rectangle for ref text +}; + +// subtypes of ID_TEXT +enum { + ID_SEL_TXT = 1, // selection rectangle + ID_STROKE // stroke for text +}; + +// subtypes of ID_NET +enum { + ID_ENTIRE_NET = 0, + ID_CONNECT, // connection + ID_AREA // copper area +}; + +// subtypes of ID_BOARD +enum { + ID_BOARD_OUTLINE = 1, +}; + +// subsubtypes of ID_NET.ID_CONNECT +enum { + ID_ENTIRE_CONNECT = 0, + ID_SEG, + ID_SEL_SEG, + ID_VERTEX, + ID_SEL_VERTEX, + ID_VIA +}; + +// subsubtypes of ID_NET.ID_AREA, ID_BOARD.ID_BOARD_OUTLINE, ID_SM_CUTOUT +enum { + ID_SIDE = 1, + ID_SEL_SIDE, + ID_SEL_CORNER, + ID_HATCH, + ID_PIN_X, // only used by ID_AREA + ID_STUB_X // only used by ID_AREA +}; + +// subtypes of ID_DRC +// for subsubtypes, use types in DesignRules.h +enum { + ID_DRE = 1, + ID_SEL_DRE +}; + diff --git a/polygon/makefile.include b/polygon/makefile.include index 4b778a67d1..ce63f3c433 100644 --- a/polygon/makefile.include +++ b/polygon/makefile.include @@ -9,7 +9,8 @@ OBJECTS= \ php_polygon.o\ php_polygon_vertex.o\ PolyLine.o\ - math_for_graphics.o + math_for_graphics.o\ + cdisplaylist_stuff.o GenericPolygonClipperLibrary.o: GenericPolygonClipperLibrary.cpp GenericPolygonClipperLibrary.h diff --git a/polygon/math_for_graphics.cpp b/polygon/math_for_graphics.cpp new file mode 100644 index 0000000000..f4e1e7ca43 --- /dev/null +++ b/polygon/math_for_graphics.cpp @@ -0,0 +1,1736 @@ +// math for graphics utility routines, from FreePCB + +using namespace std; + +#include + +#include +#include +#include + +#include "defs-macros.h" + +#include "PolyLine2Kicad.h" +#include "freepcb_ids.h" +#include "PolyLine.h" + + +// function to find inflection-pont to create a "dogleg" of two straight-line segments +// where one segment is vertical or horizontal and the other is at 45 degrees or 90 degrees +// enter with: +// pi = start point +// pf = end point +// mode = IM_90_45 or IM_45_90 or IM_90 +// +CPoint GetInflectionPoint( CPoint pi, CPoint pf, int mode ) +{ + CPoint p = pi; + if( mode == IM_NONE ) + return p; + + int dx = pf.x - pi.x; + int dy = pf.y - pi.y; + if( dx == 0 || dy == 0 || abs(dx) == abs(dy) ) + { + // only one segment needed + } + else + { + if( abs(dy) > abs(dx) ) + { + // vertical > horizontal + if( mode == IM_90 ) + { + p.x = pi.x; + p.y = pf.y; + } + else if( mode == IM_45_90 || mode == IM_90_45 ) + { + int vert; // length of vertical line needed + if( dy > 0 ) + vert = dy - abs(dx); // positive + else + vert = dy + abs(dx); // negative + if( mode == IM_90_45 ) + p.y = pi.y + vert; + else if( mode == IM_45_90 ) + { + p.y = pf.y - vert; + p.x = pf.x; + } + } + else + ASSERT(0); + } + else + { + // horizontal > vertical + if( mode == IM_90 ) + { + p.x = pf.x; + p.y = pi.y; + } + else if( mode == IM_45_90 || mode == IM_90_45 ) + { + int hor; // length of horizontal line needed + if( dx > 0 ) + hor = dx - abs(dy); // positive + else + hor = dx + abs(dy); // negative + if( mode == IM_90_45 ) + p.x = pi.x + hor; + else if( mode == IM_45_90 ) + { + p.x = pf.x - hor; + p.y = pf.y; + } + } + else + ASSERT(0); + } + } + return p; +} + +// +// function to rotate a point clockwise about another point +// currently, angle must be 0, 90, 180 or 270 +// +void RotatePoint( CPoint *p, int angle, CPoint org ) +{ + if( angle == 90 ) + { + int tempy = org.y + (org.x - p->x); + p->x = org.x + (p->y - org.y); + p->y = tempy; + } + else if( angle > 90 ) + { + for( int i=0; i<(angle/90); i++ ) + RotatePoint( p, 90, org ); + } +} + +// function to rotate a rectangle clockwise about a point +// angle must be 0, 90, 180 or 270 +// on exit, r->top > r.bottom, r.right > r.left +// +void RotateRect( CRect *r, int angle, CPoint org ) +{ + CRect tr; + if( angle == 90 ) + { + tr.left = org.x + (r->bottom - org.y); + tr.right = org.x + (r->top - org.y); + tr.top = org.y + (org.x - r->right); + tr.bottom = org.y + (org.x - r->left); + if( tr.left > tr.right ) + { + int temp = tr.right; + tr.left = tr.right; + tr.left = temp; + } + if( tr.left > tr.right ) + { + int temp = tr.right; + tr.left = tr.right; + tr.left = temp; + } + if( tr.bottom > tr.top ) + { + int temp = tr.bottom; + tr.bottom = tr.top; + tr.top = temp; + } + } + else if( angle > 90 ) + { + tr = *r; + for( int i=0; i<(angle/90); i++ ) + RotateRect( &tr, 90, org ); + } + *r = tr; +} + +// test for hit on line segment +// i.e. cursor within a given distance from segment +// enter with: x,y = cursor coords +// (xi,yi) and (xf,yf) are the end-points of the line segment +// dist = maximum distance for hit +// +int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist ) +{ + double dd; + + // test for vertical or horizontal segment + if( xf==xi ) + { + // vertical segment + dd = fabs( (double)(x-xi) ); + if( ddyi && yyi) || (yfyf && yxi && xxi) || (xfxf && x0.7 ) + { + // line segment more vertical than horizontal + if( ddyi && ypyi) || (yfyf && ypxi && xpxi) || (xfxf && xp xxi && yyf > yyi ) + { + xo = xxf; + yo = yyi; + el->theta1 = M_PI; + el->theta2 = M_PI/2.0; + } + else if( xxf < xxi && yyf > yyi ) + { + xo = xxi; + yo = yyf; + el->theta1 = -M_PI/2.0; + el->theta2 = -M_PI; + } + else if( xxf < xxi && yyf < yyi ) + { + xo = xxf; + yo = yyi; + el->theta1 = 0.0; + el->theta2 = -M_PI/2.0; + } + else if( xxf > xxi && yyf < yyi ) + { + xo = xxi; + yo = yyf; + el->theta1 = M_PI/2.0; + el->theta2 = 0.0; + } + el->Center.X = xo; + el->Center.Y = yo; + el->xrad = abs(xf-xi); + el->yrad = abs(yf-yi); +#if 0 + el->Phi = 0.0; + el->MaxRad = el->xrad; + el->MinRad = el->yrad; + if( el->MaxRad < el->MinRad ) + { + el->MaxRad = el->yrad; + el->MinRad = el->xrad; + el->Phi = M_PI/2.0; + } +#endif + return 0; +} + +// find intersections between line segment (xi,yi) to (xf,yf) +// and line segment (xi2,yi2) to (xf2,yf2) +// the line segments may be arcs (i.e. quadrant of an ellipse) or straight +// returns number of intersections found (max of 2) +// returns coords of intersections in arrays x[2], y[2] +// +int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style, + int xi2, int yi2, int xf2, int yf2, int style2, + double x[], double y[] ) +{ + double xr[12], yr[12]; + int iret = 0; + + if( max(xi,xf) < min(xi2,xf2) + || min(xi,xf) > max(xi2,xf2) + || max(yi,yf) < min(yi2,yf2) + || min(yi,yf) > max(yi2,yf2) ) + return 0; + + if( style != CPolyLine::STRAIGHT && style2 != CPolyLine::STRAIGHT ) + { + // two identical arcs intersect + if( style == style2 && xi == xi2 && yi == yi2 && xf == xf2 && yf == yf2 ) + { + if( x && y ) + { + x[0] = xi; + y[0] = yi; + } + return 1; + } + else if( style != style2 && xi == xf2 && yi == yf2 && xf == xi2 && yf == yi2 ) + { + if( x && y ) + { + x[0] = xi; + y[0] = yi; + } + return 1; + } + } + + if( style == CPolyLine::STRAIGHT && style2 == CPolyLine::STRAIGHT ) + { + // both straight-line segments + int x, y; + bool bYes = TestForIntersectionOfStraightLineSegments( xi, yi, xf, yf, xi2, yi2, xf2, yf2, &x, &y ); + if( !bYes ) + return 0; + xr[0] = x; + yr[0] = y; + iret = 1; + } + else if( style == CPolyLine::STRAIGHT ) + { + // first segment is straight, second segment is an arc + int ret; + double x1r, y1r, x2r, y2r; + if( xf == xi ) + { + // vertical first segment + double a = xi; + double b = DBL_MAX/2.0; + ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2, + &x1r, &y1r, &x2r, &y2r ); + } + else + { + double b = (double)(yf-yi)/(double)(xf-xi); + double a = yf - b*xf; + ret = FindLineSegmentIntersection( a, b, xi2, yi2, xf2, yf2, style2, + &x1r, &y1r, &x2r, &y2r ); + } + if( ret == 0 ) + return 0; + if( InRange( x1r, xi, xf ) && InRange( y1r, yi, yf ) ) + { + xr[iret] = x1r; + yr[iret] = y1r; + iret++; + } + if( ret == 2 ) + { + if( InRange( x2r, xi, xf ) && InRange( y2r, yi, yf ) ) + { + xr[iret] = x2r; + yr[iret] = y2r; + iret++; + } + } + } + else if( style2 == CPolyLine::STRAIGHT ) + { + // first segment is an arc, second segment is straight + int ret; + double x1r, y1r, x2r, y2r; + if( xf2 == xi2 ) + { + // vertical second segment + double a = xi2; + double b = DBL_MAX/2.0; + ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style, + &x1r, &y1r, &x2r, &y2r ); + } + else + { + double b = (double)(yf2-yi2)/(double)(xf2-xi2); + double a = yf2 - b*xf2; + ret = FindLineSegmentIntersection( a, b, xi, yi, xf, yf, style, + &x1r, &y1r, &x2r, &y2r ); + } + if( ret == 0 ) + return 0; + if( InRange( x1r, xi2, xf2 ) && InRange( y1r, yi2, yf2 ) ) + { + xr[iret] = x1r; + yr[iret] = y1r; + iret++; + } + if( ret == 2 ) + { + if( InRange( x2r, xi2, xf2 ) && InRange( y2r, yi2, yf2 ) ) + { + xr[iret] = x2r; + yr[iret] = y2r; + iret++; + } + } + } + else + { + // both segments are arcs + EllipseKH el1; + EllipseKH el2; + MakeEllipseFromArc( xi, yi, xf, yf, style, &el1 ); + MakeEllipseFromArc( xi2, yi2, xf2, yf2, style2, &el2 ); + int n; + if( el1.xrad+el1.yrad > el2.xrad+el2.yrad ) + n = GetArcIntersections( &el1, &el2 ); + else + n = GetArcIntersections( &el2, &el1 ); + iret = n; + } + if( x && y ) + { + for( int i=0; i DBL_MAX/10, assume vertical line at x = a +// the line segment may be an arc (i.e. quadrant of an ellipse) +// return 0 if no intersection +// returns 1 or 2 if intersections found +// sets coords of intersections in *x1, *y1, *x2, *y2 +// if no intersection, returns min distance in dist +// +int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style, + double * x1, double * y1, double * x2, double * y2, + double * dist ) +{ + double xx, yy; + bool bVert = false; + if( b > DBL_MAX/10.0 ) + bVert = true; + + if( xf != xi ) + { + // non-vertical segment, get intersection + if( style == CPolyLine::STRAIGHT || yf == yi ) + { + // horizontal or oblique straight segment + // put into form y = c + dx; + double d = (double)(yf-yi)/(double)(xf-xi); + double c = yf - d*xf; + if( bVert ) + { + // if vertical line, easy + if( InRange( a, xi, xf ) ) + { + *x1 = a; + *y1 = c + d*a; + return 1; + } + else + { + if( dist ) + *dist = min( abs(a-xi), abs(a-xf) ); + return 0; + } + } + if( fabs(b-d) < 1E-12 ) + { + // parallel lines + if( dist ) + { + *dist = GetPointToLineDistance( a, b, xi, xf ); + } + return 0; // lines parallel + } + // calculate intersection + xx = (c-a)/(b-d); + yy = a + b*(xx); + // see if intersection is within the line segment + if( yf == yi ) + { + // horizontal line + if( (xx>=xi && xx>xf) || (xx<=xi && xx=xi && xx>xf) || (xx<=xi && xxyi && yy>yf) || (yy xxi && yyf > yyi ) + { + xo = xxf; + yo = yyi; + } + else if( xxf < xxi && yyf > yyi ) + { + xo = xxi; + yo = yyf; + } + else if( xxf < xxi && yyf < yyi ) + { + xo = xxf; + yo = yyi; + } + else if( xxf > xxi && yyf < yyi ) + { + xo = xxi; + yo = yyf; + } + rx = fabs( (double)(xxi-xxf) ); + ry = fabs( (double)(yyi-yyf) ); + bool test; + double xx1, xx2, yy1, yy2, aa; + if( bVert ) + { + // shift vertical line to coordinate system of ellipse + aa = a - xo; + test = FindVerticalLineEllipseIntersections( rx, ry, aa, &yy1, &yy2 ); + if( !test ) + return 0; + // shift back to PCB coordinates + yy1 += yo; + yy2 += yo; + xx1 = a; + xx2 = a; + } + else + { + // shift line to coordinate system of ellipse + aa = a + b*xo - yo; + test = FindLineEllipseIntersections( rx, ry, aa, b, &xx1, &xx2 ); + if( !test ) + return 0; + // shift back to PCB coordinates + yy1 = aa + b*xx1; + xx1 += xo; + yy1 += yo; + yy2 = aa + b*xx2; + xx2 += xo; + yy2 += yo; + } + int npts = 0; + if( (xxf>xxi && xx1xxi) || (xxfxxf) ) + { + if( (yyf>yyi && yy1yyi) || (yyfyyf) ) + { + *x1 = xx1; + *y1 = yy1; + npts = 1; + } + } + if( (xxf>xxi && xx2xxi) || (xxfxxf) ) + { + if( (yyf>yyi && yy2yyi) || (yyfyyf) ) + { + if( npts == 0 ) + { + *x1 = xx2; + *y1 = yy2; + npts = 1; + } + else + { + *x2 = xx2; + *y2 = yy2; + npts = 2; + } + } + } + return npts; + } + else + ASSERT(0); + } + else + { + // vertical line segment + if( bVert ) + return 0; + xx = xi; + yy = a + b*xx; + if( (yy>=yi && yy>yf) || (yy<=yi && yy= pivot) && (left < right)) + right--; + if (left != right) + { + numbers[left] = numbers[right]; + index[left] = index[right]; + left++; + } + while ((numbers[left] <= pivot) && (left < right)) + left++; + if (left != right) + { + numbers[right] = numbers[left]; + index[right] = index[left]; + right--; + } + } + numbers[left] = pivot; + index[left] = pivot_index; + + pivot = left; + left = l_hold; + right = r_hold; + if (left < pivot) + q_sort(numbers, index, left, pivot-1); + if (right > pivot) + q_sort(numbers, index, pivot+1, right); +} + +// 3-way quicksort...useful where there are duplicate values +// +void q_sort_3way( int a[], int b[], int l, int r ) +{ + #define EXCH(i,j) {int temp=a[i]; a[i]=a[j]; a[j]=temp; temp=b[i]; b[i]=b[j]; b[j]=temp;} + + int i = l - 1; + int j = r; + int p = l - 1; + int q = r; + int v = a[r]; + + if( r <= l ) + return; + + for(;;) + { + while( a[++i] < v ); + while( v < a[--j] ) + if( j == 1 ) + break; + if( i >= j ) + break; + EXCH( i, j ); + if( a[i] == v ) + { + p++; + EXCH( p, i ); + } + if( v == a[j] ) + { + q--; + EXCH( j, q ); + } + } + EXCH( i, r ); + j = i - 1; + i = i + 1; + for( int k=l; kq; k--, i++ ) + EXCH( i, k ); + q_sort_3way( a, b, l, j ); + q_sort_3way( a, b, i, r ); +} + +// solves quadratic equation +// i.e. ax**2 + bx + c = 0 +// returns true if solution exist, with solutions in x1 and x2 +// else returns false +// +bool Quadratic( double a, double b, double c, double *x1, double *x2 ) +{ + double root = b*b - 4.0*a*c; + if( root < 0.0 ) + return false; + root = sqrt( root ); + *x1 = (-b+root)/(2.0*a); + *x2 = (-b-root)/(2.0*a); + return true; +} + +// finds intersections of vertical line at x +// with ellipse defined by (x^2)/(a^2) + (y^2)/(b^2) = 1; +// returns true if solution exist, with solutions in y1 and y2 +// else returns false +// +bool FindVerticalLineEllipseIntersections( double a, double b, double x, double *y1, double *y2 ) +{ + double y_sqr = (1.0-(x*x)/(a*a))*b*b; + if( y_sqr < 0.0 ) + return false; + *y1 = sqrt(y_sqr); + *y2 = -*y1; + return true; +} + +// finds intersections of straight line y = c + dx +// with ellipse defined by (x^2)/(a^2) + (y^2)/(b^2) = 1; +// returns true if solution exist, with solutions in x1 and x2 +// else returns false +// +bool FindLineEllipseIntersections( double a, double b, double c, double d, double *x1, double *x2 ) +{ + // quadratic terms + double A = d*d+b*b/(a*a); + double B = 2.0*c*d; + double C = c*c-b*b; + return Quadratic( A, B, C, x1, x2 ); +} + + +#if 0 +// draw a straight line or an arc between xi,yi and xf,yf +// +void DrawArc( CDC * pDC, int shape, int xxi, int yyi, int xxf, int yyf, bool bMeta ) +{ + int xi, yi, xf, yf; + if( shape == DL_LINE || xxi == xxf || yyi == yyf ) + { + // draw straight line + pDC->MoveTo( xxi, yyi ); + pDC->LineTo( xxf, yyf ); + } + else if( shape == DL_ARC_CCW || shape == DL_ARC_CW ) + { + // set endpoints so we can always draw counter-clockwise arc + if( shape == DL_ARC_CW ) + { + xi = xxf; + yi = yyf; + xf = xxi; + yf = yyi; + } + else + { + xi = xxi; + yi = yyi; + xf = xxf; + yf = yyf; + } + pDC->MoveTo( xi, yi ); + if( xf > xi && yf > yi ) + { + // quadrant 1 + int w = (xf-xi)*2; + int h = (yf-yi)*2; + if( !bMeta ) + pDC->Arc( xf-w, yi+h, xf, yi, + xi, yi, xf, yf ); + else + pDC->Arc( xf-w, yi, xf, yi+h, + xf, yf, xi, yi ); + } + else if( xf < xi && yf > yi ) + { + // quadrant 2 + int w = -(xf-xi)*2; + int h = (yf-yi)*2; + if( !bMeta ) + pDC->Arc( xi-w, yf, xi, yf-h, + xi, yi, xf, yf ); + else + pDC->Arc( xi-w, yf-h, xi, yf, + xf, yf, xi, yi ); + } + else if( xf < xi && yf < yi ) + { + // quadrant 3 + int w = -(xf-xi)*2; + int h = -(yf-yi)*2; + if( !bMeta ) + pDC->Arc( xf, yi, xf+w, yi-h, + xi, yi, xf, yf ); + else + pDC->Arc( xf, yi-h, xf+w, yi, + xf, yf, xi, yi ); + } + else if( xf > xi && yf < yi ) + { + // quadrant 4 + int w = (xf-xi)*2; + int h = -(yf-yi)*2; + if( !bMeta ) + pDC->Arc( xi, yf+h, xi+w, yf, + xi, yi, xf, yf ); + else + pDC->Arc( xi, yf, xi+w, yf+h, + xf, yf, xi, yi ); + } + pDC->MoveTo( xxf, yyf ); + } + else + ASSERT(0); // oops +} + +#endif + +// Get arrays of circles, rects and line segments to represent pad +// for purposes of drawing pad or calculating clearances +// margins of circles and line segments represent pad outline +// circles and rects are used to find points inside pad +// +void GetPadElements( int type, int x, int y, int wid, int len, int radius, int angle, + int * nr, my_rect r[], int * nc, my_circle c[], int * ns, my_seg s[] ) +{ + *nc = 0; + *nr = 0; + *ns = 0; + if( type == PAD_ROUND ) + { + *nc = 1; + c[0] = my_circle(x,y,wid/2); + return; + } + if( type == PAD_SQUARE ) + { + *nr = 1; + r[0] = my_rect(x-wid/2, y-wid/2,x+wid/2, y+wid/2); + *ns = 4; + s[0] = my_seg(x-wid/2, y+wid/2,x+wid/2, y+wid/2); // top + s[1] = my_seg(x-wid/2, y-wid/2,x+wid/2, y-wid/2); // bottom + s[2] = my_seg(x-wid/2, y-wid/2,x-wid/2, y+wid/2); // left + s[3] = my_seg(x+wid/2, y-wid/2,x+wid/2, y+wid/2); // right + return; + } + if( type == PAD_OCTAGON ) + { + const double pi = 3.14159265359; + *nc = 1; // circle represents inside of polygon + c[0] = my_circle(x, y, wid/2); + *ns = 8; // now create sides of polygon + double theta = pi/8.0; + double radius = 0.5*(double)wid/cos(theta); + double last_x = x + radius*cos(theta); + double last_y = y + radius*sin(theta); + for( int is=0; is<8; is++ ) + { + theta += pi/4.0; + double dx = x + radius*cos(theta); + double dy = y + radius*sin(theta); + s[is] = my_seg(last_x, last_y, x, y); + last_x = dx; + last_y = dy; + } + return; + } + // + int h; + int v; + if( angle == 90 || angle == 270 ) + { + h = wid; + v = len; + } + else + { + v = wid; + h = len; + } + if( type == PAD_RECT ) + { + *nr = 1; + r[0] = my_rect(x-h/2, y-v/2, x+h/2, y+v/2); + *ns = 4; + s[0] = my_seg(x-h/2, y+v/2,x+h/2, y+v/2); // top + s[1] = my_seg(x-h/2, y-v/2,x+h/2, y-v/2); // bottom + s[2] = my_seg(x-h/2, y-v/2,x-h/2, y+v/2); // left + s[3] = my_seg(x+h/2, y-v/2,x+h/2, y+v/2); // right + return; + } + if( type == PAD_RRECT ) + { + *nc = 4; + c[0] = my_circle(x-h/2+radius, y-v/2+radius, radius); // bottom left circle + c[1] = my_circle(x+h/2-radius, y-v/2+radius, radius); // bottom right circle + c[2] = my_circle(x-h/2+radius, y+v/2-radius, radius); // top left circle + c[3] = my_circle(x+h/2-radius, y+v/2-radius, radius); // top right circle + *ns = 4; + s[0] = my_seg(x-h/2+radius, y+v/2, x+h/2-radius, y+v/2); // top + s[1] = my_seg(x-h/2+radius, y-v/2, x+h/2-radius, y+v/2); // bottom + s[2] = my_seg(x-h/2, y-v/2+radius, x-h/2, y+v/2-radius); // left + s[3] = my_seg(x+h/2, y-v/2+radius, x+h/2, y+v/2-radius); // right + return; + } + if( type == PAD_OVAL ) + { + if( h > v ) + { + // horizontal + *nc = 2; + c[0] = my_circle(x-h/2+v/2, y, v/2); // left circle + c[1] = my_circle(x+h/2-v/2, y, v/2); // right circle + *nr = 1; + r[0] = my_rect(x-h/2+v/2, y-v/2, x+h/2-v/2, y+v/2); + *ns = 2; + s[0] = my_seg(x-h/2+v/2, y+v/2, x+h/2-v/2, y+v/2); // top + s[1] = my_seg(x-h/2+v/2, y-v/2, x+h/2-v/2, y-v/2); // bottom + } + else + { + // vertical + *nc = 2; + c[0] = my_circle(x, y+v/2-h/2, h/2); // top circle + c[1] = my_circle(x, y-v/2+h/2, h/2); // bottom circle + *nr = 1; + r[0] = my_rect(x-h/2, y-v/2+h/2, x+h/2, y+v/2-h/2); + *ns = 2; + s[0] = my_seg(x-h/2, y-v/2+h/2, x-h/2, y+v/2-h/2); // left + s[1] = my_seg(x+h/2, y-v/2+h/2, x+h/2, y+v/2-h/2); // left + } + return; + } + ASSERT(0); +} + +// Find distance from a staright line segment to a pad +// +int GetClearanceBetweenSegmentAndPad( int x1, int y1, int x2, int y2, int w, + int type, int x, int y, int wid, int len, int radius, int angle ) +{ + if( type == PAD_NONE ) + return INT_MAX; + else + { + int nc, nr, ns; + my_circle c[4]; + my_rect r[2]; + my_seg s[8]; + GetPadElements( type, x, y, wid, len, radius, angle, + &nr, r, &nc, c, &ns, s ); + // first test for endpoints of line segment in rectangle + for( int ir=0; ir= r[ir].xlo && x1 <= r[ir].xhi && y1 >= r[ir].ylo && y1 <= r[ir].yhi ) + return 0; + if( x2 >= r[ir].xlo && x2 <= r[ir].xhi && y2 >= r[ir].ylo && y2 <= r[ir].yhi ) + return 0; + } + // now get distance from elements of pad outline + int dist = INT_MAX; + for( int ic=0; ic max_cl, just returns max_cl and doesn't return x,y +// +int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int style1, int w1, + int x2i, int y2i, int x2f, int y2f, int style2, int w2, + int max_cl, int * x, int * y ) +{ + // check clearance between bounding rectangles + int test = max_cl + w1/2 + w2/2; + if( min(x1i,x1f)-max(x2i,x2f) > test ) + return max_cl; + if( min(x2i,x2f)-max(x1i,x1f) > test ) + return max_cl; + if( min(y1i,y1f)-max(y2i,y2f) > test ) + return max_cl; + if( min(y2i,y2f)-max(y1i,y1f) > test ) + return max_cl; + + if( style1 == CPolyLine::STRAIGHT && style1 == CPolyLine::STRAIGHT ) + { + // both segments are straight lines + int xx, yy; + double dd; + TestForIntersectionOfStraightLineSegments( x1i, y1i, x1f, y1f, + x2i, y2i, x2f, y2f, &xx, &yy, &dd ); + int d = max( 0, dd - w1/2 - w2/2 ); + if( x ) + *x = xx; + if( y ) + *y = yy; + return d; + } + + // not both straight-line segments + // see if segments intersect + double xr[2]; + double yr[2]; + test = FindSegmentIntersections( x1i, y1i, x1f, y1f, style1, x2i, y2i, x2f, y2f, style2, xr, yr ); + if( test ) + { + if( x ) + *x = xr[0]; + if( y ) + *y = yr[0]; + return 0.0; + } + + // at least one segment is an arc + EllipseKH el1; + EllipseKH el2; + bool bArcs; + int xi, yi, xf, yf; + if( style2 == CPolyLine::STRAIGHT ) + { + // style1 = arc, style2 = straight + MakeEllipseFromArc( x1i, y1i, x1f, y1f, style1, &el1 ); + xi = x2i; + yi = y2i; + xf = x2f; + yf = y2f; + bArcs = false; + } + else if( style1 == CPolyLine::STRAIGHT ) + { + // style2 = arc, style1 = straight + xi = x1i; + yi = y1i; + xf = x1f; + yf = y1f; + MakeEllipseFromArc( x2i, y2i, x2f, y2f, style2, &el1 ); + bArcs = false; + } + else + { + // style1 = arc, style2 = arc + MakeEllipseFromArc( x1i, y1i, x1f, y1f, style1, &el1 ); + MakeEllipseFromArc( x2i, y2i, x2f, y2f, style2, &el2 ); + bArcs = true; + } + const int NSTEPS = 32; + + if( el1.theta2 > el1.theta1 ) + ASSERT(0); + if( bArcs && el2.theta2 > el2.theta1 ) + ASSERT(0); + + // test multiple points in both segments + double th1; + double th2; + double len2; + if( bArcs ) + { + th1 = el2.theta1; + th2 = el2.theta2; + len2 = max(el2.xrad, el2.yrad); + } + else + { + th1 = 1.0; + th2 = 0.0; + len2 = abs(xf-xi)+abs(yf-yi); + } + double s_start = el1.theta1; + double s_end = el1.theta2; + double s_start2 = th1; + double s_end2 = th2; + double dmin = DBL_MAX; + double xmin, ymin, smin, smin2; + + int nsteps = NSTEPS; + int nsteps2 = NSTEPS; + double step = (s_start-s_end)/(nsteps-1); + double step2 = (s_start2-s_end2)/(nsteps2-1); + while( (step * max(el1.xrad, el1.yrad)) > 0.1*NM_PER_MIL + && (step2 * len2) > 0.1*NM_PER_MIL ) + { + step = (s_start-s_end)/(nsteps-1); + for( int i=0; i step2 ) + { + s_start = min(el1.theta1, smin + step); + s_end = max(el1.theta2, smin - step); + step = (s_start - s_end)/nsteps; + } + else + { + s_start2 = min(th1, smin2 + step2); + s_end2 = max(th2, smin2 - step2); + step2 = (s_start2 - s_end2)/nsteps2; + } + } + if( x ) + *x = xmin; + if( y ) + *y = ymin; + return max(0,dmin-w1/2-w2/2); // allow for widths +} + + + +// Find clearance between pads +// For each pad: +// type = PAD_ROUND, PAD_SQUARE, etc. +// x, y = center position +// w, l = width and length +// r = corner radius +// angle = 0 or 90 (if 0, pad length is along x-axis) +// +int GetClearanceBetweenPads( int type1, int x1, int y1, int w1, int l1, int r1, int angle1, + int type2, int x2, int y2, int w2, int l2, int r2, int angle2 ) +{ + if( type1 == PAD_NONE ) + return INT_MAX; + if( type2 == PAD_NONE ) + return INT_MAX; + + int dist = INT_MAX; + int nr, nc, ns, nrr, ncc, nss; + my_rect r[2], rr[2]; + my_circle c[4], cc[4]; + my_seg s[8], ss[8]; + + GetPadElements( type1, x1, y1, w1, l1, r1, angle1, + &nr, r, &nc, c, &ns, s ); + GetPadElements( type2, x2, y2, w2, l2, r2, angle2, + &nrr, rr, &ncc, cc, &nss, ss ); + // now find distance from every element of pad1 to every element of pad2 + for( int ic=0; ic DBL_MAX/10, assume vertical line at x = a +// returns closest point on line in xp, yp +// +double GetPointToLineDistance( double a, double b, int x, int y, double * xpp, double * ypp ) +{ + if( b > DBL_MAX/10 ) + { + // vertical line + if( xpp && ypp ) + { + *xpp = a; + *ypp = y; + } + return abs(a-x); + } + // find c,d such that (x,y) lies on y = c + dx where d=(-1/b) + double d = -1.0/b; + double c = (double)y-d*x; + // find nearest point to (x,y) on line through (xi,yi) to (xf,yf) + double xp = (a-c)/(d-b); + double yp = a + b*xp; + if( xpp && ypp ) + { + *xpp = xp; + *ypp = yp; + } + // find distance + return Distance( x, y, xp, yp ); +} + +/***********************************************************************************/ +double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf ) +/***********************************************************************************/ +/** Function GetPointToLineSegmentDistance + * Get distance between line segment and point + * @param x,y = point + * @param xi,yi and xf,yf = the end-points of the line segment + * @return the distance + */ +{ + // test for vertical or horizontal segment + if( xf==xi ) + { + // vertical line segment + if( InRange( y, yi, yf ) ) + return abs( x - xi ); + else + return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) ); + } + else if( yf==yi ) + { + // horizontal line segment + if( InRange( x, xi, xf ) ) + return abs( y - yi ); + else + return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) ); + } + else + { + // oblique segment + // find a,b such that (xi,yi) and (xf,yf) lie on y = a + bx + double b = (double)(yf-yi)/(xf-xi); + double a = (double)yi-b*xi; + // find c,d such that (x,y) lies on y = c + dx where d=(-1/b) + double d = -1.0/b; + double c = (double)y-d*x; + // find nearest point to (x,y) on line through (xi,yi) to (xf,yf) + double xp = (a-c)/(d-b); + double yp = a + b*xp; + // find distance + if( InRange( xp, xi, xf ) && InRange( yp, yi, yf ) ) + return Distance( x, y, xp, yp ); + else + return min( Distance( x, y, xi, yi ), Distance( x, y, xf, yf ) ); + } +} + +// test for value within range +// +bool InRange( double x, double xi, double xf ) +{ + if( xf>xi ) + { + if( x >= xi && x <= xf ) + return true; + } + else + { + if( x >= xf && x <= xi ) + return true; + } + return false; +} + +// Get distance between 2 points +// +double Distance( int x1, int y1, int x2, int y2 ) +{ + double d; + d = sqrt( (double)(x1-x2)*(x1-x2) + (double)(y1-y2)*(y1-y2) ); + if( d > INT_MAX || d < INT_MIN ) + ASSERT(0); + return (int)d; +} + +// this finds approximate solutions +// note: this works best if el2 is smaller than el1 +// +int GetArcIntersections( EllipseKH * el1, EllipseKH * el2, + double * x1, double * y1, double * x2, double * y2 ) +{ + if( el1->theta2 > el1->theta1 ) + ASSERT(0); + if( el2->theta2 > el2->theta1 ) + ASSERT(0); + + const int NSTEPS = 32; + double xret[2], yret[2]; + + double xscale = 1.0/el1->xrad; + double yscale = 1.0/el1->yrad; + // now transform params of second ellipse into reference frame + // with origin at center if first ellipse, + // scaled so the first ellipse is a circle of radius = 1.0 + double xo = (el2->Center.X - el1->Center.X)*xscale; + double yo = (el2->Center.Y - el1->Center.Y)*yscale; + double xr = el2->xrad*xscale; + double yr = el2->yrad*yscale; + // now test NSTEPS positions in arc, moving clockwise (ie. decreasing theta) + double step = M_PI/((NSTEPS-1)*2.0); + double d_prev, th_prev; + double th_interp; + double th1; + int n = 0; + for( int i=0; itheta1 - i*step; + else + theta = el2->theta2; + double x = xo + xr*cos(theta); + double y = yo + yr*sin(theta); + double d = 1.0 - sqrt(x*x + y*y); + if( i>0 ) + { + bool bInt = false; + if( d >= 0.0 && d_prev <= 0.0 ) + { + th_interp = theta + (step*(-d_prev))/(d-d_prev); + bInt = true; + } + else if( d <= 0.0 && d_prev >= 0.0 ) + { + th_interp = theta + (step*d_prev)/(d_prev-d); + bInt = true; + } + if( bInt ) + { + x = xo + xr*cos(th_interp); + y = yo + yr*sin(th_interp); + th1 = atan2( y, x ); + if( th1 <= el1->theta1 && th1 >= el1->theta2 ) + { + xret[n] = x*el1->xrad + el1->Center.X; + yret[n] = y*el1->yrad + el1->Center.Y; + n++; + if( n > 2 ) + ASSERT(0); + } + } + } + d_prev = d; + th_prev = theta; + } + if( x1 ) + *x1 = xret[0]; + if( y1 ) + *y1 = yret[0]; + if( x2 ) + *x2 = xret[1]; + if( y2 ) + *y2 = yret[1]; + return n; +} + +// this finds approximate solution +// +//double GetSegmentClearance( EllipseKH * el1, EllipseKH * el2, +double GetArcClearance( EllipseKH * el1, EllipseKH * el2, + double * x1, double * y1 ) +{ + const int NSTEPS = 32; + + if( el1->theta2 > el1->theta1 ) + ASSERT(0); + if( el2->theta2 > el2->theta1 ) + ASSERT(0); + + // test multiple positions in both arcs, moving clockwise (ie. decreasing theta) + double th_start = el1->theta1; + double th_end = el1->theta2; + double th_start2 = el2->theta1; + double th_end2 = el2->theta2; + double dmin = DBL_MAX; + double xmin, ymin, thmin, thmin2; + + int nsteps = NSTEPS; + int nsteps2 = NSTEPS; + double step = (th_start-th_end)/(nsteps-1); + double step2 = (th_start2-th_end2)/(nsteps2-1); + while( (step * max(el1->xrad, el1->yrad)) > 1.0*NM_PER_MIL + && (step2 * max(el2->xrad, el2->yrad)) > 1.0*NM_PER_MIL ) + { + step = (th_start-th_end)/(nsteps-1); + for( int i=0; iCenter.X + el1->xrad*cos(theta); + double y = el1->Center.Y + el1->yrad*sin(theta); + step2 = (th_start2-th_end2)/(nsteps2-1); + for( int i2=0; i2Center.X + el2->xrad*cos(theta2); + double y2 = el2->Center.Y + el2->yrad*sin(theta2); + double d = Distance( x, y, x2, y2 ); + if( d < dmin ) + { + dmin = d; + xmin = x; + ymin = y; + thmin = theta; + thmin2 = theta2; + } + } + } + if( step > step2 ) + { + th_start = min(el1->theta1, thmin + step); + th_end = max(el1->theta2, thmin - step); + step = (th_start - th_end)/nsteps; + } + else + { + th_start2 = min(el2->theta1, thmin2 + step2); + th_end2 = max(el2->theta2, thmin2 - step2); + step2 = (th_start2 - th_end2)/nsteps2; + } + } + if( x1 ) + *x1 = xmin; + if( y1 ) + *y1 = ymin; + return dmin; +} + diff --git a/polygon/math_for_graphics.h b/polygon/math_for_graphics.h new file mode 100644 index 0000000000..75db51c9ae --- /dev/null +++ b/polygon/math_for_graphics.h @@ -0,0 +1,104 @@ +// math stuff for graphics, from FreePCB + +typedef struct PointTag +{ + double X,Y; +} Point; + +typedef struct EllipseTag +{ + Point Center; /* ellipse center */ +// double MaxRad,MinRad; /* major and minor axis */ +// double Phi; /* major axis rotation */ + double xrad, yrad; // radii on x and y + double theta1, theta2; // start and end angle for arc +} EllipseKH; + +const CPoint zero(0,0); + +class my_circle { +public: + my_circle(){}; + my_circle( int xx, int yy, int rr ) + { + x = xx; + y = yy; + r = rr; + }; + int x, y, r; +}; + +class my_rect { +public: + my_rect(){}; + my_rect( int xi, int yi, int xf, int yf ) + { + xlo = min(xi,xf); + xhi = max(xi,xf); + ylo = min(yi,yf); + yhi = max(yi,yf); + }; + int xlo, ylo, xhi, yhi; +}; + +class my_seg { +public: + my_seg(){}; + my_seg( int xxi, int yyi, int xxf, int yyf ) + { + xi = xxi; + yi = yyi; + xf = xxf; + yf = yyf; + }; + int xi, yi, xf, yf; +}; + +// math stuff for graphics +BOOL Quadratic( double a, double b, double c, double *x1, double *x2 ); +void DrawArc( CDC * pDC, int shape, int xxi, int yyi, int xxf, int yyf, BOOL bMeta=FALSE ); +void RotatePoint( CPoint *p, int angle, CPoint org ); +void RotateRect( CRect *r, int angle, CPoint org ); +int TestLineHit( int xi, int yi, int xf, int yf, int x, int y, double dist ); +int FindLineIntersection( double a, double b, double c, double d, double * x, double * y ); +int FindLineSegmentIntersection( double a, double b, int xi, int yi, int xf, int yf, int style, + double * x1, double * y1, double * x2, double * y2, double * dist=NULL ); +int FindSegmentIntersections( int xi, int yi, int xf, int yf, int style, + int xi2, int yi2, int xf2, int yf2, int style2, + double x[]=NULL, double y[]=NULL ); +BOOL FindLineEllipseIntersections( double a, double b, double c, double d, double *x1, double *x2 ); +BOOL FindVerticalLineEllipseIntersections( double a, double b, double x, double *y1, double *y2 ); +BOOL TestForIntersectionOfStraightLineSegments( int x1i, int y1i, int x1f, int y1f, + int x2i, int y2i, int x2f, int y2f, + int * x=NULL, int * y=NULL, double * dist=NULL ); +void GetPadElements( int type, int x, int y, int wid, int len, int radius, int angle, + int * nr, my_rect r[], int * nc, my_circle c[], int * ns, my_seg s[] ); +int GetClearanceBetweenPads( int type1, int x1, int y1, int w1, int l1, int r1, int angle1, + int type2, int x2, int y2, int w2, int l2, int r2, int angle2 ); +int GetClearanceBetweenSegmentAndPad( int x1, int y1, int x2, int y2, int w, + int type, int x, int y, int wid, int len, + int radius, int angle ); +int GetClearanceBetweenSegments( int x1i, int y1i, int x1f, int y1f, int style1, int w1, + int x2i, int y2i, int x2f, int y2f, int style2, int w2, + int max_cl, int * x, int * y ); + +/** Function GetPointToLineSegmentDistance + * Get distance between line segment and point + * @param x,y = point + * @param xi,yi and xf,yf = the end-points of the line segment + * @return the distance + */ +double GetPointToLineSegmentDistance( int x, int y, int xi, int yi, int xf, int yf ); + +double GetPointToLineDistance( double a, double b, int x, int y, double * xp=NULL, double * yp=NULL ); +BOOL InRange( double x, double xi, double xf ); +double Distance( int x1, int y1, int x2, int y2 ); +int GetArcIntersections( EllipseKH * el1, EllipseKH * el2, + double * x1=NULL, double * y1=NULL, + double * x2=NULL, double * y2=NULL ); +CPoint GetInflectionPoint( CPoint pi, CPoint pf, int mode ); + +// quicksort (2-way or 3-way) +void quickSort(int numbers[], int index[], int array_size); +void q_sort(int numbers[], int index[], int left, int right); +void q_sort_3way( int a[], int b[], int left, int right );