diff --git a/qa/CMakeLists.txt b/qa/CMakeLists.txt index 16472a96bf..ca3eda8bfd 100644 --- a/qa/CMakeLists.txt +++ b/qa/CMakeLists.txt @@ -60,4 +60,5 @@ add_subdirectory( drc_proto ) add_subdirectory( common_tools ) add_subdirectory( pcbnew_tools ) +add_subdirectory( pns ) diff --git a/qa/pns/CMakeLists.txt b/qa/pns/CMakeLists.txt new file mode 100644 index 0000000000..76477ddb07 --- /dev/null +++ b/qa/pns/CMakeLists.txt @@ -0,0 +1,124 @@ +# +# This program source code file is part of KiCad, a free EDA CAD application. +# +# Copyright (C) 2017 CERN +# @author Alejandro GarcĂ­a Montoro +# +# 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 + + +find_package(Boost COMPONENTS unit_test_framework REQUIRED) +find_package( wxWidgets 3.0.0 COMPONENTS gl aui adv html core net base xml stc REQUIRED ) + +add_definitions(-DBOOST_TEST_DYN_LINK -DPCBNEW) + +set ( PCB_OBJS $ ) +list ( REMOVE_ITEM PCB_OBJS pcbnew.cpp ) +list ( REMOVE_ITEM PCB_OBJS pcbnew.o ) +list ( REMOVE_ITEM PCB_OBJS pcbnew.cpp.o ) +list ( REMOVE_ITEM PCB_OBJS pcbnew/CMakeFiles/pcbnew_kiface_objects.dir/pcbnew.cpp.o ) + +add_executable( test_pns +# test_pns.cpp + ../../pcbnew/drc/drc_rule.cpp + ../../pcbnew/drc/drc_rule_condition.cpp + ../../pcbnew/drc/drc_rule_parser.cpp + ../../pcbnew/drc/drc_test_provider.cpp + ../../pcbnew/drc/drc_test_provider_copper_clearance.cpp + ../../pcbnew/drc/drc_test_provider_hole_clearance.cpp + ../../pcbnew/drc/drc_test_provider_edge_clearance.cpp + ../../pcbnew/drc/drc_test_provider_hole_size.cpp + ../../pcbnew/drc/drc_test_provider_disallow.cpp + ../../pcbnew/drc/drc_test_provider_track_width.cpp + ../../pcbnew/drc/drc_test_provider_annulus.cpp + ../../pcbnew/drc/drc_test_provider_connectivity.cpp + ../../pcbnew/drc/drc_test_provider_courtyard_clearance.cpp + ../../pcbnew/drc/drc_test_provider_via_diameter.cpp + ../../pcbnew/drc/drc_test_provider_lvs.cpp + ../../pcbnew/drc/drc_test_provider_misc.cpp + ../../pcbnew/drc/drc_test_provider_silk_to_mask.cpp + ../../pcbnew/drc/drc_test_provider_silk_clearance.cpp + ../../pcbnew/drc/drc_test_provider_matched_length.cpp + ../../pcbnew/drc/drc_test_provider_diff_pair_coupling.cpp + ../../pcbnew/drc/drc_engine.cpp + ../../pcbnew/drc/drc_item.cpp + pns_log.cpp + pns_log_viewer.cpp + pns_log_viewer_frame_base.cpp + ../qa_utils/pcb_test_frame.cpp + ../qa_utils/test_app_main.cpp + ../qa_utils/utility_program.cpp + ../qa_utils/mocks.cpp + ../../common/base_units.cpp + ) + +# Pcbnew tests, so pretend to be pcbnew (for units, etc) +target_compile_definitions( test_pns + PRIVATE PCBNEW +) + +# Anytime we link to the kiface_objects, we have to add a dependency on the last object +# to ensure that the generated lexer files are finished being used before the qa runs in a +# multi-threaded build +add_dependencies( test_pns pcbnew ) + + +target_link_libraries( test_pns + qa_pcbnew_utils + 3d-viewer + connectivity + pcbcommon + pnsrouter + gal + common + gal + qa_utils + dxflib_qcad + tinyspline_lib + nanosvg + idf3 + unit_test_utils + ${PCBNEW_IO_LIBRARIES} + ${wxWidgets_LIBRARIES} + ${GDI_PLUS_LIBRARIES} + ${PYTHON_LIBRARIES} + ${Boost_LIBRARIES} # must follow GITHUB + ${PCBNEW_EXTRA_LIBS} # -lrt must follow Boost +) + + +include_directories( BEFORE ${INC_BEFORE} ) +include_directories( + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/3d-viewer + ${CMAKE_SOURCE_DIR}/common + ${CMAKE_SOURCE_DIR}/pcbnew + ${CMAKE_SOURCE_DIR}/pcbnew/router + ${CMAKE_SOURCE_DIR}/pcbnew/tools + ${CMAKE_SOURCE_DIR}/pcbnew/dialogs + ${CMAKE_SOURCE_DIR}/polygon + ${CMAKE_SOURCE_DIR}/common/geometry + ${CMAKE_SOURCE_DIR}/qa/common + ${CMAKE_SOURCE_DIR}/qa/qa_utils + ${CMAKE_SOURCE_DIR}/qa/qa_utils/include + + ${Boost_INCLUDE_DIR} + ${INC_AFTER} +) + diff --git a/qa/pns/logviewer.fbp b/qa/pns/logviewer.fbp new file mode 100644 index 0000000000..b22602bf8c --- /dev/null +++ b/qa/pns/logviewer.fbp @@ -0,0 +1,584 @@ + + + + + ; + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + pns_log_viewer_frame_base + 1000 + none + + + 0 + PNS_LOG_VIEWER + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + PNS_LOG_VIEWER_FRAME_BASE + + 500,300 + wxDEFAULT_FRAME_STYLE + ; ; forward_declare + P&S Log Viewer + + + + wxTAB_TRAVERSAL + 1 + + + + 1 + 1 + + + 0 + wxID_ANY + + + m_menubar1 + protected + + + + ; ; forward_declare + + + + + + File + m_menu1 + protected + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + Reload + m_menuItem1 + none + + + onReload + + + + 0 + 1 + + wxID_ANY + wxITEM_NORMAL + Exit + m_menuItem2 + none + + + onExit + + + + + + m_mainSizer + wxVERTICAL + protected + + 5 + wxEXPAND + 0 + + 10 + wxBOTH + + + 0 + + fgSizer3 + wxFLEX_GROWMODE_SPECIFIED + none + 3 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Rewind: + 0 + + 0 + + + 0 + + 1 + m_rewindText + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + + + -1 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + < + + 0 + + 0 + 60,-1 + + 0 + + 1 + m_rewindLeft + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + onBtnRewindLeft + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + 100 + + 0 + + 0 + + 0 + 200,-1 + 1 + m_rewindSlider + 1 + + + protected + 1 + + Resizable + 1 + 200,-1 + wxSL_HORIZONTAL + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + 50 + + + + onRewindScroll + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + 0 + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + > + + 0 + + 0 + 60,-1 + + 0 + + 1 + m_rewindRight + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + onBtnRewindRight + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_rewindPos + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + onRewindCountText2 + onRewindCountText + + + + + + 5 + wxEXPAND + 1 + + + m_viewSizer + wxVERTICAL + protected + + + + 5 + wxEXPAND + 1 + + + bSizer6 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_itemList + 1 + + + protected + 1 + + Resizable + 1 + + wxTL_CHECKBOX|wxTL_DEFAULT_STYLE|wxTL_MULTIPLE + ; ; forward_declare + 0 + + + + + + + + + + + + + 1 + 1 + + 1 + + 0 + wxID_ANY + + + m_statusBar + protected + + + wxSTB_SIZEGRIP + ; ; forward_declare + + + + + + + + diff --git a/qa/pns/pns_log.cpp b/qa/pns/pns_log.cpp new file mode 100644 index 0000000000..9dffbf4e7b --- /dev/null +++ b/qa/pns/pns_log.cpp @@ -0,0 +1,415 @@ +/* + * 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 + */ + +#include "pns_log.h" + +#include + +#include + +using namespace PNS; + +static const wxString readLine( FILE* f ) +{ + char str[16384]; + fgets( str, sizeof( str ) - 1, f ); + return wxString( str ); +} + +PNS_LOG_FILE::PNS_LOG_FILE() +{ + m_routerSettings.reset( new PNS::ROUTING_SETTINGS( nullptr, "" ) ); +} + + +bool PNS_LOG_FILE::Load( const std::string& logName, const std::string boardName ) +{ + FILE* f = fopen( logName.c_str(), "rb" ); + + if (!f) + return false; + + while( !feof( f ) ) + { + wxStringTokenizer tokens( readLine( f ) ); + if( !tokens.CountTokens() ) + continue; + + wxString cmd = tokens.GetNextToken(); + + if (cmd == "event") + { + EVENT_ENTRY evt; + evt.p.x = wxAtoi( tokens.GetNextToken() ); + evt.p.y = wxAtoi( tokens.GetNextToken() ); + evt.type = (PNS::LOGGER::EVENT_TYPE) wxAtoi( tokens.GetNextToken() ); + evt.uuid = KIID( tokens.GetNextToken() ); + m_events.push_back(evt); + } + else if (cmd == "config") + { + m_routerSettings->SetMode( (PNS::PNS_MODE) wxAtoi( tokens.GetNextToken() ) ); + m_routerSettings->SetRemoveLoops( wxAtoi( tokens.GetNextToken() ) ); + m_routerSettings->SetFixAllSegments( wxAtoi( tokens.GetNextToken() ) ); + } + } + + fclose( f ); + + try { + PCB_IO io; + m_board.reset( io.Load( boardName.c_str(), nullptr, nullptr ) ); + + std::shared_ptr drcEngine( new DRC_ENGINE ); + + CONSOLE_LOG consoleLog; + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + + bds.m_DRCEngine = drcEngine; + + drcEngine->SetBoard( m_board.get() ); + drcEngine->SetDesignSettings( &bds ); + drcEngine->SetLogReporter( new CONSOLE_MSG_REPORTER ( &consoleLog ) ); + drcEngine->InitEngine(wxFileName()); + + } catch ( const PARSE_ERROR& parse_error ) { + printf("parse error : %s (%s)\n", + (const char *) parse_error.Problem().c_str(), + (const char *) parse_error.What().c_str() ); + + return false; + } + + return true; +} + +PNS_TEST_ENVIRONMENT::PNS_TEST_ENVIRONMENT() +{ + +} + +PNS_TEST_ENVIRONMENT::~PNS_TEST_ENVIRONMENT() +{ + +} + +void PNS_TEST_ENVIRONMENT::SetMode( PNS::ROUTER_MODE mode ) +{ + m_mode = mode; +} + + +void PNS_TEST_ENVIRONMENT::createRouter() +{ + m_iface.reset ( new PNS_KICAD_IFACE_BASE); + m_router.reset( new ROUTER ); + m_iface->SetBoard( m_board.get() ); + m_router->SetInterface( m_iface.get() ); + m_router->ClearWorld(); + m_router->SetMode( m_mode ); + m_router->SyncWorld(); + m_router->LoadSettings( new PNS::ROUTING_SETTINGS (nullptr, "")); + m_router->Settings().SetMode( PNS::RM_Shove ); + m_router->Settings().SetOptimizeDraggedTrack( true ); + + m_debugDecorator.Clear(); + m_iface->SetDebugDecorator( &m_debugDecorator ); +} + +void PNS_TEST_ENVIRONMENT::ReplayLog ( PNS_LOG_FILE* aLog, int aStartEventIndex, int aFrom, int aTo ) +{ + + m_board = aLog->GetBoard(); + + createRouter(); + + m_router->LoadSettings( aLog->GetRoutingSettings() ); + + printf("Router mode: %d\n", m_router->Settings().Mode() ); + + for( auto evt : aLog->Events() ) + { + auto item = aLog->ItemById(evt); + ITEM* ritem = item ? m_router->GetWorld()->FindItemByParent( item ) : nullptr; + + switch(evt.type) + { + case LOGGER::EVT_START_ROUTE: + { + m_debugDecorator.NewStage("route-start", 0); + printf(" rtr start-route (%d, %d) %p \n", evt.p.x, evt.p.y, ritem); + m_router->StartRouting( evt.p, ritem, ritem ? ritem->Layers().Start() : F_Cu ); + break; + } + + case LOGGER::EVT_START_DRAG: + { + m_debugDecorator.NewStage("drag-start", 0); + bool rv = m_router->StartDragging( evt.p, ritem, 0 ); + printf(" rtr start-drag (%d, %d) %p ret %d\n", evt.p.x, evt.p.y, ritem, rv?1:0); + break; + } + + case LOGGER::EVT_FIX: + { + break; + } + + case LOGGER::EVT_MOVE: + { + m_debugDecorator.NewStage("move", 0); + printf(" move -> (%d, %d)\n", evt.p.x, evt.p.y ); + m_router->Move( evt.p, ritem ); + break; + } + default: + break; + } + + + PNS::NODE* node = nullptr; + + + if( m_router->GetState() == PNS::ROUTER::ROUTE_TRACK ) + { + m_debugDecorator.BeginGroup( "head"); + + auto traces = m_router->Placer()->Traces(); + for ( const auto& t : traces.CItems() ) + { + const LINE *l = static_cast(t.item); + const auto& sh = l->CLine(); + + m_debugDecorator.AddLine( sh, 4, 10000 ); + } + + m_debugDecorator.EndGroup(); + + node = m_router->Placer()->CurrentNode(true); + } + else if( m_router->GetState() == PNS::ROUTER::DRAG_SEGMENT ) + { + node = m_router->GetDragger()->CurrentNode(); + } + + if(!node) + return; + + NODE::ITEM_VECTOR removed, added; + + node->GetUpdatedItems( removed, added ); + + if( ! added.empty() ) + { + bool first = true; + m_debugDecorator.BeginGroup( "node-added-items"); + + for( auto t : added ) + { + if( t->OfKind( PNS::ITEM::SEGMENT_T ) ) + { + auto s = static_cast(t); + m_debugDecorator.AddSegment( s->Seg(), 2 ); + first = false; + } + } + + m_debugDecorator.EndGroup(); + } + } +} + + +PNS_TEST_DEBUG_DECORATOR::STAGE* PNS_TEST_DEBUG_DECORATOR::currentStage() +{ + if (m_stages.empty() ) + m_stages.push_back( new STAGE() ); + + return m_stages.back(); +} + + +void PNS_TEST_DEBUG_DECORATOR::BeginGroup( std::string name ) +{ + STAGE* st = currentStage(); + DEBUG_ENT *ent = new DEBUG_ENT(); + + ent->m_name = name; + ent->m_iter = m_iter; + + if( m_activeEntry ) + { + m_activeEntry->AddChild( ent ); + } + + printf("LOG BeginGroup %s %p\n", name.c_str(), ent ); + + m_activeEntry = ent; + m_grouping = true; +} + + +void PNS_TEST_DEBUG_DECORATOR::EndGroup( ) +{ + printf("LOG EndGroup\n" ); + + if( !m_activeEntry ) + return; + + m_activeEntry = m_activeEntry->m_parent; + + if( !m_activeEntry ) + m_grouping = false; +} + +void PNS_TEST_DEBUG_DECORATOR::addEntry( DEBUG_ENT* ent ) +{ + auto st = currentStage(); + m_activeEntry->AddChild( ent ); +} + +void PNS_TEST_DEBUG_DECORATOR::AddPoint( VECTOR2I aP, int aColor, int aSize, const std::string aName ) +{ + auto sh = new SHAPE_LINE_CHAIN; + + sh->Append( aP.x - aSize, aP.y - aSize); + sh->Append( aP.x + aSize, aP.y + aSize); + sh->Append( aP.x, aP.y ); + sh->Append( aP.x - aSize, aP.y + aSize ); + sh->Append( aP.x + aSize, aP.y - aSize ); + + DEBUG_ENT* ent = new DEBUG_ENT(); + + ent->m_shapes.push_back( sh ); + ent->m_color = aColor; + ent->m_width = 30000; + ent->m_iter = m_iter; + ent->m_name = aName; + + addEntry( ent ); +} + + +void PNS_TEST_DEBUG_DECORATOR::AddLine( const SHAPE_LINE_CHAIN& aLine, int aType, int aWidth, + const std::string aName ) +{ + auto sh = new SHAPE_LINE_CHAIN( aLine ); + DEBUG_ENT* ent = new DEBUG_ENT(); + + ent->m_shapes.push_back( sh ); + ent->m_color = aType; + ent->m_width = aWidth; + ent->m_name = aName; + ent->m_iter = m_iter; + + addEntry( ent ); +} + +void PNS_TEST_DEBUG_DECORATOR::AddSegment( SEG aS, int aColor, const std::string aName ) +{ + auto sh = new SHAPE_LINE_CHAIN ( { aS.A, aS.B } ); + DEBUG_ENT* ent = new DEBUG_ENT(); + + ent->m_shapes.push_back( sh ); + ent->m_color = aColor; + ent->m_width = 10000; + ent->m_name = aName; + ent->m_iter = m_iter; + addEntry( ent ); + +} + +void PNS_TEST_DEBUG_DECORATOR::AddBox( BOX2I aB, int aColor, const std::string aName ) +{ + auto sh = new SHAPE_RECT ( aB.GetPosition(), aB.GetWidth(), aB.GetHeight() ); + DEBUG_ENT* ent = new DEBUG_ENT(); + + ent->m_shapes.push_back( sh ); + ent->m_color = aColor; + ent->m_width = 10000; + ent->m_name = aName; + ent->m_iter = m_iter; + addEntry( ent ); + +} + +void PNS_TEST_DEBUG_DECORATOR::AddDirections( VECTOR2D aP, int aMask, int aColor, const std::string aName ) +{ + +} + +void PNS_TEST_DEBUG_DECORATOR::Message( const wxString msg ) +{ + DEBUG_ENT* ent = new DEBUG_ENT(); + ent->m_msg = msg.c_str(); + addEntry( ent ); +} + +void PNS_TEST_DEBUG_DECORATOR::Clear() +{ + //dec_dbg("clear"); +} + +void PNS_TEST_DEBUG_DECORATOR::NewStage(const std::string& name, int iter) +{ + m_stages.push_back( new STAGE ); + m_activeEntry = m_stages.back()->m_entries; +} + + +void PNS_TEST_DEBUG_DECORATOR::DEBUG_ENT::IterateTree( std::function visitor, int depth ) +{ + printf("LOG D:%d iter: %p\n", depth, this ); + if( ! visitor( this ) ) + return; + + + for( auto child : m_children ) + { + child->IterateTree( visitor, depth+1 ); + } +} + +BOX2I PNS_TEST_DEBUG_DECORATOR::GetStageExtents(int stage) const +{ + STAGE* st = m_stages[stage]; + BOX2I bb; + bool first = true; + + auto visitor = [ & ] ( DEBUG_ENT *ent ) -> bool + { + for( auto sh : ent->m_shapes ) + { + if ( first ) + bb = sh->BBox(); + else + bb.Merge( sh->BBox() ); + + first = false; + } + + return true; + }; + + return bb; +} diff --git a/qa/pns/pns_log.h b/qa/pns/pns_log.h new file mode 100644 index 0000000000..35ffa79fe1 --- /dev/null +++ b/qa/pns/pns_log.h @@ -0,0 +1,267 @@ +/* + * 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 + */ +#ifndef __PNS_LOG_H +#define __PNS_LOG_H + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include + +#include + +class PNS_LOG_FILE +{ +public: + PNS_LOG_FILE(); + ~PNS_LOG_FILE() {} + + struct EVENT_ENTRY + { + VECTOR2I p; + PNS::LOGGER::EVENT_TYPE type; + const PNS::ITEM* item; + KIID uuid; + }; + + // loads a board file and associated P&s event log + bool Load( const std::string& logName, const std::string boardName ); + + BOARD_CONNECTED_ITEM* ItemById( const EVENT_ENTRY& evt ) + { + BOARD_CONNECTED_ITEM* parent = nullptr; + + for( auto item : m_board->AllConnectedItems() ) + { + if( item->m_Uuid == evt.uuid ) + { + parent = item; + break; + }; + } + + return parent; + } + + std::vector& Events() { return m_events; } + + std::shared_ptr GetBoard() const { return m_board; } + + void SetBoard( std::shared_ptr brd ) { m_board = brd; } + + PNS::ROUTING_SETTINGS* GetRoutingSettings() const { return m_routerSettings.get(); } + +private: + std::unique_ptr m_routerSettings; + std::vector m_events; + std::shared_ptr m_board; +}; + + +class PNS_TEST_DEBUG_DECORATOR : public PNS::DEBUG_DECORATOR +{ +public: + struct DEBUG_ENT + { + DEBUG_ENT( DEBUG_ENT* aParent = nullptr ) + { + m_iter = 0; + m_color = 0; + m_width = 10000; + m_name = ""; + m_parent = aParent; + m_visible = true; + m_selected = false; + } + + ~DEBUG_ENT() + { + for( auto s : m_shapes ) + { + delete s; + } + + for( auto ch : m_children ) + { + delete ch; + } + } + + DEBUG_ENT* NewChild() + { + DEBUG_ENT* ent = new DEBUG_ENT( this ); + m_children.push_back( ent ); + + return ent; + } + + void AddChild( DEBUG_ENT* ent ) + { + ent->m_parent = this; + m_children.push_back( ent ); + } + + bool IsVisible() const + { + if ( m_visible ) + return true; + + auto parent = m_parent; + + while(parent) + { + if(parent->m_visible) + return true; + + parent = parent->m_parent; + } + + return false; + } + + void IterateTree( std::function visitor, int depth=0 ); + + DEBUG_ENT* m_parent; + std::vector m_shapes; + std::vector m_children; + int m_color; + int m_width; + int m_iter; + std::string m_name; + std::string m_msg; + //wxTreeListItem m_item; + bool m_visible; + bool m_selected; + }; + + struct STAGE + { + STAGE() + { + m_name = ""; + m_iter = 0; + m_entries = new DEBUG_ENT(); + } + + ~STAGE() + { + } + + std::string m_name; + int m_iter; + DEBUG_ENT* m_entries; + }; + + PNS_TEST_DEBUG_DECORATOR() + { + m_iter = 0; + m_grouping = false; + m_activeEntry = nullptr; + } + + virtual ~PNS_TEST_DEBUG_DECORATOR() {} + + virtual void SetIteration( int iter ) override { m_iter = iter; } + + virtual void Message( const wxString msg ) override; + virtual void AddPoint( VECTOR2I aP, int aColor, int aSize = 100000, + const std::string aName = "" ) override; + virtual void AddLine( const SHAPE_LINE_CHAIN& aLine, int aType = 0, int aWidth = 0, + const std::string aName = "" ) override; + virtual void AddSegment( SEG aS, int aColor, const std::string aName = "" ) override; + virtual void AddBox( BOX2I aB, int aColor, const std::string aName = "" ) override; + virtual void AddDirections( VECTOR2D aP, int aMask, int aColor, + const std::string aName = "" ) override; + virtual void Clear() override; + virtual void NewStage( const std::string& name, int iter ) override; + + virtual void BeginGroup( const std::string name ) override; + virtual void EndGroup() override; + + int GetStageCount() const { return m_stages.size(); } + + STAGE* GetStage( int index ) { return m_stages[index]; } + + BOX2I GetStageExtents( int stage ) const; + +private: + void addEntry( DEBUG_ENT* ent ); + + bool m_grouping; + DEBUG_ENT* m_activeEntry; + STAGE* currentStage(); + int m_iter; + std::vector m_stages; +}; + + +class PNS_TEST_ENVIRONMENT +{ +public: + PNS_TEST_ENVIRONMENT(); + ~PNS_TEST_ENVIRONMENT(); + + void SetMode( PNS::ROUTER_MODE mode ); + void ReplayLog( PNS_LOG_FILE* aLog, int aStartEventIndex = 0, int aFrom = 0, int aTo = -1 ); + + PNS_TEST_DEBUG_DECORATOR* GetDebugDecorator() { return &m_debugDecorator; }; + +private: + void createRouter(); + + PNS::ROUTER_MODE m_mode; + PNS_TEST_DEBUG_DECORATOR m_debugDecorator; + std::shared_ptr m_board; + std::unique_ptr m_iface; + std::unique_ptr m_router; +}; + + +#endif diff --git a/qa/pns/pns_log_viewer.cpp b/qa/pns/pns_log_viewer.cpp new file mode 100644 index 0000000000..7703495f29 --- /dev/null +++ b/qa/pns/pns_log_viewer.cpp @@ -0,0 +1,1438 @@ +/* + * 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, +} ); + + diff --git a/qa/pns/pns_log_viewer_frame_base.cpp b/qa/pns/pns_log_viewer_frame_base.cpp new file mode 100644 index 0000000000..7c1e077291 --- /dev/null +++ b/qa/pns/pns_log_viewer_frame_base.cpp @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version 3.9.0 Feb 6 2021) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "pns_log_viewer_frame_base.h" + +/////////////////////////////////////////////////////////////////////////// + +PNS_LOG_VIEWER_FRAME_BASE::PNS_LOG_VIEWER_FRAME_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + m_menubar1 = new wxMenuBar( 0 ); + m_menu1 = new wxMenu(); + wxMenuItem* m_menuItem1; + m_menuItem1 = new wxMenuItem( m_menu1, wxID_ANY, wxString( wxT("Reload") ) , wxEmptyString, wxITEM_NORMAL ); + m_menu1->Append( m_menuItem1 ); + + wxMenuItem* m_menuItem2; + m_menuItem2 = new wxMenuItem( m_menu1, wxID_ANY, wxString( wxT("Exit") ) , wxEmptyString, wxITEM_NORMAL ); + m_menu1->Append( m_menuItem2 ); + + m_menubar1->Append( m_menu1, wxT("File") ); + + this->SetMenuBar( m_menubar1 ); + + m_mainSizer = new wxBoxSizer( wxVERTICAL ); + + wxFlexGridSizer* fgSizer3; + fgSizer3 = new wxFlexGridSizer( 3, 10, 0, 0 ); + fgSizer3->SetFlexibleDirection( wxBOTH ); + fgSizer3->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_rewindText = new wxStaticText( this, wxID_ANY, wxT("Rewind: "), wxDefaultPosition, wxDefaultSize, 0 ); + m_rewindText->Wrap( -1 ); + fgSizer3->Add( m_rewindText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_rewindLeft = new wxButton( this, wxID_ANY, wxT("<"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rewindLeft->SetMaxSize( wxSize( 60,-1 ) ); + + fgSizer3->Add( m_rewindLeft, 0, wxALL, 5 ); + + m_rewindSlider = new wxSlider( this, wxID_ANY, 50, 0, 100, wxDefaultPosition, wxSize( 200,-1 ), wxSL_HORIZONTAL ); + m_rewindSlider->SetMinSize( wxSize( 200,-1 ) ); + + fgSizer3->Add( m_rewindSlider, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_rewindRight = new wxButton( this, wxID_ANY, wxT(">"), wxDefaultPosition, wxDefaultSize, 0 ); + m_rewindRight->SetMaxSize( wxSize( 60,-1 ) ); + + fgSizer3->Add( m_rewindRight, 1, wxALL, 5 ); + + m_rewindPos = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_rewindPos, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + + m_mainSizer->Add( fgSizer3, 0, wxEXPAND, 5 ); + + m_viewSizer = new wxBoxSizer( wxVERTICAL ); + + + m_mainSizer->Add( m_viewSizer, 1, wxEXPAND, 5 ); + + wxBoxSizer* bSizer6; + bSizer6 = new wxBoxSizer( wxVERTICAL ); + + m_itemList = new wxTreeListCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTL_CHECKBOX|wxTL_DEFAULT_STYLE|wxTL_MULTIPLE ); + + bSizer6->Add( m_itemList, 1, wxALL|wxEXPAND, 5 ); + + + m_mainSizer->Add( bSizer6, 1, wxEXPAND, 5 ); + + + this->SetSizer( m_mainSizer ); + this->Layout(); + m_statusBar = this->CreateStatusBar( 1, wxSTB_SIZEGRIP, wxID_ANY ); + + this->Centre( wxBOTH ); + + // Connect Events + m_menu1->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onReload ), this, m_menuItem1->GetId()); + m_menu1->Bind(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onExit ), this, m_menuItem2->GetId()); + m_rewindLeft->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onBtnRewindLeft ), NULL, this ); + m_rewindSlider->Connect( wxEVT_SCROLL_TOP, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Connect( wxEVT_SCROLL_BOTTOM, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Connect( wxEVT_SCROLL_LINEUP, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Connect( wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Connect( wxEVT_SCROLL_PAGEUP, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Connect( wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Connect( wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Connect( wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Connect( wxEVT_SCROLL_CHANGED, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindRight->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onBtnRewindRight ), NULL, this ); + m_rewindPos->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindCountText2 ), NULL, this ); + m_rewindPos->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindCountText ), NULL, this ); +} + +PNS_LOG_VIEWER_FRAME_BASE::~PNS_LOG_VIEWER_FRAME_BASE() +{ + // Disconnect Events + m_rewindLeft->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onBtnRewindLeft ), NULL, this ); + m_rewindSlider->Disconnect( wxEVT_SCROLL_TOP, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Disconnect( wxEVT_SCROLL_BOTTOM, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Disconnect( wxEVT_SCROLL_LINEUP, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Disconnect( wxEVT_SCROLL_LINEDOWN, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Disconnect( wxEVT_SCROLL_PAGEUP, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Disconnect( wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Disconnect( wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Disconnect( wxEVT_SCROLL_THUMBRELEASE, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindSlider->Disconnect( wxEVT_SCROLL_CHANGED, wxScrollEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindScroll ), NULL, this ); + m_rewindRight->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onBtnRewindRight ), NULL, this ); + m_rewindPos->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindCountText2 ), NULL, this ); + m_rewindPos->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( PNS_LOG_VIEWER_FRAME_BASE::onRewindCountText ), NULL, this ); + +} diff --git a/qa/pns/pns_log_viewer_frame_base.h b/qa/pns/pns_log_viewer_frame_base.h new file mode 100644 index 0000000000..68c7b077e3 --- /dev/null +++ b/qa/pns/pns_log_viewer_frame_base.h @@ -0,0 +1,70 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version 3.9.0 Feb 6 2021) +// http://www.wxformbuilder.org/ +// +// PLEASE DO *NOT* EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class PNS_LOG_VIEWER_FRAME_BASE +/////////////////////////////////////////////////////////////////////////////// +class PNS_LOG_VIEWER_FRAME_BASE : public wxFrame +{ + private: + + protected: + wxMenuBar* m_menubar1; + wxMenu* m_menu1; + wxBoxSizer* m_mainSizer; + wxStaticText* m_rewindText; + wxButton* m_rewindLeft; + wxSlider* m_rewindSlider; + wxButton* m_rewindRight; + wxTextCtrl* m_rewindPos; + wxBoxSizer* m_viewSizer; + wxTreeListCtrl* m_itemList; + wxStatusBar* m_statusBar; + + // Virtual event handlers, override them in your derived class + virtual void onReload( wxCommandEvent& event ) { event.Skip(); } + virtual void onExit( wxCommandEvent& event ) { event.Skip(); } + virtual void onBtnRewindLeft( wxCommandEvent& event ) { event.Skip(); } + virtual void onRewindScroll( wxScrollEvent& event ) { event.Skip(); } + virtual void onBtnRewindRight( wxCommandEvent& event ) { event.Skip(); } + virtual void onRewindCountText2( wxCommandEvent& event ) { event.Skip(); } + virtual void onRewindCountText( wxCommandEvent& event ) { event.Skip(); } + + + public: + + PNS_LOG_VIEWER_FRAME_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("P&S Log Viewer"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 500,300 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); + + ~PNS_LOG_VIEWER_FRAME_BASE(); + +}; + diff --git a/qa/pns/pns_test_window.cpp b/qa/pns/pns_test_window.cpp new file mode 100644 index 0000000000..92ec5f0907 --- /dev/null +++ b/qa/pns/pns_test_window.cpp @@ -0,0 +1,105 @@ +#include +#include + +#include + +#include "pns_log_viewer_frame_base.h" +#include "pns_log.h" + + +class PNS_TEST_FRAME : public PNS_LOG_VIEWER_FRAME_BASE +{ +public: + PNS_TEST_FRAME( wxFrame* frame, + const wxString& title, + const wxPoint& pos = wxDefaultPosition, + const wxSize& size = wxDefaultSize, + long style = wxDEFAULT_FRAME_STYLE ) : + PNS_LOG_VIEWER_FRAME_BASE( frame, title, pos, size, style ) + { + #if 0 + // Make a menubar + wxMenu* fileMenu = new wxMenu; + + fileMenu->Append( wxID_OPEN, wxT( "&Open..." ) ); + fileMenu->AppendSeparator(); + fileMenu->Append( wxID_EXIT, wxT( "E&xit" ) ); + wxMenuBar* menuBar = new wxMenuBar; + menuBar->Append( fileMenu, wxT( "&File" ) ); + + createMenus( menuBar ); + SetMenuBar( menuBar ); + + createUserUI( this ); + + Show( true ); + Maximize(); + Raise(); + + auto settings = static_cast ( m_galPanel->GetView()->GetPainter()->GetSettings() ); + settings->SetZoneDisplayMode( KIGFX::PCB_RENDER_SETTINGS::DZ_HIDE_FILLED ); + #endif + } + + virtual ~PNS_TEST_FRAME() {} + + void LoadLogFile( const std::string& aFileName ); + +private: +#if 0 + void createMenus( wxMenuBar *menubar ) override + { + printf("pns::create menus\n"); + wxMenu* testMenu = new wxMenu; + + testMenu->Append( -1, wxT( "Select run" ) ); + testMenu->Append( -1, wxT( "Replay" ) ); + testMenu->Append( -1, wxT( "Dump geometry to file" ) ); + + menubar->Append( testMenu, wxT("Tests") ); + } + + void createUserUI( wxWindow *aParent ) override; + void drawLoggedItems(); + + wxSlider* m_historySlider; +#endif + + std::unique_ptr m_overlay; + PNS_LOG_FILE m_logFile; + PNS_TEST_ENVIRONMENT m_env; +}; + + +wxFrame* CreateMainFrame( const std::string& aFileName ) +{ + auto frame = new PNS_TEST_FRAME( nullptr, wxT( "P&S Test" ) ); + + frame->LoadLogFile( aFileName ); + + return frame; +} + + +void PNS_TEST_FRAME::LoadLogFile( const std::string& aFileName ) +{ + printf("Loading P&S log data from '%s'\n", aFileName.c_str() ); + + bool rv = m_logFile.Load( aFileName ); + + if(!rv) + { + printf("Log load failure\n"); + return; + } + + SetBoard( m_logFile.GetBoard() ); + + m_overlay.reset( new KIGFX::VIEW_OVERLAY() ); + m_galPanel->GetView()->Add( m_overlay.get() ); + + m_env.SetMode( PNS::PNS_MODE_ROUTE_SINGLE ); + m_env.ReplayLog( &m_logFile ); + + drawLoggedItems(); +} \ No newline at end of file diff --git a/qa/qa_utils/pcb_test_frame.cpp b/qa/qa_utils/pcb_test_frame.cpp index 5bfae48371..d600fcf1b5 100644 --- a/qa/qa_utils/pcb_test_frame.cpp +++ b/qa/qa_utils/pcb_test_frame.cpp @@ -62,7 +62,7 @@ #include #include #include -#include +#include #include #include "pcb_test_frame.h" @@ -122,12 +122,10 @@ class TEST_ACTIONS : public ACTIONS void PCB_TEST_FRAME_BASE::createView( wxWindow *aParent, PCB_DRAW_PANEL_GAL::GAL_TYPE aGalType ) { - KIGFX::GAL_DISPLAY_OPTIONS options; - - options.gl_antialiasing_mode = KIGFX::OPENGL_ANTIALIASING_MODE::NONE; //SUPERSAMPLING_X4; + m_displayOptions.gl_antialiasing_mode = KIGFX::OPENGL_ANTIALIASING_MODE::NONE; //SUPERSAMPLING_X4; m_galPanel = std::make_shared( aParent, -1, wxPoint( 0, - 0 ), wxDefaultSize, options, aGalType ); + 0 ), wxDefaultSize, m_displayOptions, aGalType ); m_galPanel->UpdateColors(); m_galPanel->SetEvtHandlerEnabled( true ); diff --git a/qa/qa_utils/pcb_test_frame.h b/qa/qa_utils/pcb_test_frame.h index 111a99f731..36dfb53d17 100644 --- a/qa/qa_utils/pcb_test_frame.h +++ b/qa/qa_utils/pcb_test_frame.h @@ -67,6 +67,8 @@ protected: std::shared_ptr < PCB_DRAW_PANEL_GAL > m_galPanel; std::shared_ptr < BOARD > m_board; + KIGFX::GAL_DISPLAY_OPTIONS m_displayOptions; + #ifdef USE_TOOL_MANAGER unique_ptr < TOOL_MANAGER > m_toolManager; unique_ptr < TOOL_DISPATCHER > m_toolDispatcher;