/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2020-2022, 2024 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 #include #include #include "label_manager.h" #include "pns_log_file.h" #include "pns_log_player.h" #include "pns_log_viewer_frame.h" #include "router/pns_diff_pair.h" #include "router/pns_utils.h" #include "router/router_preview_item.h" #include class WX_SHAPE_TREE_ITEM_DATA : public wxClientData { public: WX_SHAPE_TREE_ITEM_DATA( PNS_DEBUG_SHAPE* item, int level = 0 ) : m_item( item ), m_level( level ) {}; PNS_DEBUG_SHAPE* m_item; int m_level; }; PNS_LOG_VIEWER_OVERLAY::PNS_LOG_VIEWER_OVERLAY( KIGFX::GAL* aGal ) { m_labelMgr.reset( new LABEL_MANAGER( aGal ) ); } void PNS_LOG_VIEWER_OVERLAY::AnnotatedPolyset( const SHAPE_POLY_SET& aPolyset, std::string aName, bool aShowVertexNumbers ) { for( int i = 0; i < aPolyset.OutlineCount(); i++ ) { if( i == 0 && !aName.empty() ) AnnotatedPolyline( aPolyset.COutline( i ), aName ); else AnnotatedPolyline( aPolyset.COutline( i ), "" ); for( int j = 0; j < aPolyset.HoleCount( i ); j++ ) AnnotatedPolyline( aPolyset.CHole( i, j ), "" ); } } void PNS_LOG_VIEWER_OVERLAY::AnnotatedPolyline( const SHAPE_LINE_CHAIN& aL, std::string name, bool aShowVertexNumbers ) { Polyline( aL ); if( name.length() > 0 && aL.PointCount() > 0 ) m_labelMgr->Add( aL.CPoint( -1 ), name, GetStrokeColor() ); if( aShowVertexNumbers ) { for( int i = 0; i < aL.PointCount(); i++ ) m_labelMgr->Add( aL.CPoint(i), wxString::Format("%d", i ), GetStrokeColor() ); } } void PNS_LOG_VIEWER_OVERLAY::AnnotatedPoint( const VECTOR2I p, int size, std::string name, bool aShowVertexNumbers ) { Line( p + VECTOR2D( size, size ), p - VECTOR2D( size, size ) ); Line( p + VECTOR2D( -size, size ), p - VECTOR2D( -size, size ) ); if( name.length() > 0 ) m_labelMgr->Add( p, name, GetStrokeColor() ); } void PNS_LOG_VIEWER_OVERLAY::Arc( const SHAPE_ARC& arc ) { double radius = arc.GetRadius(); EDA_ANGLE start_angle = arc.GetStartAngle(); EDA_ANGLE angle = arc.GetCentralAngle(); KIGFX::VIEW_OVERLAY::SetLineWidth( arc.GetWidth() / 10 ); KIGFX::VIEW_OVERLAY::Arc( arc.GetCenter(), radius, start_angle, start_angle + angle ); COLOR4D prevStrokeCol = KIGFX::VIEW_OVERLAY::GetStrokeColor(); COLOR4D lightStrokeCol = prevStrokeCol.WithAlpha(0.5); KIGFX::VIEW_OVERLAY::SetStrokeColor( lightStrokeCol ); KIGFX::VIEW_OVERLAY::SetLineWidth( arc.GetWidth() ); KIGFX::VIEW_OVERLAY::Arc( arc.GetCenter(), radius, start_angle, start_angle + angle ); KIGFX::VIEW_OVERLAY::SetStrokeColor( prevStrokeCol ); } void PNS_LOG_VIEWER_OVERLAY::DrawAnnotations() { m_labelMgr->Redraw( this ); } PNS_LOG_VIEWER_FRAME::PNS_LOG_VIEWER_FRAME( wxFrame* frame ) : PNS_LOG_VIEWER_FRAME_BASE( frame ), m_rewindIter( 0 ) { LoadSettings(); createView( m_mainSplitter, PCB_DRAW_PANEL_GAL::GAL_TYPE_OPENGL ); m_reporter.reset( new WX_TEXT_CTRL_REPORTER( m_consoleText ) ); m_galPanel->SetParent( m_mainSplitter ); m_mainSplitter->SplitHorizontally( m_galPanel.get(), m_panelProps ); Layout(); Show( true ); Maximize(); Raise(); auto settings = static_cast( m_galPanel->GetView()->GetPainter()->GetSettings() ); PCB_DISPLAY_OPTIONS opts; opts.m_ZoneDisplayMode = ZONE_DISPLAY_MODE::SHOW_ZONE_OUTLINE; double opacity = 0.5; opts.m_TrackOpacity = opacity; ///< Opacity override for all tracks opts.m_ViaOpacity = opacity; ///< Opacity override for all types of via opts.m_PadOpacity = opacity; ///< Opacity override for SMD pads and PTHs opts.m_ZoneOpacity = opacity; ///< Opacity override for filled zone areas settings->LoadDisplayOptions( opts ); 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_listPopupMenu->Append( ID_LIST_DISPLAY_LINE, wxT( "Go to line in IDE" ), wxT( "" ), wxITEM_NORMAL ); m_itemList->Connect( m_itemList->GetId(), wxEVT_TREELIST_ITEM_CONTEXT_MENU, wxMouseEventHandler( PNS_LOG_VIEWER_FRAME::onListRightClick ), nullptr, this ); //m_itemList->Connect(m_itemList->GetId(),wxEVT_LISTBOX,wxCommandEventHandler(PNS_LOG_VIEWER_FRAME::onListSelect),nullptr,this); m_itemList->Connect( m_itemList->GetId(), wxEVT_TREELIST_SELECTION_CHANGED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME::onListSelect ), nullptr, this ); m_itemList->Connect( m_itemList->GetId(), wxEVT_TREELIST_ITEM_CHECKED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME::onListChecked ), nullptr, this ); m_itemList->AppendColumn( "Type" ); m_itemList->AppendColumn( "Value" ); m_itemList->AppendColumn( "File" ); m_itemList->AppendColumn( "Method" ); m_itemList->AppendColumn( "Line" ); m_itemList->AppendColumn( "VCount" ); m_itemList->AppendColumn( "Non-45" ); m_overlay.reset( new PNS_LOG_VIEWER_OVERLAY ( m_galPanel->GetGAL() ) ); m_galPanel->GetView()->Add( m_overlay.get() ); m_galPanel->GetViewControls()->EnableCursorWarping(false); for( PCB_LAYER_ID layer : LSET::AllNonCuMask().Seq() ) m_galPanel->GetView()->SetLayerVisible( layer, false ); } PNS_LOG_VIEWER_FRAME::~PNS_LOG_VIEWER_FRAME() { m_board = nullptr; m_logPlayer = nullptr; m_logFile = nullptr; m_overlay = nullptr; } void PNS_LOG_VIEWER_FRAME::createUserTools() { } PNS_DEBUG_STAGE* PNS_LOG_VIEWER_FRAME::getCurrentStage() { PNS_TEST_DEBUG_DECORATOR* dbgd = m_logPlayer->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::drawSimpleShape( SHAPE* aShape, bool aIsSelected, const std::string& aName ) { switch( aShape->Type() ) { case SH_CIRCLE: { auto cir = static_cast( aShape ); m_overlay->Circle( cir->GetCenter(), cir->GetRadius() ); break; } case SH_SEGMENT: { auto seg = static_cast( aShape ); m_overlay->Line( seg->GetSeg().A, seg->GetSeg().B ); break; } case SH_RECT: { auto rect = static_cast( aShape ); m_overlay->Rectangle( rect->GetPosition(), rect->GetPosition() + rect->GetSize() ); break; } case SH_LINE_CHAIN: { auto lc = static_cast( aShape ); m_overlay->AnnotatedPolyline( *lc, aName, m_showVertices || aIsSelected ); break; } default: break; } } void PNS_LOG_VIEWER_FRAME::drawLoggedItems( int iter ) { if( !m_logPlayer ) return; PNS_DEBUG_STAGE* st = getCurrentStage(); if( !st ) return; m_overlay.reset( new PNS_LOG_VIEWER_OVERLAY ( m_galPanel->GetGAL() ) ); m_galPanel->GetView()->Add( m_overlay.get() ); //m_galPanel->GetGAL()->EnableDepthTest( false ); auto drawShapes = [&]( PNS_DEBUG_SHAPE* ent ) -> bool { bool isEnabled = ent->IsVisible(); bool isSelected = ent->m_selected; if( m_searchString.Length() > 0 ) isEnabled = ent->m_filterMatch; if( !isEnabled ) return true; for( auto& sh : ent->m_shapes ) { COLOR4D color = ent->m_color; int lineWidth = ent->m_width; m_overlay->SetIsStroke( true ); m_overlay->SetIsFill( false ); if( isSelected ) { color.Brighten( 0.5 ); } color.a = 1.0; m_overlay->SetStrokeColor( color ); m_overlay->SetLineWidth( m_showThinLines ? 10000 : ent->m_width ); if( sh->Type() == SH_COMPOUND ) { auto cmpnd = static_cast( sh ); for( auto subshape : cmpnd->Shapes() ) { drawSimpleShape( subshape, isSelected, ent->m_name.ToStdString() ); } } else { drawSimpleShape( sh, isSelected, ent->m_name.ToStdString() ); } } return true; }; st->m_entries->IterateTree( drawShapes ); m_overlay->DrawAnnotations(); m_galPanel->GetView()->MarkDirty(); m_galPanel->GetParent()->Refresh(); } void PNS_LOG_VIEWER_FRAME::LoadLogFile( const wxString& aFile ) { std::unique_ptr logFile( new PNS_LOG_FILE ); if( logFile->Load( wxFileName( aFile ), m_reporter.get() ) ) SetLogFile( logFile.release() ); } void PNS_LOG_VIEWER_FRAME::SetLogFile( PNS_LOG_FILE* aLog ) { m_logPlayer.reset( new PNS_LOG_PLAYER ); m_board = nullptr; m_logFile.reset( aLog ); SetBoard( m_logFile->GetBoard() ); m_logPlayer->ReplayLog( m_logFile.get(), 0, 0, -1, true ); auto dbgd = m_logPlayer->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 ); updatePnsPreviewItems( m_rewindIter ); } void PNS_LOG_VIEWER_FRAME::SetBoard2( std::shared_ptr aBoard ) { SetBoard( aBoard ); 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 ); } void PNS_LOG_VIEWER_FRAME::onOpen( wxCommandEvent& event ) { wxFileDialog dlg( this, "Select Log File", m_mruPath, wxEmptyString, "PNS log files" + AddFileExtListToFilter( { "log" } ), wxFD_OPEN | wxFD_FILE_MUST_EXIST ); if( dlg.ShowModal() != wxID_CANCEL ) { wxString logPath = dlg.GetPath(); LoadLogFile( logPath ); m_mruPath = wxFileName( logPath ).GetPath(); } } void PNS_LOG_VIEWER_FRAME::onSaveAs( wxCommandEvent& event ) { if( !m_logFile ) { DisplayError( this, wxT( "No log file Loaded!" ) ); return; } wxFileDialog dlg( this, "New log file", m_mruPath, wxEmptyString, "PNS log files" + AddFileExtListToFilter( { "log" } ), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if( dlg.ShowModal() != wxID_CANCEL ) { // Enforce the extension, wxFileDialog is inept. wxFileName create_me = EnsureFileExtension( dlg.GetPath(), "log" ); wxASSERT_MSG( create_me.IsAbsolute(), wxS( "wxFileDialog returned non-absolute path" ) ); m_logFile->SaveLog( create_me, m_reporter.get() ); m_mruPath = create_me.GetPath(); } } void PNS_LOG_VIEWER_FRAME::onExit( wxCommandEvent& event ) { Close(); } void PNS_LOG_VIEWER_FRAME::onListChecked( wxCommandEvent& event ) { syncModel(); drawLoggedItems( m_rewindIter ); } void PNS_LOG_VIEWER_FRAME::onShowThinLinesChecked( wxCommandEvent& event ) { m_showThinLines = event.GetInt(); drawLoggedItems( m_rewindIter ); updatePnsPreviewItems( m_rewindIter ); } void PNS_LOG_VIEWER_FRAME::onShowRPIsChecked( wxCommandEvent& event ) { m_showRPIs = event.GetInt(); drawLoggedItems( m_rewindIter ); updatePnsPreviewItems( m_rewindIter ); } void PNS_LOG_VIEWER_FRAME::onShowVerticesChecked( wxCommandEvent& event ) { m_showVertices = event.GetInt(); drawLoggedItems( m_rewindIter ); updatePnsPreviewItems( m_rewindIter ); } void PNS_LOG_VIEWER_FRAME::onRewindScroll( wxScrollEvent& event ) { m_rewindIter = event.GetPosition(); drawLoggedItems( m_rewindIter ); updateDumpPanel( m_rewindIter ); updatePnsPreviewItems( m_rewindIter ); m_rewindPos->SetValue( std::to_string( m_rewindIter ) ); event.Skip(); } void PNS_LOG_VIEWER_FRAME::onBtnRewindLeft( wxCommandEvent& event ) { if( m_rewindIter > 0 ) { m_rewindIter--; drawLoggedItems( m_rewindIter ); updateDumpPanel( m_rewindIter ); updatePnsPreviewItems( m_rewindIter ); m_rewindPos->SetValue( std::to_string( m_rewindIter ) ); m_rewindSlider->SetValue( m_rewindIter ); } } void PNS_LOG_VIEWER_FRAME::onBtnRewindRight( wxCommandEvent& event ) { auto dbgd = m_logPlayer->GetDebugDecorator(); int count = dbgd->GetStageCount(); if( m_rewindIter < count ) { m_rewindIter++; drawLoggedItems( m_rewindIter ); updateDumpPanel( m_rewindIter ); updatePnsPreviewItems( m_rewindIter ); m_rewindPos->SetValue( std::to_string( m_rewindIter ) ); m_rewindSlider->SetValue( m_rewindIter ); } } void PNS_LOG_VIEWER_FRAME::onFilterText( wxCommandEvent& event ) { m_searchString = m_filterString->GetValue(); updateDumpPanel( m_rewindIter ); } void PNS_LOG_VIEWER_FRAME::onRewindCountText( wxCommandEvent& event ) { if( !m_logPlayer ) return; int val = wxAtoi( m_rewindPos->GetValue() ); auto dbgd = m_logPlayer->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 ); updatePnsPreviewItems( m_rewindIter ); event.Skip(); } void PNS_LOG_VIEWER_FRAME::syncModel() { 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 ) ); if( idata ) { bool checked = m_itemList->GetCheckedState( item ) == wxCHK_CHECKED; bool selected = m_itemList->IsSelected( item ); idata->m_item->m_visible = checked || selected; idata->m_item->m_selected = selected; } } } void runCommand( const wxString& aCommand ) { #ifdef __WXMSW__ wxShell( aCommand ); // on windows we need to launch a shell in order to run the command #else wxExecute( aCommand ); #endif /* __WXMSW__ */ } void PNS_LOG_VIEWER_FRAME::onListRightClick( wxMouseEvent& event ) { 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; case ID_LIST_COPY: { wxString s; PNS_DEBUG_STAGE* st = getCurrentStage(); if( !st ) return; auto formatShapes = [&]( PNS_DEBUG_SHAPE* ent ) -> bool { if( ent->m_selected ) { for( auto sh : ent->m_shapes ) { s += "// " + ent->m_name + "\n " + sh->Format() + "; \n"; } } return true; }; st->m_entries->IterateTree( formatShapes ); 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->Flush(); // Allow data to be available after closing KiCad wxTheClipboard->Close(); } return; } case ID_LIST_DISPLAY_LINE: { wxVector selectedItems; if( m_itemList->GetSelections( selectedItems ) == 1 ) { wxString filename = m_itemList->GetItemText(selectedItems.back(), 2); wxString line = m_itemList->GetItemText(selectedItems.back(), 4); if( !filename.empty() && !line.empty() ) { wxString filepath = m_filenameToPathMap[filename]; switch( m_ideChoice->GetCurrentSelection() ) { case 0: runCommand( wxString::Format( "code --goto %s:%s", filepath, line ) ); return; case 1: runCommand( wxString::Format( "start devenv /edit %s /command \"Gotoln %s\"", filepath, line ) ); return; // fixme case 2: runCommand( wxString::Format( "clion --line %s %s", line, filepath ) ); return; case 3: runCommand( wxString::Format( "emacsclient +%s %s", line, filepath ) ); return; default: return; } } } break; } } return; } void PNS_LOG_VIEWER_FRAME::onListSelect( wxCommandEvent& event ) { syncModel(); drawLoggedItems( m_rewindIter ); } static bool isLine45Degree( const SHAPE_LINE_CHAIN* lc ) { for( int i = 0; i < lc->SegmentCount(); i++ ) { const SEG& s = lc->CSegment( i ); if( lc->IsArcSegment( i ) ) continue; if( s.Length() < 10 ) continue; double angle = 180.0 / M_PI * atan2( (double) s.B.y - (double) s.A.y, (double) s.B.x - (double) s.A.x ); if( angle < 0 ) angle += 360.0; double angle_a = fabs( fmod( angle, 45.0 ) ); if( angle_a > 1.0 && angle_a < 44.0 ) return false; } return true; } bool PNS_LOG_VIEWER_FRAME::filterStringMatches( PNS_DEBUG_SHAPE* ent ) { std::set processed; std::deque q; q.push_back(ent); int total = 0; while ( q.size() > 0 ) { PNS_DEBUG_SHAPE* top = q.front(); q.pop_front(); for ( auto chld : top->m_children ) { bool match = m_searchString.Length() == 0 ? true : false; //printf("CHK %s\n", (const char *) chld->m_name.c_str() ); chld->m_filterMatch = false; if ( chld->m_name.Contains( m_searchString ) ) match = true; if ( chld->m_msg.Contains( m_searchString ) ) match = true; if( match ) { for ( PNS_DEBUG_SHAPE *cur = chld; cur; cur = cur->m_parent ) cur->m_filterMatch = match; } if( processed.find(chld) == processed.end() ) { q.push_back( chld ); processed.insert( chld ); } } total++; } printf("total: %d\n", total ); return false; } void PNS_LOG_VIEWER_FRAME::buildListTree( wxTreeListItem item, PNS_DEBUG_SHAPE* ent, int depth ) { #ifdef EXTRA_VERBOSE for( int i = 0; i < depth * 2; i++ ) printf( " " ); if( ent->m_msg.length() ) printf( "MSG: %s\n", ent->m_msg.c_str() ); else printf( "SHAPES: %s [%d]\n", ent->m_name.c_str(), ent->m_children.size() ); #endif wxTreeListItem ritem; if( !ent->m_filterMatch ) return; if( ent->m_msg.length() ) { ritem = m_itemList->AppendItem( item, "Child" ); m_itemList->SetItemText( ritem, 0, "Message" ); m_itemList->SetItemText( ritem, 1, ent->m_msg ); } else { ritem = m_itemList->AppendItem( item, "Child" ); int n_verts = 0; for(auto sh : ent->m_shapes ) { if ( sh->Type() == SH_LINE_CHAIN ) { n_verts += static_cast( sh )->PointCount(); } } m_itemList->SetItemText( ritem, 0, wxString::Format( "Shapes [%d verts]", n_verts ) ); m_itemList->SetItemText( ritem, 1, ent->m_name ); } wxString fullfilepath = ent->m_srcLoc.fileName; wxString filename = wxFileNameFromPath( fullfilepath ); if( !filename.empty() ) m_filenameToPathMap.insert( { filename, fullfilepath } ); m_itemList->SetItemText( ritem, 2, filename ); m_itemList->SetItemText( ritem, 3, ent->m_srcLoc.funcName ); m_itemList->SetItemText( ritem, 4, wxString::Format("%d", ent->m_srcLoc.line ) ); int totalVC = 0, totalVCSimplified = 0; bool is45Degree = true; for( SHAPE* sh : ent->m_shapes ) { if( sh->Type() == SH_LINE_CHAIN ) { auto lc = static_cast( sh ); totalVC += lc->PointCount(); SHAPE_LINE_CHAIN simp(*lc); simp.Simplify(); totalVCSimplified += simp.PointCount(); if( !isLine45Degree( lc ) ) is45Degree = false; } } if( totalVC > 0 ) m_itemList->SetItemText( ritem, 5, wxString::Format( "%d [%d]", totalVC, totalVCSimplified ) ); if( !is45Degree ) m_itemList->SetItemText( ritem, 6, wxT("") ); m_itemList->SetItemData( ritem, new WX_SHAPE_TREE_ITEM_DATA( ent, depth ) ); if( !ent->m_children.size() ) return; for( auto child : ent->m_children ) { buildListTree( ritem, child, depth + 1 ); } } static void expandAllChildren( wxTreeListCtrl* tree, int maxLevel = -1 ) { wxTreeListItem child = tree->GetFirstItem (); while( child.IsOk() ) { WX_SHAPE_TREE_ITEM_DATA* idata = static_cast( tree->GetItemData( child ) ); if( maxLevel < 0 || idata->m_level <= maxLevel ) tree->Expand ( child ); else tree->Collapse ( child ); child = tree->GetNextItem( child ); } } static void collapseAllChildren( wxTreeListCtrl* tree ) { wxTreeListItem child = tree->GetFirstItem (); while( child.IsOk() ) { tree->Collapse ( child ); child = tree->GetNextItem( child ); } } void PNS_LOG_VIEWER_FRAME::updateDumpPanel( int iter ) { printf("UpdateDUmp %d\n", iter ); if( !m_logPlayer ) return; auto dbgd = m_logPlayer->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 ); if( st->m_status ) { m_algoStatus->SetLabel("OK"); m_algoStatus->SetForegroundColour( wxColor(*wxGREEN)); } else { m_algoStatus->SetLabel("FAIL"); m_algoStatus->SetForegroundColour( wxColor(*wxRED)); } auto rootItem = m_itemList->GetRootItem(); m_itemList->DeleteAllItems(); filterStringMatches( st->m_entries ); buildListTree( rootItem, st->m_entries ); m_itemList->CheckItemRecursively( rootItem, wxCHK_UNCHECKED ); expandAllChildren( m_itemList, 0 ); m_itemList->Refresh(); } void PNS_LOG_VIEWER_FRAME::updatePnsPreviewItems( int iter ) { auto viewTracker = m_logPlayer->GetViewTracker(); PNS_LOG_VIEW_TRACKER::VIEW_ENTRIES& entries = viewTracker->GetEntriesForStage( iter ); auto view = m_galPanel->GetView(); printf("DBG updatePnsPreviewItems: %zu items\n", entries.size() ); m_previewItems.reset( new KIGFX::VIEW_GROUP( m_galPanel->GetView() ) ); m_galPanel->GetView()->Add( m_previewItems.get() ); m_previewItems->SetLayer( LAYER_SELECT_OVERLAY ) ; m_galPanel->GetView()->SetLayerVisible( LAYER_SELECT_OVERLAY ); if( !m_showRPIs ) return; for( auto& ent : entries ) { if ( ent.m_isHideOp ) { auto parent = ent.m_item->Parent(); if( parent ) { view->Hide( parent ); } } else { ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( ent.m_item, view ); pitem->Update( ent.m_item ); m_previewItems->Add(pitem); // printf("DBG vadd %p total %d\n", pitem, m_previewItems->GetSize() ); } } view->SetVisible( m_previewItems.get(), true ); view->Update( m_previewItems.get() ); printf("DBG vgrp %p total %d\n", m_previewItems.get(), m_previewItems->GetSize() ); //view->UpdateAllItems( KIGFX::ALL ); } REPORTER* PNS_LOG_VIEWER_FRAME::GetConsoleReporter() { return m_reporter.get(); } #if 0 static BOARD* loadBoard( const std::string& filename ) { IO_RELEASER pi( new PCB_IO_KICAD_SEXPR ); BOARD* brd = nullptr; try { brd = pi->LoadBoard( wxString( filename.c_str() ), nullptr, nullptr ); } catch( const IO_ERROR& ) { return nullptr; } return brd; } int render_perftest_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( "PCB render performance test. Just renders a board without UI update overhead.\n" ); return 0; } if( argc < 2 ) { printf( "Expected parameters: board_file\n" ); return 0; } PROF_TIMER cnt("load-board"); std::shared_ptr brd ( loadBoard( argv[1] ) ); cnt.Stop(); KI_TRACE( traceGalProfile, "%s\n", cnt.to_string() ); frame->SetBoard2( brd ); return 0; } static bool registered3 = UTILITY_REGISTRY::Register( { "render_perftest", "Renderer performance test", render_perftest_main_func, } ); VECTOR2I NearestPointFixpt( SEG seg, const VECTOR2I& aP ) { VECTOR2I d = seg.B - seg.A; SEG::ecoord l_squared = d.Dot( d ); if( l_squared == 0 ) return seg.A; SEG::ecoord t = d.Dot( aP - seg.A ); if( t < 0 ) return seg.A; else if( t > l_squared ) return seg.B; int xp = rescale( t, (SEG::ecoord) d.x, l_squared ); int yp = rescale( t, (SEG::ecoord) d.y, l_squared ); return seg.A + VECTOR2I( xp, yp ); } VECTOR2D NearestPointDbl( SEG seg, const VECTOR2I& aP ) { VECTOR2D d = seg.B - seg.A; double l_squared = d.Dot(d); if( l_squared == 0 ) return seg.A; double t = d.Dot(VECTOR2D( aP - seg.A ) ); if( t < 0 ) return seg.A; else if( t > l_squared ) return seg.B; double xp = t * d.x / l_squared; double yp = t * d.y / l_squared; return VECTOR2D(seg.A) + VECTOR2D( xp, yp ); } int ttt_main_func( int argc, char* argv[] ) { int n = 1000000; std::vector pts; std::vector segs; std::vector rv; std::vector rvi; rv.resize(n); rvi.resize(n); for (int i = 0; i < n ;i++) { pts.push_back(VECTOR2I(random()%100000000, random()%100000000)); segs.push_back(SEG( VECTOR2I(random()%100000000, random()%100000000), VECTOR2I(random()%100000000, random()%100000000) ) ); } PROF_TIMER tmrFix("nearest-fixpt"); for(int i = 0; i < n ; i++) { rvi[i] = NearestPointFixpt( segs[i], pts[i]); } tmrFix.Show(); PROF_TIMER tmrDbl("nearest-double"); for(int i = 0; i < n ; i++) { rv[i] = NearestPointDbl( segs[i], pts[i]); } tmrDbl.Show(); return 0; } static bool registered4 = UTILITY_REGISTRY::Register( { "ttt", "Renderer performance test", ttt_main_func, } ); #endif