/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020 KiCad Developers. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ // WARNING - this Tom's crappy PNS hack tool code. Please don't complain about its quality // (unless you want to improve it). #include #include #include #include #include #include #include #include "pns_log.h" #include "router/pns_diff_pair.h" #include "pns_log_viewer_frame_base.h" #include "qa/drc_proto/drc_proto.h" #define ID_LIST_COPY 10001 #define ID_LIST_SHOW_ALL 10002 #define ID_LIST_SHOW_NONE 10003 class PNS_LOG_VIEWER_FRAME : public PNS_LOG_VIEWER_FRAME_BASE, public PCB_TEST_FRAME_BASE { public: PNS_LOG_VIEWER_FRAME( wxFrame* frame ) : PNS_LOG_VIEWER_FRAME_BASE( frame ) { LoadSettings(); createView( this, PCB_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ); m_viewSizer->Add( m_galPanel.get(), 1, wxEXPAND, 5 ); Layout(); Show( true ); Maximize(); Raise(); auto settings = static_cast( m_galPanel->GetView()->GetPainter()->GetSettings() ); settings->SetZoneDisplayMode( ZONE_DISPLAY_MODE::SHOW_ZONE_OUTLINE ); m_listPopupMenu = new wxMenu(wxT("")); m_listPopupMenu->Append(ID_LIST_COPY, wxT("Copy selected geometry"), wxT(""), wxITEM_NORMAL); m_listPopupMenu->Append(ID_LIST_SHOW_ALL, wxT("Show all"), wxT (""), wxITEM_NORMAL); m_listPopupMenu->Append(ID_LIST_SHOW_NONE, wxT("Show none"), wxT (""), wxITEM_NORMAL); m_itemList->Connect(m_itemList->GetId(),wxEVT_TREELIST_ITEM_CONTEXT_MENU,wxMouseEventHandler(PNS_LOG_VIEWER_FRAME::onListRightClick),NULL,this); //m_itemList->Connect(m_itemList->GetId(),wxEVT_LISTBOX,wxCommandEventHandler(PNS_LOG_VIEWER_FRAME::onListSelect),NULL,this); m_itemList->Connect(m_itemList->GetId(),wxEVT_TREELIST_SELECTION_CHANGED,wxCommandEventHandler(PNS_LOG_VIEWER_FRAME::onListSelect),NULL,this); m_itemList->Connect(m_itemList->GetId(),wxEVT_TREELIST_ITEM_CHECKED,wxCommandEventHandler(PNS_LOG_VIEWER_FRAME::onListChecked),NULL,this); //Connect(ID_LIST_COPY,wxEVT_COMMAND_MENU_SELECTED,wxCommandEventHandler(PNS_LOG_VIEWER_FRAME::onListCopy),NULL,this); //Connect(ID_LIST_SHOW_ALL,wxEVT_COMMAND_MENU_SELECTED,wxCommandEventHandler(PNS_LOG_VIEWER_FRAME::onListShowAll),NULL,this); //Connect(ID_LIST_SHOW_NONE,wxEVT_COMMAND_MENU_SELECTED,wxCommandEventHandler(PNS_LOG_VIEWER_FRAME::onListShowNone),NULL,this); m_itemList->AppendColumn ( "Type" ); m_itemList->AppendColumn ( "Value" ); } virtual ~PNS_LOG_VIEWER_FRAME() { } void SetLogFile( PNS_LOG_FILE* aLog ); private: void drawLoggedItems( int iter ); void updateDumpPanel( int iter ); virtual void createUserTools() override; void buildListTree( wxTreeListItem item, PNS_TEST_DEBUG_DECORATOR::DEBUG_ENT* ent ); void syncModel(); PNS_TEST_DEBUG_DECORATOR::STAGE* getCurrentStage(); virtual void onReload( wxCommandEvent& event ) override; virtual void onExit( wxCommandEvent& event ) override; virtual void onRewindScroll( wxScrollEvent& event ) override; virtual void onRewindCountText( wxCommandEvent& event ) override; virtual void onListRightClick(wxMouseEvent& event); virtual void onListShowAll( wxCommandEvent& event ); virtual void onListShowNone( wxCommandEvent& event ); virtual void onListCopy( wxCommandEvent& event ); virtual void onListSelect( wxCommandEvent& event ); virtual void onBtnRewindLeft( wxCommandEvent& event ) override; virtual void onBtnRewindRight( wxCommandEvent& event ) override; virtual void onListChecked( wxCommandEvent& event ); std::shared_ptr m_overlay; std::shared_ptr m_logFile; std::shared_ptr m_env; int m_rewindIter; wxMenu* m_listPopupMenu; }; class WX_SHAPE_TREE_ITEM_DATA : public wxClientData { public: WX_SHAPE_TREE_ITEM_DATA(PNS_TEST_DEBUG_DECORATOR::DEBUG_ENT *item ) : m_item(item) {}; PNS_TEST_DEBUG_DECORATOR::DEBUG_ENT *m_item; }; void PNS_LOG_VIEWER_FRAME::createUserTools() { } static const COLOR4D assignColor( int aStyle ) { COLOR4D color; switch( aStyle ) { case 0: color = COLOR4D( 0, 1, 0, 1 ); break; case 1: color = COLOR4D( 1, 0, 0, 1 ); break; case 2: color = COLOR4D( 1, 1, 0, 1 ); break; case 3: color = COLOR4D( 0, 0, 1, 1 ); break; case 4: color = COLOR4D( 1, 1, 1, 1 ); break; case 5: color = COLOR4D( 1, 1, 0, 1 ); break; case 6: color = COLOR4D( 0, 1, 1, 1 ); break; case 32: color = COLOR4D( 0, 0, 1, 1 ); break; default: color = COLOR4D( 0.4, 0.4, 0.4, 1 ); break; } return color; } PNS_TEST_DEBUG_DECORATOR::STAGE* PNS_LOG_VIEWER_FRAME::getCurrentStage() { PNS_TEST_DEBUG_DECORATOR* dbgd = m_env->GetDebugDecorator(); int count = dbgd->GetStageCount(); int iter = m_rewindIter; if( count <= 0 ) return nullptr; if( iter < 0 ) iter = 0; if( iter >= count ) iter = count - 1; return dbgd->GetStage( iter ); } void PNS_LOG_VIEWER_FRAME::drawLoggedItems( int iter ) { if( !m_env ) return; m_overlay = m_galPanel->DebugOverlay(); m_overlay->Clear(); PNS_TEST_DEBUG_DECORATOR::STAGE* st = getCurrentStage(); if( !st ) return; auto drawShapes = [ & ] ( PNS_TEST_DEBUG_DECORATOR::DEBUG_ENT *ent ) -> bool { bool isEnabled = ent->IsVisible(); bool isSelected = false; if (! isEnabled ) return true; for( auto& sh : ent->m_shapes ) { m_overlay->SetIsStroke( true ); m_overlay->SetIsFill( false ); m_overlay->SetStrokeColor( assignColor( ent->m_color ) ); m_overlay->SetLineWidth( ent->m_width ); switch( sh->Type() ) { case SH_CIRCLE: { auto cir = static_cast( sh ); m_overlay->Circle( cir->GetCenter(), cir->GetRadius() ); break; } case SH_RECT: { auto rect = static_cast( sh ); m_overlay->Rectangle( rect->GetPosition(), rect->GetPosition() + rect->GetSize() ); break; } case SH_LINE_CHAIN: { auto lc = static_cast( sh ); if( isSelected ) { m_overlay->SetLineWidth( ent->m_width * 2 ); m_overlay->SetStrokeColor( COLOR4D(1.0, 1.0, 1.0, 1.0) ); } for( int i = 0; i < lc->SegmentCount(); i++ ) { auto s = lc->CSegment( i ); m_overlay->Line( s.A, s.B ); } break; } default: break; } } return true; }; st->m_entries->IterateTree( drawShapes ); /* if ( !m_itemList->IsChecked(itemID) || !ent.m_shapes.size() ) { if( itemID != m_selectedItem ) { itemID++; continue; } }*/ //m_galPanel->ForceRefresh(); m_galPanel->GetView()->MarkDirty(); m_galPanel->GetParent()->Refresh(); } static BOARD* loadBoard( const std::string& filename ) { PLUGIN::RELEASER pi( new PCB_IO ); BOARD* brd = nullptr; try { brd = pi->Load( wxString( filename.c_str() ), NULL, NULL ); } catch( const IO_ERROR& ioe ) { wxString msg = wxString::Format( _( "Error loading board.\n%s" ), ioe.Problem() ); printf( "Board Loading Error: '%s'\n", (const char*) msg.mb_str() ); return nullptr; } return brd; } void PNS_LOG_VIEWER_FRAME::SetLogFile( PNS_LOG_FILE* aLog ) { m_logFile.reset( aLog ); SetBoard( m_logFile->GetBoard() ); m_env.reset( new PNS_TEST_ENVIRONMENT ); m_env->SetMode( PNS::PNS_MODE_ROUTE_SINGLE ); m_env->ReplayLog( m_logFile.get() ); auto dbgd = m_env->GetDebugDecorator(); int n_stages = dbgd->GetStageCount(); m_rewindSlider->SetMax( n_stages - 1 ); m_rewindSlider->SetValue( n_stages - 1 ); m_rewindIter = n_stages - 1; auto extents = m_board->GetBoundingBox(); BOX2D bbd; bbd.SetOrigin( extents.GetOrigin() ); bbd.SetWidth( extents.GetWidth() ); bbd.SetHeight( extents.GetHeight() ); bbd.Inflate( std::min( bbd.GetWidth(), bbd.GetHeight() )/ 5); m_galPanel->GetView()->SetViewport( bbd ); drawLoggedItems( m_rewindIter ); updateDumpPanel( m_rewindIter ); } void PNS_LOG_VIEWER_FRAME::onReload( wxCommandEvent& event ) { event.Skip(); } void PNS_LOG_VIEWER_FRAME::onExit( wxCommandEvent& event ) { event.Skip(); } void PNS_LOG_VIEWER_FRAME::onListChecked( wxCommandEvent& event ) { printf("On-list-checked\n"); syncModel(); drawLoggedItems(m_rewindIter); } void PNS_LOG_VIEWER_FRAME::onRewindScroll( wxScrollEvent& event ) { m_rewindIter = event.GetPosition(); drawLoggedItems(m_rewindIter); updateDumpPanel(m_rewindIter); char str[128]; sprintf(str,"%d",m_rewindIter); m_rewindPos->SetValue( str ); event.Skip(); } void PNS_LOG_VIEWER_FRAME::onBtnRewindLeft( wxCommandEvent& event ) { if(m_rewindIter > 0) { m_rewindIter--; drawLoggedItems(m_rewindIter); updateDumpPanel(m_rewindIter); char str[128]; sprintf(str,"%d",m_rewindIter); m_rewindPos->SetValue( str ); } } void PNS_LOG_VIEWER_FRAME::onBtnRewindRight( wxCommandEvent& event ) { auto dbgd = m_env->GetDebugDecorator(); int count = dbgd->GetStageCount(); if(m_rewindIter < count) { m_rewindIter++; drawLoggedItems(m_rewindIter); updateDumpPanel(m_rewindIter); char str[128]; sprintf(str,"%d",m_rewindIter); m_rewindPos->SetValue( str ); } } void PNS_LOG_VIEWER_FRAME::onRewindCountText( wxCommandEvent& event ) { if( !m_env ) return; int val = wxAtoi( m_rewindPos->GetValue() ); auto dbgd = m_env->GetDebugDecorator(); int count = dbgd->GetStageCount(); if( val < 0 ) val = 0; if( val >= count ) val = count - 1; m_rewindIter = val; m_rewindSlider->SetValue( m_rewindIter ); drawLoggedItems( m_rewindIter ); updateDumpPanel( m_rewindIter ); event.Skip(); } void PNS_LOG_VIEWER_FRAME::syncModel() { printf("SyncModel\n"); for( wxTreeListItem item = m_itemList->GetFirstItem(); item.IsOk(); item = m_itemList->GetNextItem( item ) ) { WX_SHAPE_TREE_ITEM_DATA* idata = static_cast ( m_itemList->GetItemData( item ) ); printf("IDATA %p\n", idata); if( idata ) { idata->m_item->m_visible = m_itemList->GetCheckedState( item ) == wxCHK_CHECKED; } } } void PNS_LOG_VIEWER_FRAME::onListRightClick(wxMouseEvent& event) { fprintf(stderr,"OnListMenu\n"); //m_itemList->PopupMenu(m_listPopupMenu); auto sel = m_itemList->GetPopupMenuSelectionFromUser( *m_listPopupMenu ); switch( sel ) { case ID_LIST_SHOW_NONE: m_itemList->CheckItemRecursively( m_itemList->GetRootItem(), wxCHK_UNCHECKED ); syncModel(); drawLoggedItems( m_rewindIter ); break; case ID_LIST_SHOW_ALL: m_itemList->CheckItemRecursively( m_itemList->GetRootItem(), wxCHK_CHECKED ); syncModel(); drawLoggedItems( m_rewindIter ); break; } } void PNS_LOG_VIEWER_FRAME::onListShowAll( wxCommandEvent& event ) { } void PNS_LOG_VIEWER_FRAME::onListShowNone( wxCommandEvent& event ) { m_itemList->CheckItemRecursively( m_itemList->GetRootItem(), wxCHK_UNCHECKED ); drawLoggedItems( m_rewindIter ); } void PNS_LOG_VIEWER_FRAME::onListCopy( wxCommandEvent& event ) { wxString s; int sel = 0; //m_itemList->GetSelection(); auto dbgd = m_env->GetDebugDecorator(); int count = dbgd->GetStageCount(); if( count <= 0 ) return; int iter = m_rewindIter; if( iter < 0 ) iter = 0; if( iter >= count ) iter = count - 1; const PNS_TEST_DEBUG_DECORATOR::STAGE* st = dbgd->GetStage( iter ); // if ( st->m_entries.size() < sel ) // return; const PNS_TEST_DEBUG_DECORATOR::DEBUG_ENT& ent = st->m_entries[sel]; for( auto& sh : ent.m_shapes ) { s += sh->Format() + " "; } if (wxTheClipboard->Open()) { // This data objects are held by the clipboard, // so do not delete them in the app. wxTheClipboard->SetData( new wxTextDataObject(s) ); wxTheClipboard->Close(); } } void PNS_LOG_VIEWER_FRAME::onListSelect( wxCommandEvent& event ) { printf("OnListSelect!\n"); drawLoggedItems( m_rewindIter ); } void PNS_LOG_VIEWER_FRAME::buildListTree( wxTreeListItem item, PNS_TEST_DEBUG_DECORATOR::DEBUG_ENT* ent ) { printf("LOG append %p\n", ent ); wxTreeListItem ritem; if( ent->m_children.size() ) ritem = m_itemList->AppendItem( item, "Child" ); else ritem = item; //ent->m_item = ritem; if( ent->m_msg.length() ) { m_itemList->SetItemText( ritem, 0, "Message" ); m_itemList->SetItemText( ritem, 1, ent->m_msg ); } else { m_itemList->SetItemText( ritem, 0, "Shapes" ); m_itemList->SetItemText( ritem, 1, ent->m_name ); } m_itemList->SetItemData( ritem, new WX_SHAPE_TREE_ITEM_DATA( ent ) ); for( auto child : ent->m_children ) { auto citem = m_itemList->AppendItem( ritem, "" ); buildListTree( citem, child ); } } static void expandAllChildren( wxTreeListCtrl* tree, const wxTreeListItem& item) { tree->Freeze(); // expand this item first, this might result in its children being added on // the fly if ( item != tree->GetRootItem() ) tree->Expand(item); //else: expanding hidden root item is unsupported and unnecessary // then (recursively) expand all the children for ( auto idCurr = tree->GetFirstChild(item); idCurr.IsOk(); idCurr = tree->GetNextSibling(item) ) { expandAllChildren(tree, idCurr); } tree->Thaw(); } void PNS_LOG_VIEWER_FRAME::updateDumpPanel( int iter ) { if ( !m_env ) return; auto dbgd = m_env->GetDebugDecorator(); int count = dbgd->GetStageCount(); wxArrayString dumpStrings; if( count <= 0 ) return; if( iter < 0 ) iter = 0; if( iter >= count ) iter = count - 1; auto st = dbgd->GetStage( iter ); auto rootItem = m_itemList->GetRootItem(); printf("ItemList: %p\n", m_itemList ); m_itemList->DeleteAllItems(); buildListTree( rootItem, st->m_entries ); m_itemList->CheckItemRecursively ( rootItem, wxCHK_CHECKED ); //m_itemList->Expand( List->GetRootItem() ); //expandAllChildren( m_itemList, m_itemList->GetRootItem() ); m_itemList->Refresh(); } static bool commonParallelProjection( SEG p, SEG n, SEG &pClip, SEG& nClip ) { SEG n_proj_p( p.LineProject( n.A ), p.LineProject( n.B ) ); int64_t t_a = 0; int64_t t_b = p.TCoef( p.B ); int64_t tproj_a = p.TCoef( n_proj_p.A ); int64_t tproj_b = p.TCoef( n_proj_p.B ); if( t_b < t_a ) std::swap( t_b, t_a ); if( tproj_b < tproj_a ) std::swap( tproj_b, tproj_a ); if( t_b <= tproj_a ) return false; if( t_a >= tproj_b ) return false; int64_t t[4] = { 0, p.TCoef( p.B ), p.TCoef( n_proj_p.A ), p.TCoef( n_proj_p.B ) }; std::vector tv( t, t + 4 ); std::sort( tv.begin(), tv.end() ); // fixme: awful and disgusting way of finding 2 midpoints int64_t pLenSq = p.SquaredLength(); VECTOR2I dp = p.B - p.A; pClip.A.x = p.A.x + rescale( (int64_t)dp.x, tv[1], pLenSq ); pClip.A.y = p.A.y + rescale( (int64_t)dp.y, tv[1], pLenSq ); pClip.B.x = p.A.x + rescale( (int64_t)dp.x, tv[2], pLenSq ); pClip.B.y = p.A.y + rescale( (int64_t)dp.y, tv[2], pLenSq ); nClip.A = n.LineProject( pClip.A ); nClip.B = n.LineProject( pClip.B ); return true; } #if 0 using namespace PNS; template typename std::map::iterator findClosestKey( std::map & data, T1 key) { if (data.size() == 0) { return data.end(); } auto lower = data.lower_bound(key); if (lower == data.end()) // If none found, return the last one. return std::prev(lower); if (lower == data.begin()) return lower; // Check which one is closest. auto previous = std::prev(lower); if ((key - previous->first) < (lower->first - key)) return previous; return lower; } void extractDiffPairItems ( PNS::NODE* node, int net_p, int net_n ) { std::set pendingItems, complementItems; node->AllItemsInNet( net_p, pendingItems, PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ); node->AllItemsInNet( net_n, complementItems, PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ); while( pendingItems.size() ) { PNS::LINE l = node->AssembleLine( static_cast( *pendingItems.begin() ) ); printf("l net %d segs %d layer %d\n", net_p, l.SegmentCount(), l.Layer() ); std::map gapMap; std::map coupledCandidates; for( auto li : l.Links() ) { pendingItems.erase( li ); auto sp = dyn_cast ( li ); if(!sp) continue; for ( auto ci : complementItems ) { auto sn = dyn_cast ( ci ); if( !sn->Layers().Overlaps( sp->Layers() )) continue; auto ssp = sp->Seg(); auto ssn = sn->Seg(); if( ssp.ApproxParallel(ssn) ) { SEG ca, cb; bool coupled = commonParallelProjection( ssp, ssn, ca, cb ); if( coupled ) { /* g_overlay->SetStrokeColor( KIGFX::COLOR4D( 1.0, 0.8, 0.8, 1.0 ) ); g_overlay->SetIsFill(false); g_overlay->SetIsStroke(true ); g_overlay->SetLineWidth( 10000 ); g_overlay->Line( ca ); g_overlay->SetStrokeColor( KIGFX::COLOR4D( 0.8, 0.8, 1.0, 1.0 ) ); g_overlay->Line( cb );*/ int len = ca.Length(); int gap = (int)(ca.A - cb.A).EuclideanNorm() - (sp->Width()+sn->Width()) / 2; auto closestGap = findClosestKey( gapMap, gap ); coupledCandidates[sn] = gap; if( closestGap == gapMap.end() || std::abs(closestGap->first - gap) > 50 ) { gapMap[gap] = len; } else { closestGap->second += len; } printf("Seg %p %p gap %d dist %d\n", sp, sn, gap, len ); } } } } int bestGap = -1; int maxLength = 0; for( auto i : gapMap ) { if( i.second > maxLength ) { maxLength = i.second; bestGap = i.first; } } printf("Best gap: %d\n", bestGap); bool pendingCandidates = true; while ( pendingCandidates ) { pendingCandidates = false; for ( auto c : coupledCandidates ) { if( std::abs(c.second - bestGap ) < 50 ) { PNS::LINE l_coupled = node->AssembleLine( c.first ); PNS::DIFF_PAIR pair(l, l_coupled, bestGap ); pair.SetNets( l.Net(), l_coupled.Net() ); /* g_overlay->SetStrokeColor( KIGFX::COLOR4D( 1.0, 0.8, 0.8, 1.0 ) ); g_overlay->SetIsFill(true); g_overlay->SetIsStroke(true ); g_overlay->SetLineWidth( 10000 ); g_overlay->Polyline( l.CLine() ); g_overlay->SetStrokeColor( KIGFX::COLOR4D( 0.8, 0.8, 1.0, 1.0 ) ); g_overlay->Polyline( l_coupled.CLine() ); */ for( auto li : l_coupled.Links() ) coupledCandidates.erase( li ); printf("Orig line : %d segs, coupled line: %d segs\n", l.SegmentCount(), l_coupled.SegmentCount() ); pendingCandidates = true; break; } } } } } int pns1_main_func( int argc, char* argv[] ) { auto frame = new PNS_LOG_VIEWER_FRAME(nullptr); std::shared_ptr board; board.reset( loadBoard ( argv[1] ) ); frame->SetBoard( board ); Pgm().App().SetTopWindow( frame ); // wxApp gets a face. frame->Show(); PNS_KICAD_IFACE_BASE iface; PNS::NODE *world = new PNS::NODE; iface.SetBoard( board.get() ); iface.SyncWorld( world ); #if 0 std::map diffPairs; // FIXMe: GetNetInfo copies f***ing pointers... const auto& netinfo = board->GetNetInfo(); int n_nets = netinfo.GetNetCount(); for (int net = 0 ; net < n_nets; net++ ) { int coupled = iface.GetRuleResolver()->DpCoupledNet( net ); if( coupled > 0 && diffPairs.find( coupled ) == diffPairs.end() ) { printf("net %d coupled %d\n", net, coupled ); diffPairs[net] = coupled; } } for( auto p : diffPairs ) { extractDiffPairItems ( world, p.first, p.second ); } #endif return 0; } static bool registered = UTILITY_REGISTRY::Register( { "pns1", "PNS Dumb Test (Tom's hacks)", pns1_main_func, } ); #endif //extern "C" void drcCreateTestsProviderClearance(); //extern "C" void drcCreateTestsProviderEdgeClearance(); int replay_main_func( int argc, char* argv[] ) { auto frame = new PNS_LOG_VIEWER_FRAME(nullptr); // drcCreateTestsProviderClearance(); // drcCreateTestsProviderEdgeClearance(); if( argc >= 2 && std::string(argv[1]) == "-h") { printf("PNS Log (Re)player. Allows to step through the log written by the ROUTER_TOOL in debug Kicad builds. "); printf("Requires a board file with UUIDs and a matching log file. Both are written to /tmp when you press '0' during routing."); return 0; } if(argc < 3) { printf("Expected parameters: log_file.log board_file.dump\n"); return 0; } PNS_LOG_FILE* logFile = new PNS_LOG_FILE; logFile->Load( argv[1], argv[2] ); frame->SetLogFile( logFile ); //SetTopFrame( frame ); // wxApp gets a face. return 0; } static bool registered2 = UTILITY_REGISTRY::Register( { "replay", "PNS Log Player", replay_main_func, } ); bool commonParallelProjection( SEG p, SEG n, SEG &pClip, SEG& nClip ); template typename std::map::iterator findClosestKey( std::map & data, T1 key) { if (data.size() == 0) { return data.end(); } auto lower = data.lower_bound(key); if (lower == data.end()) // If none found, return the last one. return std::prev(lower); if (lower == data.begin()) return lower; // Check which one is closest. auto previous = std::prev(lower); if ((key - previous->first) < (lower->first - key)) return previous; return lower; } void extractDiffPair( BOARD* aBoard, int net_p, int net_n ) { std::set pendingItems, complementItems; struct DP_COUPLED_SEGMENTS { TRACK* itemP, *itemN; }; for( auto trk: aBoard->Tracks() ) { if( trk->Type() == PCB_TRACE_T || trk->Type() == PCB_ARC_T ) { if( trk->GetNetCode() == net_p ) pendingItems.insert( trk ); else if( trk->GetNetCode() == net_n ) complementItems.insert( trk ); } } while( pendingItems.size() ) { auto li = *pendingItems.begin(); pendingItems.erase( li ); auto sp = dyn_cast(li); if(!sp) continue; for ( auto ci : complementItems ) { auto sn = dyn_cast ( ci ); if( ( sn->GetLayerSet() & sp->GetLayerSet() ).none() ) continue; SEG ssp ( sp->GetStart(), sp->GetEnd() ); SEG ssn ( sn->GetStart(), sn->GetEnd() ); if( ssp.ApproxParallel(ssn) ) { SEG ca, cb; bool coupled = commonParallelProjection( ssp, ssn, ca, cb ); } } } } static int matchDpSuffix( const wxString& aNetName, wxString& aComplementNet, wxString& aBaseDpName ) { int rv = 0; if( aNetName.EndsWith( "+" ) ) { aComplementNet = "-"; rv = 1; } else if( aNetName.EndsWith( "P" ) ) { aComplementNet = "N"; rv = 1; } else if( aNetName.EndsWith( "-" ) ) { aComplementNet = "+"; rv = -1; } else if( aNetName.EndsWith( "N" ) ) { aComplementNet = "P"; rv = -1; } // Match P followed by 2 digits else if( aNetName.Right( 2 ).IsNumber() && aNetName.Right( 3 ).Left( 1 ) == "P" ) { aComplementNet = "N" + aNetName.Right( 2 ); rv = 1; } // Match P followed by 1 digit else if( aNetName.Right( 1 ).IsNumber() && aNetName.Right( 2 ).Left( 1 ) == "P" ) { aComplementNet = "N" + aNetName.Right( 1 ); rv = 1; } // Match N followed by 2 digits else if( aNetName.Right( 2 ).IsNumber() && aNetName.Right( 3 ).Left( 1 ) == "N" ) { aComplementNet = "P" + aNetName.Right( 2 ); rv = -1; } // Match N followed by 1 digit else if( aNetName.Right( 1 ).IsNumber() && aNetName.Right( 2 ).Left( 1 ) == "N" ) { aComplementNet = "P" + aNetName.Right( 1 ); rv = -1; } if( rv != 0 ) { aBaseDpName = aNetName.Left( aNetName.Length() - aComplementNet.Length() ); aComplementNet = aBaseDpName + aComplementNet; } return rv; } static int dpCoupledNet( BOARD* aBoard, int aNet ) { wxString refName = aBoard->FindNet( aNet )->GetNetname(); wxString dummy, coupledNetName; if( matchDpSuffix( refName, coupledNetName, dummy ) ) { NETINFO_ITEM* net = aBoard->FindNet( coupledNetName ); if( !net ) return -1; return net->GetNetCode(); } return -1; } void extractAllDiffPairs( BOARD* aBoard ) { std::map diffPairs; // FIXMe: GetNetInfo copies f***ing pointers... const auto& netinfo = aBoard->GetNetInfo(); int n_nets = netinfo.GetNetCount(); for (int net = 0 ; net < n_nets; net++ ) { int coupled = dpCoupledNet(aBoard, net ); if( coupled > 0 && diffPairs.find( coupled ) == diffPairs.end() ) { diffPairs[net] = coupled; } } for( auto p : diffPairs ) { printf("Extract DP: %s/%s\n", (const char*) aBoard->FindNet( p.first )->GetNetname(), (const char*) aBoard->FindNet( p.second )->GetNetname() ); extractDiffPair ( aBoard, p.first, p.second ); } } #if 0 int test1_main_func( int argc, char* argv[] ) { auto frame = new PNS_LOG_VIEWER_FRAME(nullptr); PROJECT_CONTEXT project = loadKicadProject( argv[1], OPT() ); frame->SetBoard( project.board); Pgm().App().SetTopWindow( frame ); // wxApp gets a face. frame->Show(); auto view = frame->GetPanel()->GetView(); //auto settings = static_cast( view->GetPainter()->GetSettings() ); //settings->SetZoneDisplayMode( ZONE_DISPLAY_MODE:: ); /* view->SetLayerVisible( LAYER_TRACKS, false ); view->SetLayerVisible( LAYER_PADS, false ); view->SetLayerVisible( LAYER_MOD_TEXT_BK, false ); view->SetLayerVisible( LAYER_MOD_TEXT_FR, false ); view->SetLayerVisible( LAYER_MOD_REFERENCES, false ); view->SetLayerVisible( LAYER_MOD_VALUES, false ); view->SetLayerVisible( LAYER_MOD_BK, false ); view->SetLayerVisible( LAYER_MOD_FR, false );*/ runDRCProto( project, frame->GetPanel()->DebugOverlay() ); return 0; } #endif //std::shared_ptr g_overlay; static std::shared_ptr overlay; bool segmentCrossesHullBoundary( const SHAPE_LINE_CHAIN& hull, const SEG& seg) { for( int j = 0; j < hull.SegmentCount(); j++ ) { auto sr = hull.CSegment(j); } } bool walkaround2( SHAPE_LINE_CHAIN& aLine, SHAPE_LINE_CHAIN aObstacle, SHAPE_LINE_CHAIN& aPre, SHAPE_LINE_CHAIN& aWalk, SHAPE_LINE_CHAIN& aPost, bool aCw ) { const SHAPE_LINE_CHAIN& line( aLine ); if( line.SegmentCount() < 1 ) return false; const auto pFirst = line.CPoint(0); const auto pLast = line.CPoint(-1); bool inFirst = aObstacle.PointInside( pFirst ) && !aObstacle.PointOnEdge( pFirst ); bool inLast = aObstacle.PointInside( pLast ) && !aObstacle.PointOnEdge( pLast ); if( inFirst || inLast ) { return false; } enum VERTEX_TYPE { INSIDE = 0, OUTSIDE, ON_EDGE }; struct VERTEX { VERTEX_TYPE type; bool isHull; VECTOR2I pos; std::vector neighbours; int indexp, indexh; bool visited = false; }; SHAPE_LINE_CHAIN::INTERSECTIONS ips; line.Intersect( aObstacle, ips ); SHAPE_LINE_CHAIN pnew(aLine), hnew(aObstacle); std::vector vts; auto findVertex = [&]( VECTOR2I pos) -> VERTEX* { for(auto& v : vts) if(v.pos == pos ) return &v; return nullptr; }; for( auto ip : ips ) { bool isNewP, isNewH; if( pnew.Find( ip.p ) < 0 ) { pnew.Split(ip.p); //overlay->SetStrokeColor( YELLOW ); //overlay->Circle( ip.p, 200000 ); } if( hnew.Find( ip.p ) < 0 ) { hnew.Split(ip.p); //overlay->SetStrokeColor( CYAN ); //overlay->Circle( ip.p, 250000 ); } } if ( !aCw ) hnew = hnew.Reverse(); for( int i = 0; i < pnew.PointCount(); i++ ) { auto p = pnew.CPoint(i); VERTEX v; v.indexp = i; v.isHull = false; v.pos = p; v.type = hnew.PointInside( p ) && !hnew.PointOnEdge( p ) ? INSIDE : hnew.PointOnEdge( p ) ? ON_EDGE : OUTSIDE; vts.push_back(v); overlay->SetStrokeColor( WHITE ); overlay->SetFillColor( WHITE ); overlay->SetGlyphSize( VECTOR2D( 200000, 200000 )); overlay->BitmapText( wxString::Format("%d", i), v.pos, 0 ); } for( int i = 0; i < pnew.PointCount() - 1; i++ ) { vts[i].neighbours.push_back(&vts[i+1]); } for( int i = 0; i < hnew.PointCount(); i++ ) { auto hp = hnew.CPoint(i); bool found = false; auto vn = findVertex( hp ); if( vn ) { vn->isHull = true; vn->indexh = i; } else { VERTEX v; v.pos = hp; v.type = ON_EDGE; v.indexh = i; v.isHull = true; vts.push_back( v ); } overlay->SetStrokeColor( WHITE ); overlay->SetFillColor( WHITE ); overlay->SetGlyphSize( VECTOR2D( 200000, 200000 )); // overlay->BitmapText( wxString::Format("%d", i), hp, 0 ); } for( int i = 0; i < hnew.PointCount(); i++ ) { auto vc = findVertex( hnew.CPoint(i ) ); auto vnext = findVertex( hnew.CPoint(i+1) ); if(vc && vnext) vc->neighbours.push_back(vnext); } for( auto& v : vts ) { printf(" ip %d ih %d t %d ishull %d\n", v.indexp, v.indexh, v.type, v.isHull?1:0); } VERTEX* v = &vts[0]; SHAPE_LINE_CHAIN out; int n = 0; while (v->indexp != pnew.PointCount() ) { printf("SCAN ip %d ih %d t %d ishull %d\n", v->indexp, v->indexh, v->type, v->isHull?1:0); out.Append( v->pos ); if(v->visited) break; overlay->SetStrokeColor( v->isHull ? RED : WHITE ); overlay->SetFillColor( v->isHull ? RED : WHITE ); overlay->SetGlyphSize( VECTOR2D( 200000, 200000 )); //overlay->BitmapText( wxString::Format("%d", v->isHull ? v->indexh : v->indexp), v->pos, 0 ); v->visited = true; if ( (n++) == 2000 ) // sanity check break; for( auto vn : v->neighbours ) { printf(" ---> next ip %d ih %d t %d ishull %d\n",vn->indexp, vn->indexh, vn->type, vn->isHull?1:0); } if (v->type == OUTSIDE) { out.Append(v->pos); for( auto vn : v->neighbours ) if( (vn->indexp > v->indexp) && vn->type != INSIDE ) { v = vn; break; } } else if (v->type == ON_EDGE) { VERTEX* v_next = nullptr; int best_dist = INT_MAX; for( auto vn: v->neighbours) { if( vn->type == ON_EDGE && (vn->indexp == (v->indexp + 1) ) ) { v_next = vn; break; } } if( !v_next ) { for( auto vn: v->neighbours) { if( vn->type == OUTSIDE ) { v_next = vn; break; } } } if( !v_next ) { for( auto vn: v->neighbours) { if ( v->type == ON_EDGE ) { if( vn->indexh == ( (v->indexh + 1) % hnew.PointCount() ) ) { v_next = vn; //printf("E2\n"); break; } } } } v = v_next; } } out.Append( v->pos ); aWalk = out; return true; } int test2_main_func( int argc, char* argv[] ) { auto frame = new PNS_LOG_VIEWER_FRAME(nullptr); Pgm().App().SetTopWindow( frame ); // wxApp gets a face. frame->Show(); overlay = frame->GetPanel()->DebugOverlay(); overlay->SetIsFill(false); overlay->SetLineWidth(10000); // auto hull = SHAPE_LINE_CHAIN( { VECTOR2I( 155977520, 128439216), VECTOR2I( 155639216, 128777520), VECTOR2I( 155160784, 128777520), VECTOR2I( 154822480, 128439216), VECTOR2I( 154822480, 97960784), VECTOR2I( 155160784, 97622480), VECTOR2I( 155639216, 97622480), VECTOR2I( 155977520, 97960784)}, true ); //auto path = SHAPE_LINE_CHAIN( { VECTOR2I( 148981200, 102320000), VECTOR2I( 154822480, 102320000), VECTOR2I( 154822480, 98777520), VECTOR2I( 60977520, 98777520), VECTOR2I( 60977520, 127622480), VECTOR2I( 155639216, 127622480), VECTOR2I( 155977520, 127960784), VECTOR2I( 155977520, 128439216), VECTOR2I( 155639216, 128777520), VECTOR2I( 60160784, 128777520), VECTOR2I( 59822480, 128439216), VECTOR2I( 59822480, 97960784), VECTOR2I( 60160784, 97622480), VECTOR2I( 155639216, 97622480), VECTOR2I( 155977520, 97960784), VECTOR2I( 155977520, 102320000), VECTOR2I( 160208000, 102320000), VECTOR2I( 160462000, 102574000)}, false ); //auto path = SHAPE_LINE_CHAIN( { VECTOR2I( 112456000, 102726400), VECTOR2I( 112456000, 98560800), VECTOR2I( 112456000, 98827020)}, false ); //auto hull = SHAPE_LINE_CHAIN( { VECTOR2I( 155659720, 97572980), VECTOR2I( 156027020, 97940280), VECTOR2I( 156027020, 98459720), VECTOR2I( 155659720, 98827020), VECTOR2I( 60140280, 98827020), VECTOR2I( 59772980, 98459720), VECTOR2I( 59772980, 97940280), VECTOR2I( 60140280, 97572980)}, true ); //auto path = SHAPE_LINE_CHAIN( { VECTOR2I( 119364800, 100288000), VECTOR2I( 119364800, 97697200), VECTOR2I( 119364800, 97572980)}, false ); //auto hull = SHAPE_LINE_CHAIN( { VECTOR2I( 155659720, 97572980), VECTOR2I( 156027020, 97940280), VECTOR2I( 156027020, 98459720), VECTOR2I( 155659720, 98827020), VECTOR2I( 60140280, 98827020), VECTOR2I( 59772980, 98459720), VECTOR2I( 59772980, 97940280), VECTOR2I( 60140280, 97572980)}, true ); //auto path = SHAPE_LINE_CHAIN( { VECTOR2I( 119263200, 101253200), VECTOR2I( 119263200, 98827020), VECTOR2I( 61027020, 98827020), VECTOR2I( 61027020, 127572980), VECTOR2I( 154772980, 127572980), VECTOR2I( 154772980, 97940280), VECTOR2I( 155140280, 97572980), VECTOR2I( 155659720, 97572980), VECTOR2I( 156027020, 97940280), VECTOR2I( 156027020, 128459720), VECTOR2I( 155659720, 128827020), VECTOR2I( 60140280, 128827020), VECTOR2I( 59772980, 128459720), VECTOR2I( 59772980, 97940280), VECTOR2I( 60140280, 97572980), VECTOR2I( 119641420, 97572980), VECTOR2I( 121650800, 95563600)}, false ); //auto hull = SHAPE_LINE_CHAIN( { VECTOR2I( 155659720, 97572980), VECTOR2I( 156027020, 97940280), VECTOR2I( 156027020, 98459720), VECTOR2I( 155659720, 98827020), VECTOR2I( 60140280, 98827020), VECTOR2I( 59772980, 98459720), VECTOR2I( 59772980, 97940280), VECTOR2I( 60140280, 97572980)}, true ); // auto hull = SHAPE_LINE_CHAIN( { VECTOR2I( 96722489, 117694794), VECTOR2I( 97594794, 116822489), VECTOR2I( 99205206, 116822489), VECTOR2I( 100077511, 117694794), VECTOR2I( 100077511, 119305206), VECTOR2I( 99205206, 120177511), VECTOR2I( 97594794, 120177511), VECTOR2I( 96722489, 119305206)}, true ); // auto path = SHAPE_LINE_CHAIN( { VECTOR2I( 103400000, 118500000), VECTOR2I( 93400000, 118500000)}, false ); auto hull = SHAPE_LINE_CHAIN( { VECTOR2I( 66280505, 107710033), VECTOR2I( 65914967, 107344495), VECTOR2I( 65914967, 106827549), VECTOR2I( 66280505, 106462011), VECTOR2I( 74810033, 106462009), VECTOR2I( 75175571, 106827547), VECTOR2I( 75175571, 107344493), VECTOR2I( 74810033, 107710031)}, true ); auto path = SHAPE_LINE_CHAIN( { /*VECTOR2I( 143928480, 109445996), VECTOR2I( 111066480, 109445996), VECTOR2I( 106254391, 104633907), VECTOR2I( 105909001, 104633907), VECTOR2I( 105775094, 104500000),*/ VECTOR2I( 76250000, 104500000), VECTOR2I( 74287991, 106462009), VECTOR2I( 66280505, 106462011), VECTOR2I( 66012989, 106462011)}, false ); BOX2D bb ( path.BBox().GetPosition(), path.BBox().GetSize() ); frame->GetPanel()->GetView()->SetViewport(bb); PNS::LINE l; SHAPE_LINE_CHAIN path_pre, path_walk, path_post; l.SetShape( path ); auto status = l.Walkaround( hull, path_walk, false ); printf("Stat: %d\n", status ); //printf("status: %d\n", walkaround2( path, hull, path_pre, path_walk, // path_post, false ) ); overlay->SetLineWidth(200000.0); overlay->SetStrokeColor( BLUE ); //overlay->Polyline( path_pre ); overlay->Polyline( path_walk ); //overlay->Polyline( path_post ); overlay->SetStrokeColor( WHITE ); overlay->SetLineWidth( 100000.0 ); overlay->Polyline( path ); overlay->SetStrokeColor( RED ); overlay->Polyline( hull ); return 0; } #if 0 static bool registered3 = UTILITY_REGISTRY::Register( { "test1", "Test1", test1_main_func, } ); #endif static bool registered4 = UTILITY_REGISTRY::Register( { "test2", "Test2", test2_main_func, } );