1439 lines
41 KiB
C++
1439 lines
41 KiB
C++
|
/*
|
||
|
* 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 <wx/clipbrd.h>
|
||
|
|
||
|
#include <pcb_painter.h>
|
||
|
#include <pcb_test_frame.h>
|
||
|
#include <qa_utils/utility_registry.h>
|
||
|
#include <pgm_base.h>
|
||
|
|
||
|
#include <profile.h>
|
||
|
|
||
|
#include <view/view_overlay.h>
|
||
|
|
||
|
#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<KIGFX::PCB_RENDER_SETTINGS*>(
|
||
|
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<KIGFX::VIEW_OVERLAY> m_overlay;
|
||
|
std::shared_ptr<PNS_LOG_FILE> m_logFile;
|
||
|
std::shared_ptr<PNS_TEST_ENVIRONMENT> 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<SHAPE_CIRCLE*>( sh );
|
||
|
m_overlay->Circle( cir->GetCenter(), cir->GetRadius() );
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case SH_RECT:
|
||
|
{
|
||
|
auto rect = static_cast<SHAPE_RECT*>( sh );
|
||
|
m_overlay->Rectangle( rect->GetPosition(), rect->GetPosition() + rect->GetSize() );
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case SH_LINE_CHAIN:
|
||
|
{
|
||
|
auto lc = static_cast<SHAPE_LINE_CHAIN*>( 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<WX_SHAPE_TREE_ITEM_DATA*> ( 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<int64_t> 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 T1, typename T2>
|
||
|
typename std::map<T1, T2>::iterator findClosestKey( std::map<T1, T2> & 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<PNS::ITEM*> 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<PNS::LINKED_ITEM*>( *pendingItems.begin() ) );
|
||
|
|
||
|
printf("l net %d segs %d layer %d\n", net_p, l.SegmentCount(), l.Layer() );
|
||
|
|
||
|
std::map<int, int> gapMap;
|
||
|
std::map<PNS::LINKED_ITEM*, int> coupledCandidates;
|
||
|
|
||
|
for( auto li : l.Links() )
|
||
|
{
|
||
|
pendingItems.erase( li );
|
||
|
auto sp = dyn_cast<PNS::SEGMENT*> ( li );
|
||
|
|
||
|
if(!sp)
|
||
|
continue;
|
||
|
|
||
|
for ( auto ci : complementItems )
|
||
|
{
|
||
|
auto sn = dyn_cast<PNS::SEGMENT*> ( 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;
|
||
|
|
||
|
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<int, int> 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 T1, typename T2>
|
||
|
typename std::map<T1, T2>::iterator findClosestKey( std::map<T1, T2> & 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<TRACK*> 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<TRACK*>(li);
|
||
|
|
||
|
if(!sp)
|
||
|
continue;
|
||
|
|
||
|
for ( auto ci : complementItems )
|
||
|
{
|
||
|
auto sn = dyn_cast<TRACK*> ( 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<int, int> 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<wxString>() );
|
||
|
|
||
|
frame->SetBoard( project.board);
|
||
|
|
||
|
Pgm().App().SetTopWindow( frame ); // wxApp gets a face.
|
||
|
frame->Show();
|
||
|
|
||
|
auto view = frame->GetPanel()->GetView();
|
||
|
//auto settings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( 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<KIGFX::VIEW_OVERLAY> g_overlay;
|
||
|
static std::shared_ptr<KIGFX::VIEW_OVERLAY> 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<VERTEX*> 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<VERTEX> 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,
|
||
|
} );
|
||
|
|
||
|
|