2023-10-07 03:54:14 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2023 Alex Shvartzkop <dudesuchamazing@gmail.com>
|
|
|
|
* Copyright (C) 2023 KiCad Developers, see AUTHORS.txt for contributors.
|
|
|
|
*
|
|
|
|
* 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 <pcb_generator.h>
|
|
|
|
#include <generators_mgr.h>
|
|
|
|
|
|
|
|
#include <optional>
|
|
|
|
#include <magic_enum.hpp>
|
|
|
|
|
|
|
|
#include <wx/debug.h>
|
|
|
|
#include <geometry/shape_circle.h>
|
2023-10-09 14:22:16 +00:00
|
|
|
#include <kiplatform/ui.h>
|
2023-10-09 20:55:07 +00:00
|
|
|
#include <dialogs/dialog_unit_entry.h>
|
2023-10-09 14:22:16 +00:00
|
|
|
#include <collectors.h>
|
2023-10-11 15:02:07 +00:00
|
|
|
#include <scoped_set_reset.h>
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-09 20:55:07 +00:00
|
|
|
#include <board_design_settings.h>
|
|
|
|
#include <drc/drc_engine.h>
|
2023-10-07 03:54:14 +00:00
|
|
|
#include <pcb_track.h>
|
|
|
|
#include <pcb_shape.h>
|
|
|
|
#include <pcb_group.h>
|
|
|
|
|
|
|
|
#include <tool/edit_points.h>
|
|
|
|
#include <tools/drawing_tool.h>
|
|
|
|
#include <tools/generator_tool.h>
|
2023-10-09 14:22:16 +00:00
|
|
|
#include <tools/pcb_picker_tool.h>
|
|
|
|
#include <tools/pcb_selection_tool.h>
|
2023-10-11 13:43:54 +00:00
|
|
|
#include <tools/zone_filler_tool.h>
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
#include <preview_items/draw_context.h>
|
|
|
|
#include <view/view.h>
|
|
|
|
|
|
|
|
#include <router/pns_meander_placer_base.h>
|
|
|
|
#include <router/pns_meander.h>
|
|
|
|
#include <router/pns_kicad_iface.h>
|
|
|
|
#include <router/pns_segment.h>
|
|
|
|
#include <router/pns_arc.h>
|
|
|
|
#include <router/pns_topology.h>
|
2023-10-09 20:55:07 +00:00
|
|
|
#include <router/pns_tune_status_popup.h>
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
#include <dialogs/dialog_meander_properties.h>
|
|
|
|
|
|
|
|
|
|
|
|
enum LENGTH_TUNING_MODE
|
|
|
|
{
|
|
|
|
SINGLE,
|
|
|
|
DIFF_PAIR,
|
|
|
|
DIFF_PAIR_SKEW
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static LENGTH_TUNING_MODE TuningFromString( const std::string& aStr )
|
|
|
|
{
|
|
|
|
if( aStr == "single" )
|
|
|
|
return LENGTH_TUNING_MODE::SINGLE;
|
|
|
|
else if( aStr == "diff_pair" )
|
|
|
|
return LENGTH_TUNING_MODE::DIFF_PAIR;
|
|
|
|
else if( aStr == "diff_pair_skew" )
|
|
|
|
return LENGTH_TUNING_MODE::DIFF_PAIR_SKEW;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wxFAIL_MSG( wxS( "Unknown length tuning token" ) );
|
|
|
|
return LENGTH_TUNING_MODE::SINGLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static std::string TuningToString( const LENGTH_TUNING_MODE aTuning )
|
|
|
|
{
|
|
|
|
switch( aTuning )
|
|
|
|
{
|
|
|
|
case LENGTH_TUNING_MODE::SINGLE: return "single";
|
|
|
|
case LENGTH_TUNING_MODE::DIFF_PAIR: return "diff_pair";
|
|
|
|
case LENGTH_TUNING_MODE::DIFF_PAIR_SKEW: return "diff_pair_skew";
|
|
|
|
default: wxFAIL; return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-10-11 16:10:48 +00:00
|
|
|
static LENGTH_TUNING_MODE FromPNSMode( PNS::ROUTER_MODE aRouterMode )
|
|
|
|
{
|
|
|
|
switch( aRouterMode )
|
|
|
|
{
|
|
|
|
case PNS::PNS_MODE_TUNE_SINGLE: return LENGTH_TUNING_MODE::SINGLE;
|
|
|
|
case PNS::PNS_MODE_TUNE_DIFF_PAIR: return LENGTH_TUNING_MODE::DIFF_PAIR;
|
|
|
|
case PNS::PNS_MODE_TUNE_DIFF_PAIR_SKEW: return LENGTH_TUNING_MODE::DIFF_PAIR_SKEW;
|
|
|
|
default: return LENGTH_TUNING_MODE::SINGLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
static PNS::MEANDER_SIDE SideFromString( const std::string& aStr )
|
|
|
|
{
|
|
|
|
if( aStr == "default" )
|
|
|
|
return PNS::MEANDER_SIDE_DEFAULT;
|
|
|
|
else if( aStr == "left" )
|
|
|
|
return PNS::MEANDER_SIDE_LEFT;
|
|
|
|
else if( aStr == "right" )
|
|
|
|
return PNS::MEANDER_SIDE_RIGHT;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wxFAIL_MSG( wxS( "Unknown meander side token" ) );
|
|
|
|
return PNS::MEANDER_SIDE_DEFAULT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static std::string StatusToString( const PNS::MEANDER_PLACER_BASE::TUNING_STATUS aStatus )
|
|
|
|
{
|
|
|
|
switch( aStatus )
|
|
|
|
{
|
|
|
|
case PNS::MEANDER_PLACER_BASE::TOO_LONG: return "too_long";
|
|
|
|
case PNS::MEANDER_PLACER_BASE::TOO_SHORT: return "too_short";
|
|
|
|
case PNS::MEANDER_PLACER_BASE::TUNED: return "tuned";
|
|
|
|
default: wxFAIL; return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static PNS::MEANDER_PLACER_BASE::TUNING_STATUS StatusFromString( const std::string& aStr )
|
|
|
|
{
|
|
|
|
if( aStr == "too_long" )
|
|
|
|
return PNS::MEANDER_PLACER_BASE::TOO_LONG;
|
|
|
|
else if( aStr == "too_short" )
|
|
|
|
return PNS::MEANDER_PLACER_BASE::TOO_SHORT;
|
|
|
|
else if( aStr == "tuned" )
|
|
|
|
return PNS::MEANDER_PLACER_BASE::TUNED;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
wxFAIL_MSG( wxS( "Unknown tuning status token" ) );
|
|
|
|
return PNS::MEANDER_PLACER_BASE::TUNED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static std::string SideToString( const PNS::MEANDER_SIDE aValue )
|
|
|
|
{
|
|
|
|
switch( aValue )
|
|
|
|
{
|
|
|
|
case PNS::MEANDER_SIDE_DEFAULT: return "default";
|
|
|
|
case PNS::MEANDER_SIDE_LEFT: return "left";
|
|
|
|
case PNS::MEANDER_SIDE_RIGHT: return "right";
|
|
|
|
default: wxFAIL; return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class PCB_GENERATOR_MEANDERS : public PCB_GENERATOR
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
static const wxString GENERATOR_TYPE;
|
|
|
|
static const wxString DISPLAY_NAME;
|
|
|
|
|
2023-10-10 10:08:53 +00:00
|
|
|
PCB_GENERATOR_MEANDERS( BOARD_ITEM* aParent = nullptr, PCB_LAYER_ID aLayer = F_Cu,
|
|
|
|
LENGTH_TUNING_MODE aMode = LENGTH_TUNING_MODE::SINGLE ) :
|
|
|
|
PCB_GENERATOR( aParent, aLayer ),
|
|
|
|
m_singleSide( false ),
|
|
|
|
m_rounded( true ),
|
|
|
|
m_tuningMode( aMode ),
|
|
|
|
m_tuningStatus( PNS::MEANDER_PLACER_BASE::TUNING_STATUS::TUNED )
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
|
|
|
m_generatorType = GENERATOR_TYPE;
|
|
|
|
m_name = DISPLAY_NAME;
|
|
|
|
|
|
|
|
m_minAmplitude = pcbIUScale.mmToIU( 0.1 );
|
|
|
|
m_maxAmplitude = pcbIUScale.mmToIU( 2.0 );
|
|
|
|
m_spacing = pcbIUScale.mmToIU( 0.6 );
|
|
|
|
m_targetLength = pcbIUScale.mmToIU( 100 );
|
|
|
|
m_targetSkew = pcbIUScale.mmToIU( 0 );
|
|
|
|
m_end = VECTOR2I( pcbIUScale.mmToIU( 10 ), 0 );
|
|
|
|
m_cornerRadiusPercentage = 100;
|
|
|
|
m_initialSide = PNS::MEANDER_SIDE_DEFAULT;
|
|
|
|
}
|
|
|
|
|
|
|
|
wxString GetGeneratorType() const override { return wxS( "meanders" ); }
|
|
|
|
|
|
|
|
int snapToNearestTrackPoint( VECTOR2I& aP, BOARD* aBoard, int aNet )
|
|
|
|
{
|
|
|
|
SEG::ecoord minDistSq = VECTOR2I::ECOORD_MAX;
|
|
|
|
VECTOR2I closestPt = aP;
|
|
|
|
int closestNet = -1;
|
|
|
|
|
|
|
|
for( PCB_TRACK *track : aBoard->Tracks() )
|
|
|
|
{
|
|
|
|
if( aNet >= 0 && track->GetNetCode() != aNet )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
SEG seg ( track->GetStart(), track->GetEnd() );
|
|
|
|
|
|
|
|
VECTOR2I nearest = seg.NearestPoint( aP );
|
|
|
|
SEG::ecoord distSq = ( nearest - aP ).SquaredEuclideanNorm();
|
|
|
|
|
|
|
|
if( distSq < minDistSq )
|
|
|
|
{
|
|
|
|
minDistSq = distSq;
|
|
|
|
closestPt = nearest;
|
|
|
|
closestNet = track->GetNetCode();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( minDistSq != VECTOR2I::ECOORD_MAX )
|
|
|
|
{
|
|
|
|
aP = closestPt;
|
|
|
|
return closestNet;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
bool baselineValid()
|
|
|
|
{
|
|
|
|
if( m_tuningMode == DIFF_PAIR )
|
|
|
|
{
|
|
|
|
return( m_baseLine && m_baseLine->PointCount() > 1
|
|
|
|
&& m_baseLineCoupled && m_baseLineCoupled->PointCount() > 1 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return( m_baseLine && m_baseLine->PointCount() > 1 );
|
|
|
|
}
|
|
|
|
}
|
2023-10-08 13:04:45 +00:00
|
|
|
|
2023-10-10 10:08:53 +00:00
|
|
|
static PCB_GENERATOR_MEANDERS* CreateNew( GENERATOR_TOOL* aTool, PCB_BASE_EDIT_FRAME* aFrame,
|
2023-10-11 16:10:48 +00:00
|
|
|
BOARD_CONNECTED_ITEM* aStartItem,
|
|
|
|
LENGTH_TUNING_MODE aMode )
|
2023-10-09 20:55:07 +00:00
|
|
|
{
|
2023-10-10 10:08:53 +00:00
|
|
|
BOARD* board = aStartItem->GetBoard();
|
2023-10-09 20:55:07 +00:00
|
|
|
std::shared_ptr<DRC_ENGINE>& drcEngine = board->GetDesignSettings().m_DRCEngine;
|
|
|
|
DRC_CONSTRAINT constraint;
|
2023-10-11 16:10:48 +00:00
|
|
|
PCB_LAYER_ID layer = aStartItem->GetLayer();
|
|
|
|
PNS::RULE_RESOLVER* resolver = aTool->Router()->GetRuleResolver();
|
2023-10-09 20:55:07 +00:00
|
|
|
|
2023-10-11 16:10:48 +00:00
|
|
|
if( aMode == SINGLE && resolver->DpCoupledNet( aStartItem->GetNet() ) )
|
|
|
|
aMode = DIFF_PAIR;
|
2023-10-10 10:08:53 +00:00
|
|
|
|
2023-10-11 16:10:48 +00:00
|
|
|
PCB_GENERATOR_MEANDERS* meander = new PCB_GENERATOR_MEANDERS( board, layer, aMode );
|
2023-10-10 10:08:53 +00:00
|
|
|
|
|
|
|
constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, aStartItem, nullptr, layer );
|
2023-10-09 20:55:07 +00:00
|
|
|
|
2023-10-11 16:10:48 +00:00
|
|
|
if( aMode == DIFF_PAIR_SKEW )
|
2023-10-09 20:55:07 +00:00
|
|
|
{
|
2023-10-11 16:10:48 +00:00
|
|
|
if( constraint.IsNull() )
|
|
|
|
{
|
|
|
|
WX_UNIT_ENTRY_DIALOG dlg( aFrame, _( "Tune Skew" ), _( "Target skew:" ), 0 );
|
2023-10-09 20:55:07 +00:00
|
|
|
|
2023-10-11 16:10:48 +00:00
|
|
|
if( dlg.ShowModal() != wxID_OK )
|
|
|
|
return nullptr;
|
2023-10-09 20:55:07 +00:00
|
|
|
|
2023-10-11 16:10:48 +00:00
|
|
|
meander->m_targetSkew = dlg.GetValue();
|
|
|
|
meander->m_overrideCustomRules = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
meander->m_targetSkew = constraint.GetValue().Opt();
|
|
|
|
meander->m_overrideCustomRules = false;
|
|
|
|
}
|
2023-10-09 20:55:07 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-10-11 16:10:48 +00:00
|
|
|
if( constraint.IsNull() )
|
|
|
|
{
|
|
|
|
WX_UNIT_ENTRY_DIALOG dlg( aFrame, _( "Tune Length" ), _( "Target length:" ),
|
|
|
|
100 * PCB_IU_PER_MM );
|
|
|
|
|
|
|
|
if( dlg.ShowModal() != wxID_OK )
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
meander->m_targetLength = dlg.GetValue();
|
|
|
|
meander->m_overrideCustomRules = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
meander->m_targetLength = constraint.GetValue().Opt();
|
|
|
|
meander->m_overrideCustomRules = false;
|
|
|
|
}
|
2023-10-09 20:55:07 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 21:32:17 +00:00
|
|
|
meander->SetFlags( IS_NEW );
|
|
|
|
|
2023-10-09 20:55:07 +00:00
|
|
|
return meander;
|
|
|
|
}
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
void EditStart( GENERATOR_TOOL* aTool, BOARD* aBoard, PCB_BASE_EDIT_FRAME* aFrame,
|
|
|
|
BOARD_COMMIT* aCommit ) override
|
|
|
|
{
|
2023-10-09 20:48:34 +00:00
|
|
|
m_removedItems.clear();
|
|
|
|
|
2023-10-09 14:22:16 +00:00
|
|
|
if( aCommit )
|
2023-10-09 21:32:17 +00:00
|
|
|
{
|
|
|
|
if( IsNew() )
|
|
|
|
aCommit->Add( this );
|
|
|
|
else
|
|
|
|
aCommit->Modify( this );
|
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 23:30:16 +00:00
|
|
|
// Remove ourselves from the selection so that we don't redraw our existing children as
|
|
|
|
// part of the selection VIEW_GROUP (which ignores the HIDDEN flags).
|
|
|
|
PCB_SELECTION_TOOL*selectionTool = aFrame->GetToolManager()->GetTool<PCB_SELECTION_TOOL>();
|
|
|
|
selectionTool->RemoveItemFromSel( this, true );
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
int layer = GetLayer();
|
|
|
|
PNS::ROUTER* router = aTool->Router();
|
|
|
|
|
|
|
|
aTool->ClearRouterCommit();
|
|
|
|
router->SyncWorld();
|
|
|
|
|
|
|
|
if( !baselineValid() )
|
|
|
|
{
|
|
|
|
InitBaseLine( router, layer, aBoard );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-09 20:59:55 +00:00
|
|
|
PNS::LINKED_ITEM* PickSegment( PNS::ROUTER* aRouter, const VECTOR2I& aWhere, int aLayer,
|
|
|
|
VECTOR2I& aPointOut )
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
|
|
|
static const int candidateCount = 2;
|
|
|
|
PNS::LINKED_ITEM* prioritized[candidateCount];
|
|
|
|
SEG::ecoord dist[candidateCount];
|
|
|
|
VECTOR2I point[candidateCount];
|
|
|
|
|
|
|
|
for( int i = 0; i < candidateCount; i++ )
|
|
|
|
{
|
|
|
|
prioritized[i] = nullptr;
|
|
|
|
dist[i] = VECTOR2I::ECOORD_MAX;
|
|
|
|
}
|
|
|
|
|
2023-10-09 20:59:55 +00:00
|
|
|
auto haveCandidates =
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
for( PNS::ITEM* item : prioritized )
|
|
|
|
{
|
|
|
|
if( item )
|
|
|
|
return true;
|
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-09 20:59:55 +00:00
|
|
|
return false;
|
|
|
|
};
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
for( bool useClearance : { false, true } )
|
|
|
|
{
|
|
|
|
PNS::ITEM_SET candidates = aRouter->QueryHoverItems( aWhere, useClearance );
|
|
|
|
|
|
|
|
for( PNS::ITEM* item : candidates.Items() )
|
|
|
|
{
|
|
|
|
if( !item->OfKind( PNS::ITEM::SEGMENT_T | PNS::ITEM::ARC_T ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( !item->IsRoutable() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if( !item->Layers().Overlaps( aLayer ) )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
PNS::LINKED_ITEM* linked = static_cast<PNS::LINKED_ITEM*>( item );
|
|
|
|
|
|
|
|
if( item->Kind() & PNS::ITEM::ARC_T )
|
|
|
|
{
|
|
|
|
SEG::ecoord d0 = ( item->Anchor( 0 ) - aWhere ).SquaredEuclideanNorm();
|
|
|
|
SEG::ecoord d1 = ( item->Anchor( 1 ) - aWhere ).SquaredEuclideanNorm();
|
|
|
|
|
|
|
|
if( d0 <= dist[1] )
|
|
|
|
{
|
|
|
|
prioritized[1] = linked;
|
|
|
|
dist[1] = d0;
|
|
|
|
point[1] = item->Anchor( 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( d1 <= dist[1] )
|
|
|
|
{
|
|
|
|
prioritized[1] = linked;
|
|
|
|
dist[1] = d1;
|
|
|
|
point[1] = item->Anchor( 1 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( item->Kind() & PNS::ITEM::SEGMENT_T )
|
|
|
|
{
|
|
|
|
PNS::SEGMENT* segm = static_cast<PNS::SEGMENT*>( item );
|
|
|
|
|
|
|
|
VECTOR2I nearest = segm->CLine().NearestPoint( aWhere, false );
|
|
|
|
SEG::ecoord dd = ( aWhere - nearest ).SquaredEuclideanNorm();
|
|
|
|
|
|
|
|
if( dd <= dist[1] )
|
|
|
|
{
|
|
|
|
prioritized[1] = segm;
|
|
|
|
dist[1] = dd;
|
|
|
|
point[1] = nearest;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( haveCandidates() )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
PNS::LINKED_ITEM* rv = nullptr;
|
|
|
|
|
|
|
|
for( int i = 0; i < candidateCount; i++ )
|
|
|
|
{
|
|
|
|
PNS::LINKED_ITEM* item = prioritized[i];
|
|
|
|
|
|
|
|
if( item && ( aLayer < 0 || item->Layers().Overlaps( aLayer ) ) )
|
|
|
|
{
|
|
|
|
rv = item;
|
|
|
|
aPointOut = point[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
bool initBaseLine( PNS::ROUTER* router, int layer, BOARD* aBoard, VECTOR2I& aStart,
|
|
|
|
VECTOR2I& aEnd, int aNetCode, std::optional<SHAPE_LINE_CHAIN>& aBaseLine )
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
|
|
|
PNS::NODE* world = router->GetWorld();
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
snapToNearestTrackPoint( aStart, aBoard, aNetCode );
|
|
|
|
snapToNearestTrackPoint( aEnd, aBoard, aNetCode );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
VECTOR2I startSnapPoint, endSnapPoint;
|
2023-10-08 13:04:45 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
PNS::LINKED_ITEM* startItem = PickSegment( router, aStart, layer, startSnapPoint );
|
|
|
|
PNS::LINKED_ITEM* endItem = PickSegment( router, aEnd, layer, endSnapPoint );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
wxASSERT( startItem );
|
|
|
|
wxASSERT( endItem );
|
|
|
|
|
2023-10-09 20:48:34 +00:00
|
|
|
if( !startItem || !endItem || startSnapPoint == endSnapPoint )
|
2023-10-07 03:54:14 +00:00
|
|
|
return false;
|
|
|
|
|
2023-10-09 20:59:55 +00:00
|
|
|
PNS::LINE line = world->AssembleLine( startItem, nullptr, false, true );
|
|
|
|
const SHAPE_LINE_CHAIN& chain = line.CLine();
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
wxASSERT( line.ContainsLink( endItem ) );
|
|
|
|
|
|
|
|
wxASSERT( chain.PointOnEdge( startSnapPoint, 1 ) );
|
|
|
|
wxASSERT( chain.PointOnEdge( endSnapPoint, 1 ) );
|
|
|
|
|
|
|
|
SHAPE_LINE_CHAIN pre;
|
|
|
|
SHAPE_LINE_CHAIN mid;
|
|
|
|
SHAPE_LINE_CHAIN post;
|
|
|
|
|
|
|
|
chain.Split( startSnapPoint, endSnapPoint, pre, mid, post );
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
aBaseLine = mid;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
bool InitBaseLine( PNS::ROUTER* router, int layer, BOARD* aBoard )
|
|
|
|
{
|
|
|
|
m_baseLineCoupled.reset();
|
|
|
|
|
|
|
|
int netCode = snapToNearestTrackPoint( m_origin, aBoard, -1 );
|
|
|
|
|
|
|
|
if( !initBaseLine( router, layer, aBoard, m_origin, m_end, netCode, m_baseLine ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if( m_tuningMode == DIFF_PAIR )
|
|
|
|
{
|
|
|
|
PNS::RULE_RESOLVER* resolver = router->GetRuleResolver();
|
|
|
|
|
|
|
|
if( PNS::NET_HANDLE handle = resolver->DpCoupledNet( aBoard->FindNet( netCode ) ) )
|
|
|
|
{
|
|
|
|
int coupledNet = static_cast<NETINFO_ITEM*>( handle )->GetNetCode();
|
|
|
|
VECTOR2I coupledStart = m_origin;
|
|
|
|
VECTOR2I coupledEnd = m_end;
|
|
|
|
|
|
|
|
return initBaseLine( router, layer, aBoard, coupledStart, coupledEnd, coupledNet,
|
|
|
|
m_baseLineCoupled );
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void removeToBaseline( PNS::ROUTER* aRouter, int aLayer, SHAPE_LINE_CHAIN& baseLine )
|
|
|
|
{
|
|
|
|
VECTOR2I startSnapPoint, endSnapPoint;
|
|
|
|
|
|
|
|
std::optional<PNS::LINE> line = getLine( baseLine.CPoint( 0 ), baseLine.CPoint( -1 ),
|
|
|
|
aRouter, aLayer, startSnapPoint, endSnapPoint );
|
|
|
|
|
|
|
|
wxCHECK( line, /* void */ );
|
|
|
|
|
|
|
|
SHAPE_LINE_CHAIN pre;
|
|
|
|
SHAPE_LINE_CHAIN mid;
|
|
|
|
SHAPE_LINE_CHAIN post;
|
|
|
|
line->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
|
|
|
|
|
|
|
|
// LINE does not have a separate remover, as LINEs are never truly a member of the tree
|
|
|
|
for( PNS::LINKED_ITEM* li : line->Links() )
|
|
|
|
aRouter->GetInterface()->RemoveItem( li );
|
|
|
|
|
|
|
|
aRouter->GetWorld()->Remove( *line );
|
|
|
|
|
|
|
|
SHAPE_LINE_CHAIN straightChain;
|
|
|
|
straightChain.Append( pre );
|
|
|
|
straightChain.Append( baseLine );
|
|
|
|
straightChain.Append( post );
|
|
|
|
straightChain.Simplify();
|
|
|
|
|
|
|
|
PNS::LINE straightLine( *line, straightChain );
|
|
|
|
|
|
|
|
// LINE does not have a separate remover, as LINEs are never truly a member of the tree
|
|
|
|
aRouter->GetWorld()->Add( straightLine, false );
|
|
|
|
|
|
|
|
for( PNS::LINKED_ITEM* li : straightLine.Links() )
|
|
|
|
aRouter->GetInterface()->AddItem( li );
|
|
|
|
}
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
void Remove( GENERATOR_TOOL* aTool, BOARD* aBoard, PCB_BASE_EDIT_FRAME* aFrame,
|
|
|
|
BOARD_COMMIT* aCommit ) override
|
|
|
|
{
|
|
|
|
aTool->Router()->SyncWorld();
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
PNS::ROUTER* router = aTool->Router();
|
|
|
|
int layer = GetLayer();
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
// Ungroup first so that undo works
|
|
|
|
if( !GetItems().empty() )
|
|
|
|
{
|
|
|
|
PCB_GENERATOR* group = this;
|
|
|
|
PICKED_ITEMS_LIST undoList;
|
|
|
|
|
|
|
|
for( BOARD_ITEM* member : group->GetItems() )
|
|
|
|
undoList.PushItem( ITEM_PICKER( nullptr, member, UNDO_REDO::UNGROUP ) );
|
|
|
|
|
|
|
|
group->RemoveAll();
|
|
|
|
|
|
|
|
aFrame->SaveCopyInUndoList( undoList, UNDO_REDO::UNGROUP );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aCommit->Push( "" );
|
|
|
|
}
|
|
|
|
|
|
|
|
aCommit->Remove( this );
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
aTool->ClearRouterCommit();
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
if( baselineValid() )
|
|
|
|
{
|
2023-10-12 14:45:33 +00:00
|
|
|
removeToBaseline( router, layer, *m_baseLine );
|
|
|
|
removeToBaseline( router, layer, *m_baseLineCoupled );
|
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
std::set<BOARD_ITEM*> clearRouterRemovedItems = aTool->GetRouterCommitRemovedItems();
|
|
|
|
std::set<BOARD_ITEM*> clearRouterAddedItems = aTool->GetRouterCommitAddedItems();
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
for( BOARD_ITEM* item : clearRouterRemovedItems )
|
|
|
|
{
|
|
|
|
item->ClearSelected();
|
|
|
|
aCommit->Remove( item );
|
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
for( BOARD_ITEM* item : clearRouterAddedItems )
|
|
|
|
{
|
|
|
|
aCommit->Add( item );
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 20:46:24 +00:00
|
|
|
aCommit->Push( "Remove Meander", APPEND_UNDO );
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 20:49:57 +00:00
|
|
|
std::optional<PNS::LINE> getLine( const VECTOR2I& aStart, const VECTOR2I& aEnd,
|
|
|
|
PNS::ROUTER* router, int layer, VECTOR2I& aStartOut,
|
|
|
|
VECTOR2I& aEndOut )
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
|
|
|
PNS::NODE* world = router->GetWorld();
|
|
|
|
|
2023-10-09 20:59:55 +00:00
|
|
|
PNS::LINKED_ITEM* startItem = PickSegment( router, aStart, layer, aStartOut );
|
|
|
|
PNS::LINKED_ITEM* endItem = PickSegment( router, aEnd, layer, aEndOut );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
wxASSERT( startItem );
|
|
|
|
wxASSERT( endItem );
|
|
|
|
|
|
|
|
if( !startItem || !endItem )
|
|
|
|
return std::nullopt;
|
|
|
|
|
|
|
|
PNS::LINE line = world->AssembleLine( startItem, nullptr, false, true );
|
|
|
|
SHAPE_LINE_CHAIN oldChain = line.CLine();
|
|
|
|
|
|
|
|
wxCHECK( line.ContainsLink( endItem ), std::nullopt );
|
|
|
|
|
|
|
|
wxASSERT( oldChain.PointOnEdge( aStartOut, 1 ) );
|
|
|
|
wxASSERT( oldChain.PointOnEdge( aEndOut, 1 ) );
|
|
|
|
|
|
|
|
return line;
|
|
|
|
}
|
|
|
|
|
|
|
|
PNS::MEANDER_SETTINGS ToMeanderSettings()
|
|
|
|
{
|
|
|
|
PNS::MEANDER_SETTINGS settings;
|
|
|
|
|
|
|
|
settings.m_cornerStyle = m_rounded ? PNS::MEANDER_STYLE::MEANDER_STYLE_ROUND
|
|
|
|
: PNS::MEANDER_STYLE::MEANDER_STYLE_CHAMFER;
|
|
|
|
|
|
|
|
settings.m_minAmplitude = m_minAmplitude;
|
|
|
|
settings.m_maxAmplitude = m_maxAmplitude;
|
|
|
|
settings.m_spacing = m_spacing;
|
|
|
|
settings.m_targetLength = m_targetLength;
|
|
|
|
settings.m_targetSkew = m_targetSkew;
|
2023-10-11 10:48:43 +00:00
|
|
|
settings.m_overrideCustomRules = m_overrideCustomRules;
|
2023-10-07 03:54:14 +00:00
|
|
|
settings.m_singleSided = m_singleSide;
|
|
|
|
settings.m_segmentSide = m_initialSide;
|
|
|
|
settings.m_cornerRadiusPercentage = m_cornerRadiusPercentage;
|
|
|
|
|
|
|
|
return settings;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FromMeanderSettings( const PNS::MEANDER_SETTINGS& aSettings )
|
|
|
|
{
|
|
|
|
m_rounded = aSettings.m_cornerStyle == PNS::MEANDER_STYLE::MEANDER_STYLE_ROUND;
|
|
|
|
m_minAmplitude = aSettings.m_minAmplitude;
|
|
|
|
m_maxAmplitude = aSettings.m_maxAmplitude;
|
|
|
|
m_spacing = aSettings.m_spacing;
|
|
|
|
m_targetLength = aSettings.m_targetLength;
|
|
|
|
m_targetSkew = aSettings.m_targetSkew;
|
2023-10-11 10:48:43 +00:00
|
|
|
m_overrideCustomRules = aSettings.m_overrideCustomRules;
|
2023-10-07 03:54:14 +00:00
|
|
|
m_singleSide = aSettings.m_singleSided;
|
|
|
|
m_initialSide = aSettings.m_segmentSide;
|
|
|
|
m_cornerRadiusPercentage = aSettings.m_cornerRadiusPercentage;
|
|
|
|
}
|
|
|
|
|
|
|
|
PNS::ROUTER_MODE ToPNSMode()
|
|
|
|
{
|
|
|
|
switch( m_tuningMode )
|
|
|
|
{
|
2023-10-11 10:48:43 +00:00
|
|
|
case LENGTH_TUNING_MODE::SINGLE: return PNS::PNS_MODE_TUNE_SINGLE;
|
|
|
|
case LENGTH_TUNING_MODE::DIFF_PAIR: return PNS::PNS_MODE_TUNE_DIFF_PAIR;
|
2023-10-07 03:54:14 +00:00
|
|
|
case LENGTH_TUNING_MODE::DIFF_PAIR_SKEW: return PNS::PNS_MODE_TUNE_DIFF_PAIR_SKEW;
|
2023-10-11 10:48:43 +00:00
|
|
|
default: return PNS::PNS_MODE_TUNE_SINGLE;
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
bool resetToBaseline( PNS::ROUTER* aRouter, int aLayer, PCB_BASE_EDIT_FRAME* aFrame,
|
|
|
|
SHAPE_LINE_CHAIN& aBaseLine, bool aPrimary )
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
2023-10-12 14:45:33 +00:00
|
|
|
PNS::NODE* world = aRouter->GetWorld();
|
|
|
|
VECTOR2I startSnapPoint, endSnapPoint;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
std::optional<PNS::LINE> line = getLine( aBaseLine.CPoint( 0 ), aBaseLine.CPoint( -1 ),
|
|
|
|
aRouter, aLayer, startSnapPoint, endSnapPoint );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
wxCHECK( line, false );
|
|
|
|
|
|
|
|
SHAPE_LINE_CHAIN straightChain;
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
2023-10-12 14:45:33 +00:00
|
|
|
SHAPE_LINE_CHAIN pre, mid, post;
|
|
|
|
line->CLine().Split( startSnapPoint, endSnapPoint, pre, mid, post );
|
|
|
|
|
|
|
|
straightChain.Append( pre );
|
|
|
|
straightChain.Append( aBaseLine );
|
|
|
|
straightChain.Append( post );
|
|
|
|
straightChain.Simplify();
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
// LINE does not have a separate remover, as LINEs are never truly a member of the tree
|
|
|
|
for( PNS::LINKED_ITEM* li : line->Links() )
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
2023-10-12 14:45:33 +00:00
|
|
|
if( li->Parent() )
|
|
|
|
{
|
|
|
|
aFrame->GetCanvas()->GetView()->Hide( li->Parent() );
|
|
|
|
m_removedItems.insert( li->Parent() );
|
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
world->Remove( *line );
|
|
|
|
|
|
|
|
PNS::LINE straightLine( *line, straightChain );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
world->Add( straightLine, false );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
if( aPrimary )
|
|
|
|
{
|
|
|
|
m_origin = straightChain.NearestPoint( m_origin );
|
|
|
|
m_end = straightChain.NearestPoint( m_end );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
// Don't allow points too close
|
|
|
|
if( ( m_end - m_origin ).EuclideanNorm() < pcbIUScale.mmToIU( 0.1 ) )
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
2023-10-12 14:45:33 +00:00
|
|
|
m_origin = startSnapPoint;
|
|
|
|
m_end = endSnapPoint;
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
SHAPE_LINE_CHAIN pre, mid, post;
|
2023-10-12 14:45:33 +00:00
|
|
|
straightChain.Split( m_origin, m_end, pre, mid, post );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
m_baseLine = mid;
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
2023-10-12 14:45:33 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
VECTOR2I start = straightChain.NearestPoint( m_origin );
|
|
|
|
VECTOR2I end = straightChain.NearestPoint( m_end );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
{
|
2023-10-12 14:45:33 +00:00
|
|
|
SHAPE_LINE_CHAIN pre, mid, post;
|
|
|
|
straightChain.Split( start, end, pre, mid, post );
|
|
|
|
|
|
|
|
m_baseLineCoupled = mid;
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
2023-10-12 14:45:33 +00:00
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
bool Update( GENERATOR_TOOL* aTool, BOARD* aBoard, PCB_BASE_EDIT_FRAME* aFrame,
|
|
|
|
BOARD_COMMIT* aCommit ) override
|
|
|
|
{
|
|
|
|
PNS::ROUTER* router = aTool->Router();
|
|
|
|
PNS_KICAD_IFACE* iface = aTool->GetInterface();
|
|
|
|
int layer = GetLayer();
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
iface->SetStartLayer( layer );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
if( router->RoutingInProgress() )
|
|
|
|
{
|
|
|
|
router->StopRouting();
|
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
if( !baselineValid() )
|
|
|
|
{
|
|
|
|
InitBaseLine( router, layer, aBoard );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if( resetToBaseline( router, layer, aFrame, *m_baseLine, true ) )
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
2023-10-12 14:45:33 +00:00
|
|
|
m_origin = m_baseLine->CPoint( 0 );
|
|
|
|
m_end = m_baseLine->CPoint( -1 );
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
2023-10-12 14:45:33 +00:00
|
|
|
else
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
2023-10-12 14:45:33 +00:00
|
|
|
InitBaseLine( router, layer, aBoard );
|
|
|
|
return false;
|
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
if( m_tuningMode == DIFF_PAIR
|
|
|
|
&& !resetToBaseline( router, layer, aFrame, *m_baseLineCoupled, false ) )
|
|
|
|
{
|
|
|
|
InitBaseLine( router, layer, aBoard );
|
|
|
|
return false;
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Snap points
|
|
|
|
VECTOR2I startSnapPoint, endSnapPoint;
|
|
|
|
|
2023-10-09 20:59:55 +00:00
|
|
|
PNS::LINKED_ITEM* startItem = PickSegment( router, m_origin, layer, startSnapPoint );
|
|
|
|
PNS::LINKED_ITEM* endItem = PickSegment( router, m_end, layer, endSnapPoint );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
wxASSERT( startItem );
|
|
|
|
wxASSERT( endItem );
|
|
|
|
|
|
|
|
if( !startItem || !endItem )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
router->SetMode( ToPNSMode() );
|
|
|
|
|
|
|
|
if( !router->StartRouting( startSnapPoint, startItem, layer ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
|
|
|
|
|
|
|
|
PNS::MEANDER_SETTINGS settings = ToMeanderSettings();
|
|
|
|
|
|
|
|
placer->UpdateSettings( settings );
|
|
|
|
router->Move( m_end, nullptr );
|
|
|
|
|
|
|
|
m_lastNetName = iface->GetNetName( startItem->Net() );
|
|
|
|
m_tuningInfo = placer->TuningInfo( aFrame->GetUserUnits() );
|
|
|
|
m_tuningStatus = placer->TuningStatus();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditPush( GENERATOR_TOOL* aTool, BOARD* aBoard, PCB_BASE_EDIT_FRAME* aFrame,
|
2023-10-09 14:22:16 +00:00
|
|
|
BOARD_COMMIT* aCommit, const wxString& aCommitMsg = wxEmptyString,
|
|
|
|
int aCommitFlags = 0 ) override
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
|
|
|
PNS::ROUTER* router = aTool->Router();
|
|
|
|
|
|
|
|
if( router->RoutingInProgress() )
|
|
|
|
{
|
|
|
|
router->FixRoute( m_end, nullptr, true );
|
|
|
|
router->StopRouting();
|
|
|
|
|
|
|
|
std::set<BOARD_ITEM*> routerRemovedItems = aTool->GetRouterCommitRemovedItems();
|
|
|
|
std::set<BOARD_ITEM*> routerAddedItems = aTool->GetRouterCommitAddedItems();
|
|
|
|
|
2023-10-09 20:48:34 +00:00
|
|
|
for( BOARD_ITEM* item : m_removedItems )
|
|
|
|
{
|
2023-10-12 14:47:58 +00:00
|
|
|
aFrame->GetCanvas()->GetView()->Hide( item, false );
|
2023-10-09 20:48:34 +00:00
|
|
|
aCommit->Remove( item );
|
|
|
|
}
|
|
|
|
|
|
|
|
m_removedItems.clear();
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
for( BOARD_ITEM* item : routerRemovedItems )
|
|
|
|
{
|
|
|
|
aCommit->Remove( item );
|
|
|
|
}
|
|
|
|
|
|
|
|
for( BOARD_ITEM* item : routerAddedItems )
|
|
|
|
{
|
2023-10-10 14:24:58 +00:00
|
|
|
item->SetSelected();
|
2023-10-07 03:54:14 +00:00
|
|
|
AddItem( item );
|
|
|
|
aCommit->Add( item );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-10 14:24:58 +00:00
|
|
|
if( aCommitMsg.IsEmpty() )
|
|
|
|
aCommit->Push( _( "Edit Meander" ), aCommitFlags );
|
|
|
|
else
|
|
|
|
aCommit->Push( aCommitMsg, aCommitFlags );
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
|
2023-10-09 20:48:34 +00:00
|
|
|
void EditRevert( GENERATOR_TOOL* aTool, BOARD* aBoard, PCB_BASE_EDIT_FRAME* aFrame,
|
|
|
|
BOARD_COMMIT* aCommit ) override
|
|
|
|
{
|
|
|
|
for( BOARD_ITEM* item : m_removedItems )
|
2023-10-11 23:30:16 +00:00
|
|
|
aFrame->GetCanvas()->GetView()->Hide( item, false );
|
2023-10-09 20:48:34 +00:00
|
|
|
|
|
|
|
m_removedItems.clear();
|
|
|
|
|
2023-10-11 14:50:15 +00:00
|
|
|
aTool->Router()->StopRouting();
|
|
|
|
|
2023-10-09 20:48:34 +00:00
|
|
|
if( aCommit )
|
|
|
|
aCommit->Revert();
|
|
|
|
}
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
bool MakeEditPoints( std::shared_ptr<EDIT_POINTS> points ) const override
|
|
|
|
{
|
|
|
|
points->AddPoint( m_origin );
|
|
|
|
points->AddPoint( m_end );
|
|
|
|
|
|
|
|
SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
|
|
|
|
: SEG( m_origin, m_end );
|
|
|
|
|
|
|
|
int offset = m_maxAmplitude;
|
|
|
|
|
|
|
|
if( m_initialSide == -1 )
|
|
|
|
offset *= -1;
|
|
|
|
|
|
|
|
VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( offset );
|
|
|
|
|
|
|
|
points->AddPoint( m_origin + widthHandleOffset );
|
|
|
|
points->Point( 2 ).SetGridConstraint( IGNORE_GRID );
|
|
|
|
|
|
|
|
VECTOR2I spacingHandleOffset =
|
2023-10-09 20:59:55 +00:00
|
|
|
widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_spacing * 1.5 ) );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
points->AddPoint( m_origin + spacingHandleOffset );
|
|
|
|
points->Point( 3 ).SetGridConstraint( IGNORE_GRID );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UpdateFromEditPoints( std::shared_ptr<EDIT_POINTS> aEditPoints,
|
|
|
|
BOARD_COMMIT* aCommit ) override
|
|
|
|
{
|
|
|
|
SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
|
|
|
|
: SEG( m_origin, m_end );
|
|
|
|
|
|
|
|
m_origin = aEditPoints->Point( 0 ).GetPosition();
|
|
|
|
m_end = aEditPoints->Point( 1 ).GetPosition();
|
|
|
|
|
|
|
|
if( aEditPoints->Point( 2 ).IsActive() )
|
|
|
|
{
|
|
|
|
VECTOR2I wHandle = aEditPoints->Point( 2 ).GetPosition();
|
|
|
|
|
|
|
|
int value = base.LineDistance( wHandle );
|
|
|
|
SetMaxAmplitude( KiROUND( value / pcbIUScale.mmToIU( 0.1 ) ) * pcbIUScale.mmToIU( 0.1 ) );
|
|
|
|
|
|
|
|
int side = base.Side( wHandle );
|
|
|
|
|
|
|
|
if( side < 0 )
|
|
|
|
m_initialSide = PNS::MEANDER_SIDE_LEFT;
|
|
|
|
else
|
|
|
|
m_initialSide = PNS::MEANDER_SIDE_RIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( aEditPoints->Point( 3 ).IsActive() )
|
|
|
|
{
|
|
|
|
VECTOR2I wHandle = aEditPoints->Point( 2 ).GetPosition();
|
|
|
|
VECTOR2I sHandle = aEditPoints->Point( 3 ).GetPosition();
|
|
|
|
|
2023-10-09 20:59:55 +00:00
|
|
|
int value = KiROUND( SEG( m_origin, wHandle ).LineDistance( sHandle ) / 1.5 );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
SetSpacing( KiROUND( value / pcbIUScale.mmToIU( 0.01 ) ) * pcbIUScale.mmToIU( 0.01 ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool UpdateEditPoints( std::shared_ptr<EDIT_POINTS> aEditPoints ) override
|
|
|
|
{
|
|
|
|
SEG base = m_baseLine && m_baseLine->SegmentCount() > 0 ? m_baseLine->CSegment( 0 )
|
|
|
|
: SEG( m_origin, m_end );
|
|
|
|
|
|
|
|
int offset = m_maxAmplitude;
|
|
|
|
|
|
|
|
if( m_initialSide == -1 )
|
|
|
|
offset *= -1;
|
|
|
|
|
|
|
|
VECTOR2I widthHandleOffset = ( base.B - base.A ).Perpendicular().Resize( offset );
|
|
|
|
|
|
|
|
aEditPoints->Point( 0 ).SetPosition( m_origin );
|
|
|
|
aEditPoints->Point( 1 ).SetPosition( m_end );
|
|
|
|
|
|
|
|
aEditPoints->Point( 2 ).SetPosition( m_origin + widthHandleOffset );
|
|
|
|
|
|
|
|
VECTOR2I spacingHandleOffset =
|
2023-10-09 20:59:55 +00:00
|
|
|
widthHandleOffset + ( base.B - base.A ).Resize( KiROUND( m_spacing * 1.5 ) );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
aEditPoints->Point( 3 ).SetPosition( m_origin + spacingHandleOffset );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
SHAPE_LINE_CHAIN GetRectShape() const
|
|
|
|
{
|
|
|
|
SHAPE_LINE_CHAIN chain;
|
|
|
|
|
|
|
|
if( m_baseLine )
|
|
|
|
{
|
|
|
|
bool singleSided = m_singleSide;
|
|
|
|
|
|
|
|
if( singleSided )
|
|
|
|
{
|
|
|
|
SHAPE_LINE_CHAIN left, right;
|
|
|
|
|
|
|
|
if( m_baseLine->OffsetLine( m_maxAmplitude, CORNER_STRATEGY::ROUND_ALL_CORNERS,
|
|
|
|
ARC_LOW_DEF, left, right, true ) )
|
|
|
|
{
|
|
|
|
chain.Append( m_baseLine->CPoint( 0 ) );
|
|
|
|
chain.Append( m_initialSide >= 0 ? right : left );
|
|
|
|
chain.Append( m_baseLine->CPoint( -1 ) );
|
|
|
|
|
|
|
|
return chain;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
singleSided = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !singleSided )
|
|
|
|
{
|
|
|
|
SHAPE_POLY_SET poly;
|
|
|
|
|
2023-10-09 20:59:55 +00:00
|
|
|
poly.OffsetLineChain( *m_baseLine, m_maxAmplitude * 2,
|
|
|
|
CORNER_STRATEGY::ROUND_ALL_CORNERS, ARC_LOW_DEF, false );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
if( poly.OutlineCount() > 0 )
|
|
|
|
{
|
|
|
|
chain = poly.Outline( 0 );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return chain;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Move( const VECTOR2I& aMoveVector ) override
|
|
|
|
{
|
|
|
|
m_origin += aMoveVector;
|
|
|
|
m_end += aMoveVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
const BOX2I GetBoundingBox() const override { return GetRectShape().BBox(); }
|
|
|
|
|
|
|
|
void ViewGetLayers( int aLayers[], int& aCount ) const override
|
|
|
|
{
|
|
|
|
aCount = 0;
|
|
|
|
aLayers[aCount++] = LAYER_ANCHOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override
|
|
|
|
{
|
|
|
|
return GetRectShape().Collide( aPosition, aAccuracy );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const override
|
|
|
|
{
|
|
|
|
return GetBoundingBox().Intersects( aRect );
|
|
|
|
}
|
|
|
|
|
|
|
|
const BOX2I ViewBBox() const override { return GetBoundingBox(); }
|
|
|
|
|
|
|
|
EDA_ITEM* Clone() const override { return new PCB_GENERATOR_MEANDERS( *this ); }
|
|
|
|
|
|
|
|
void swapData( BOARD_ITEM* aImage ) override
|
|
|
|
{
|
|
|
|
wxASSERT( aImage->Type() == PCB_GENERATOR_T );
|
|
|
|
|
|
|
|
std::swap( *this, *static_cast<PCB_GENERATOR_MEANDERS*>( aImage ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewDraw( int aLayer, KIGFX::VIEW* aView ) const override final
|
|
|
|
{
|
2023-10-11 13:09:53 +00:00
|
|
|
if( !IsSelected() && !IsNew() )
|
2023-10-07 03:54:14 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
KIGFX::PREVIEW::DRAW_CONTEXT ctx( *aView );
|
2023-10-11 11:06:10 +00:00
|
|
|
int size = KiROUND( aView->ToWorld( EDIT_POINT::POINT_SIZE ) * 0.8 );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
if( m_baseLine )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < m_baseLine->SegmentCount(); i++ )
|
|
|
|
{
|
|
|
|
SEG seg = m_baseLine->CSegment( i );
|
|
|
|
ctx.DrawLine( seg.A, seg.B, false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ctx.DrawLine( m_origin, m_end, false );
|
|
|
|
}
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
if( m_baseLineCoupled )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < m_baseLineCoupled->SegmentCount(); i++ )
|
|
|
|
{
|
|
|
|
SEG seg = m_baseLineCoupled->CSegment( i );
|
|
|
|
ctx.DrawLine( seg.A, seg.B, false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
SHAPE_LINE_CHAIN chain = GetRectShape();
|
|
|
|
|
|
|
|
for( int i = 0; i < chain.SegmentCount(); i++ )
|
|
|
|
{
|
|
|
|
SEG seg = chain.Segment( i );
|
2023-10-11 11:06:10 +00:00
|
|
|
ctx.DrawLineDashed( seg.A, seg.B, size, size / 2, false );
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const VECTOR2I& GetEnd() const { return m_end; }
|
|
|
|
void SetEnd( const VECTOR2I& aValue ) { m_end = aValue; }
|
|
|
|
|
|
|
|
int GetEndX() const { return m_end.x; }
|
|
|
|
void SetEndX( int aValue ) { m_end.x = aValue; }
|
|
|
|
|
|
|
|
int GetEndY() const { return m_end.y; }
|
|
|
|
void SetEndY( int aValue ) { m_end.y = aValue; }
|
|
|
|
|
|
|
|
LENGTH_TUNING_MODE GetTuningMode() const { return m_tuningMode; }
|
|
|
|
void SetTuningMode( LENGTH_TUNING_MODE aValue ) { m_tuningMode = aValue; }
|
|
|
|
|
|
|
|
int GetMinAmplitude() const { return m_minAmplitude; }
|
|
|
|
void SetMinAmplitude( int aValue ) { m_minAmplitude = aValue; }
|
|
|
|
|
|
|
|
int GetMaxAmplitude() const { return m_maxAmplitude; }
|
|
|
|
void SetMaxAmplitude( int aValue ) { m_maxAmplitude = aValue; }
|
|
|
|
|
|
|
|
PNS::MEANDER_SIDE GetInitialSide() const { return m_initialSide; }
|
|
|
|
void SetInitialSide( PNS::MEANDER_SIDE aValue ) { m_initialSide = aValue; }
|
|
|
|
|
|
|
|
int GetSpacing() const { return m_spacing; }
|
|
|
|
void SetSpacing( int aValue ) { m_spacing = aValue; }
|
|
|
|
|
2023-10-11 13:09:53 +00:00
|
|
|
long long int GetTargetLength() const { return m_targetLength; }
|
|
|
|
void SetTargetLength( long long int aValue ) { m_targetLength = aValue; }
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
int GetTargetSkew() const { return m_targetSkew; }
|
|
|
|
void SetTargetSkew( int aValue ) { m_targetSkew = aValue; }
|
|
|
|
|
2023-10-11 10:48:43 +00:00
|
|
|
bool GetOverrideCustomRules() const { return m_overrideCustomRules; }
|
|
|
|
void SetOverrideCustomRules( bool aOverride ) { m_overrideCustomRules = aOverride; }
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
int GetCornerRadiusPercentage() const { return m_cornerRadiusPercentage; }
|
|
|
|
void SetCornerRadiusPercentage( int aValue ) { m_cornerRadiusPercentage = aValue; }
|
|
|
|
|
|
|
|
bool IsSingleSided() const { return m_singleSide; }
|
|
|
|
void SetSingleSided( bool aValue ) { m_singleSide = aValue; }
|
|
|
|
|
|
|
|
bool IsRounded() const { return m_rounded; }
|
|
|
|
void SetRounded( bool aValue ) { m_rounded = aValue; }
|
|
|
|
|
|
|
|
std::vector<std::pair<wxString, wxVariant>> GetRowData() override
|
|
|
|
{
|
|
|
|
std::vector<std::pair<wxString, wxVariant>> data = PCB_GENERATOR::GetRowData();
|
|
|
|
data.emplace_back( _HKI( "Net" ), m_lastNetName );
|
|
|
|
data.emplace_back( _HKI( "Tuning" ), m_tuningInfo );
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
const STRING_ANY_MAP GetProperties() const override
|
|
|
|
{
|
|
|
|
STRING_ANY_MAP props = PCB_GENERATOR::GetProperties();
|
|
|
|
|
|
|
|
props.set( "tuning_mode", TuningToString( m_tuningMode ) );
|
|
|
|
props.set( "initial_side", SideToString( m_initialSide ) );
|
|
|
|
props.set( "last_status", StatusToString( m_tuningStatus ) );
|
|
|
|
|
|
|
|
props.set( "end", m_end );
|
|
|
|
props.set( "corner_radius_percent", m_cornerRadiusPercentage );
|
|
|
|
props.set( "single_sided", m_singleSide );
|
|
|
|
props.set( "rounded", m_rounded );
|
|
|
|
|
|
|
|
props.set_iu( "max_amplitude", m_maxAmplitude );
|
|
|
|
props.set_iu( "min_spacing", m_spacing );
|
|
|
|
props.set_iu( "target_length", m_targetLength );
|
|
|
|
props.set_iu( "target_skew", m_targetSkew );
|
|
|
|
|
|
|
|
props.set( "last_netname", m_lastNetName );
|
|
|
|
props.set( "last_tuning", m_tuningInfo );
|
2023-10-11 10:48:43 +00:00
|
|
|
props.set( "override_custom_rules", m_overrideCustomRules );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
if( m_baseLine )
|
|
|
|
props.set( "base_line", wxAny( *m_baseLine ) );
|
|
|
|
|
2023-10-12 14:45:33 +00:00
|
|
|
if( m_baseLineCoupled )
|
|
|
|
props.set( "base_line_coupled", wxAny( *m_baseLineCoupled ) );
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
return props;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetProperties( const STRING_ANY_MAP& aProps ) override
|
|
|
|
{
|
|
|
|
PCB_GENERATOR::SetProperties( aProps );
|
|
|
|
|
|
|
|
wxString tuningMode;
|
|
|
|
aProps.get_to( "tuning_mode", tuningMode );
|
|
|
|
m_tuningMode = TuningFromString( tuningMode.utf8_string() );
|
|
|
|
|
|
|
|
wxString side;
|
|
|
|
aProps.get_to( "initial_side", side );
|
|
|
|
m_initialSide = SideFromString( side.utf8_string() );
|
|
|
|
|
|
|
|
wxString status;
|
|
|
|
aProps.get_to( "last_status", status );
|
|
|
|
m_tuningStatus = StatusFromString( status.utf8_string() );
|
|
|
|
|
|
|
|
aProps.get_to( "end", m_end );
|
|
|
|
aProps.get_to( "corner_radius_percent", m_cornerRadiusPercentage );
|
|
|
|
aProps.get_to( "single_sided", m_singleSide );
|
|
|
|
aProps.get_to( "side", m_initialSide );
|
|
|
|
aProps.get_to( "rounded", m_rounded );
|
|
|
|
|
|
|
|
aProps.get_to_iu( "max_amplitude", m_maxAmplitude );
|
|
|
|
aProps.get_to_iu( "min_spacing", m_spacing );
|
|
|
|
aProps.get_to_iu( "target_length", m_targetLength );
|
|
|
|
aProps.get_to_iu( "target_skew", m_targetSkew );
|
2023-10-11 10:48:43 +00:00
|
|
|
aProps.get_to( "override_custom_rules", m_overrideCustomRules );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
aProps.get_to( "last_netname", m_lastNetName );
|
|
|
|
aProps.get_to( "last_tuning", m_tuningInfo );
|
|
|
|
|
|
|
|
if( auto baseLine = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line" ) )
|
|
|
|
m_baseLine = *baseLine;
|
2023-10-12 14:45:33 +00:00
|
|
|
|
|
|
|
if( auto baseLineCoupled = aProps.get_opt<SHAPE_LINE_CHAIN>( "base_line_coupled" ) )
|
|
|
|
m_baseLineCoupled = *baseLineCoupled;
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
|
2023-10-08 13:04:45 +00:00
|
|
|
void ShowPropertiesDialog( PCB_BASE_EDIT_FRAME* aEditFrame ) override
|
2023-10-07 03:54:14 +00:00
|
|
|
{
|
|
|
|
PNS::MEANDER_SETTINGS settings = ToMeanderSettings();
|
2023-10-11 10:48:43 +00:00
|
|
|
DRC_CONSTRAINT constraint;
|
|
|
|
|
|
|
|
if( !m_items.empty() )
|
|
|
|
{
|
|
|
|
BOARD_ITEM* startItem = *m_items.begin();
|
|
|
|
std::shared_ptr<DRC_ENGINE>& drcEngine = GetBoard()->GetDesignSettings().m_DRCEngine;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 10:48:43 +00:00
|
|
|
constraint = drcEngine->EvalRules( LENGTH_CONSTRAINT, startItem, nullptr, GetLayer() );
|
|
|
|
|
|
|
|
if( !constraint.IsNull() && !settings.m_overrideCustomRules )
|
|
|
|
settings.m_targetLength = constraint.GetValue().Opt();
|
|
|
|
}
|
|
|
|
|
|
|
|
DIALOG_MEANDER_PROPERTIES dlg( aEditFrame, settings, ToPNSMode(), constraint );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
if( dlg.ShowModal() == wxID_OK )
|
|
|
|
{
|
|
|
|
BOARD_COMMIT commit( aEditFrame );
|
|
|
|
commit.Modify( this );
|
|
|
|
|
|
|
|
FromMeanderSettings( settings );
|
|
|
|
|
2023-10-10 14:24:58 +00:00
|
|
|
commit.Push( _( "Edit Meander Properties" ) );
|
2023-10-07 03:54:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
aEditFrame->GetToolManager()->PostAction<PCB_GENERATOR*>( PCB_ACTIONS::regenerateItem,
|
|
|
|
this );
|
|
|
|
}
|
|
|
|
|
2023-10-10 10:55:57 +00:00
|
|
|
void UpdateStatus( GENERATOR_TOOL* aTool, PCB_BASE_EDIT_FRAME* aFrame,
|
|
|
|
STATUS_TEXT_POPUP* aPopup ) override
|
|
|
|
{
|
|
|
|
auto* placer = dynamic_cast<PNS::MEANDER_PLACER_BASE*>( aTool->Router()->Placer() );
|
|
|
|
|
|
|
|
if( !placer )
|
|
|
|
return;
|
|
|
|
|
|
|
|
aPopup->SetText( placer->TuningInfo( aFrame->GetUserUnits() ) );
|
|
|
|
|
|
|
|
// Determine the background color first and choose a contrasting value
|
|
|
|
COLOR4D bg( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
|
|
|
|
double h, s, l;
|
|
|
|
bg.ToHSL( h, s, l );
|
|
|
|
|
|
|
|
switch( placer->TuningStatus() )
|
|
|
|
{
|
|
|
|
case PNS::MEANDER_PLACER_BASE::TUNED:
|
|
|
|
if( l < 0.5 )
|
|
|
|
aPopup->SetTextColor( wxColor( 127, 200, 127 ) );
|
|
|
|
else
|
|
|
|
aPopup->SetTextColor( wxColor( 0, 92, 0 ) );
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PNS::MEANDER_PLACER_BASE::TOO_SHORT:
|
|
|
|
if( l < 0.5 )
|
|
|
|
aPopup->SetTextColor( wxColor( 242, 100, 126 ) );
|
|
|
|
else
|
|
|
|
aPopup->SetTextColor( wxColor( 122, 0, 0 ) );
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case PNS::MEANDER_PLACER_BASE::TOO_LONG:
|
|
|
|
if( l < 0.5 )
|
|
|
|
aPopup->SetTextColor( wxColor( 66, 184, 235 ) );
|
|
|
|
else
|
|
|
|
aPopup->SetTextColor( wxColor( 19, 19, 195 ) );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
protected:
|
2023-10-11 10:48:43 +00:00
|
|
|
VECTOR2I m_end;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 10:48:43 +00:00
|
|
|
int m_minAmplitude;
|
|
|
|
int m_maxAmplitude;
|
|
|
|
int m_spacing;
|
2023-10-11 13:09:53 +00:00
|
|
|
long long int m_targetLength;
|
2023-10-11 10:48:43 +00:00
|
|
|
int m_targetSkew;
|
|
|
|
bool m_overrideCustomRules;
|
|
|
|
int m_cornerRadiusPercentage;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 10:48:43 +00:00
|
|
|
PNS::MEANDER_SIDE m_initialSide;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
|
|
|
std::optional<SHAPE_LINE_CHAIN> m_baseLine;
|
2023-10-12 14:45:33 +00:00
|
|
|
std::optional<SHAPE_LINE_CHAIN> m_baseLineCoupled;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-10 10:08:53 +00:00
|
|
|
bool m_singleSide;
|
|
|
|
bool m_rounded;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-10 10:08:53 +00:00
|
|
|
LENGTH_TUNING_MODE m_tuningMode;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-10 10:08:53 +00:00
|
|
|
wxString m_lastNetName;
|
|
|
|
wxString m_tuningInfo;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-10 10:08:53 +00:00
|
|
|
PNS::MEANDER_PLACER_BASE::TUNING_STATUS m_tuningStatus;
|
2023-10-09 20:48:34 +00:00
|
|
|
|
|
|
|
// Temp storage during editing
|
|
|
|
std::set<BOARD_ITEM*> m_removedItems;
|
2023-10-07 03:54:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const wxString PCB_GENERATOR_MEANDERS::DISPLAY_NAME = _HKI( "Meanders" );
|
|
|
|
const wxString PCB_GENERATOR_MEANDERS::GENERATOR_TYPE = wxS( "meanders" );
|
|
|
|
|
|
|
|
|
2023-10-11 15:02:07 +00:00
|
|
|
using SCOPED_DRAW_MODE = SCOPED_SET_RESET<DRAWING_TOOL::MODE>;
|
|
|
|
|
|
|
|
|
2023-10-09 14:22:16 +00:00
|
|
|
#define HITTEST_THRESHOLD_PIXELS 5
|
|
|
|
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
int DRAWING_TOOL::PlaceMeander( const TOOL_EVENT& aEvent )
|
|
|
|
{
|
2023-10-11 13:43:54 +00:00
|
|
|
if( m_inDrawingTool )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
REENTRANCY_GUARD guard( &m_inDrawingTool );
|
2023-10-09 14:22:16 +00:00
|
|
|
|
|
|
|
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
m_frame->PushTool( aEvent );
|
2023-10-09 14:22:16 +00:00
|
|
|
Activate();
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 16:10:48 +00:00
|
|
|
LENGTH_TUNING_MODE mode = FromPNSMode( aEvent.Parameter<PNS::ROUTER_MODE>() );
|
2023-10-11 13:43:54 +00:00
|
|
|
KIGFX::VIEW_CONTROLS* controls = getViewControls();
|
|
|
|
BOARD* board = m_frame->GetBoard();
|
|
|
|
PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
|
|
|
|
GENERAL_COLLECTORS_GUIDE guide = m_frame->GetCollectorsGuide();
|
|
|
|
GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
|
2023-10-11 14:50:15 +00:00
|
|
|
PNS::ROUTER* router = generatorTool->Router();
|
2023-10-11 15:02:07 +00:00
|
|
|
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::MEANDER );
|
2023-10-11 13:43:54 +00:00
|
|
|
|
2023-10-09 14:22:16 +00:00
|
|
|
m_pickerItem = nullptr;
|
|
|
|
m_meander = nullptr;
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 13:09:53 +00:00
|
|
|
// Add a VIEW_GROUP that serves as a preview for the new item
|
|
|
|
m_preview.Clear();
|
|
|
|
m_view->Add( &m_preview );
|
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
auto setCursor =
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
|
|
|
|
controls->ShowCursor( true );
|
|
|
|
};
|
|
|
|
|
2023-10-11 14:50:15 +00:00
|
|
|
auto updateMeander =
|
|
|
|
[&]()
|
|
|
|
{
|
|
|
|
if( m_meander && m_meander->GetPosition() != m_meander->GetEnd() )
|
|
|
|
{
|
|
|
|
m_meander->EditStart( generatorTool, m_board, m_frame, nullptr );
|
|
|
|
m_meander->Update( generatorTool, m_board, m_frame, nullptr );
|
|
|
|
|
|
|
|
m_statusPopup->Popup();
|
|
|
|
canvas()->SetStatusPopup( m_statusPopup.get() );
|
|
|
|
|
|
|
|
m_view->Update( &m_preview );
|
|
|
|
|
|
|
|
m_meander->UpdateStatus( generatorTool, m_frame, m_statusPopup.get() );
|
|
|
|
m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, 20 ) );
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
// Set initial cursor
|
|
|
|
setCursor();
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
while( TOOL_EVENT* evt = Wait() )
|
|
|
|
{
|
|
|
|
setCursor();
|
|
|
|
VECTOR2D cursorPos = controls->GetMousePosition();
|
|
|
|
|
|
|
|
if( evt->IsCancelInteractive() || evt->IsActivate() )
|
|
|
|
{
|
|
|
|
if( m_meander )
|
2023-10-09 14:22:16 +00:00
|
|
|
{
|
2023-10-11 13:43:54 +00:00
|
|
|
// First click already made; clean up meander preview
|
|
|
|
m_meander->EditRevert( generatorTool, m_board, m_frame, nullptr );
|
2023-10-09 20:46:24 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
m_preview.Clear();
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
delete m_meander;
|
|
|
|
m_meander = nullptr;
|
|
|
|
}
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if( evt->IsMotion() )
|
|
|
|
{
|
|
|
|
if( !m_meander )
|
2023-10-09 14:22:16 +00:00
|
|
|
{
|
2023-10-11 13:43:54 +00:00
|
|
|
// First click not yet made; we're in highlight-net-under-cursor mode
|
2023-10-09 14:22:16 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
GENERAL_COLLECTOR collector;
|
2023-10-09 14:22:16 +00:00
|
|
|
collector.m_Threshold = KiROUND( getView()->ToWorld( HITTEST_THRESHOLD_PIXELS ) );
|
2023-10-11 13:43:54 +00:00
|
|
|
collector.Collect( board, { PCB_TRACE_T, PCB_ARC_T }, cursorPos, guide );
|
2023-10-09 14:22:16 +00:00
|
|
|
|
|
|
|
if( collector.GetCount() > 1 )
|
2023-10-11 13:43:54 +00:00
|
|
|
selectionTool->GuessSelectionCandidates( collector, cursorPos );
|
2023-10-09 14:22:16 +00:00
|
|
|
|
|
|
|
BOARD_ITEM* item = collector.GetCount() == 1 ? collector[ 0 ] : nullptr;
|
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
if( !m_pickerItem )
|
2023-10-09 14:22:16 +00:00
|
|
|
{
|
2023-10-11 13:43:54 +00:00
|
|
|
m_pickerItem = static_cast<BOARD_CONNECTED_ITEM*>( item );
|
2023-10-11 16:10:48 +00:00
|
|
|
generatorTool->HighlightNets( m_pickerItem );
|
2023-10-09 14:22:16 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-10-11 13:43:54 +00:00
|
|
|
m_pickerItem = static_cast<BOARD_CONNECTED_ITEM*>( item );
|
2023-10-11 16:10:48 +00:00
|
|
|
generatorTool->UpdateHighlightedNets( m_pickerItem );
|
2023-10-09 14:22:16 +00:00
|
|
|
}
|
2023-10-11 13:43:54 +00:00
|
|
|
}
|
|
|
|
else
|
2023-10-09 14:22:16 +00:00
|
|
|
{
|
2023-10-11 13:43:54 +00:00
|
|
|
// First click already made; we're in preview-meander mode
|
|
|
|
|
|
|
|
m_meander->SetEnd( cursorPos );
|
2023-10-11 14:50:15 +00:00
|
|
|
updateMeander();
|
2023-10-11 13:43:54 +00:00
|
|
|
}
|
|
|
|
}
|
2023-10-11 16:10:48 +00:00
|
|
|
else if( evt->IsClick( BUT_LEFT ) )
|
|
|
|
{
|
|
|
|
if( m_pickerItem && !m_meander )
|
|
|
|
{
|
|
|
|
// First click; create a meander
|
|
|
|
|
|
|
|
generatorTool->HighlightNets( nullptr );
|
|
|
|
|
|
|
|
m_frame->SetActiveLayer( m_pickerItem->GetLayer() );
|
|
|
|
m_meander = PCB_GENERATOR_MEANDERS::CreateNew( generatorTool, m_frame,
|
|
|
|
m_pickerItem, mode );
|
|
|
|
|
|
|
|
int dummyDist;
|
|
|
|
int dummyClearance = std::numeric_limits<int>::max() / 2;
|
|
|
|
VECTOR2I closestPt;
|
|
|
|
|
|
|
|
m_pickerItem->GetEffectiveShape()->Collide( cursorPos, dummyClearance,
|
|
|
|
&dummyDist, &closestPt );
|
|
|
|
m_meander->SetPosition( closestPt );
|
|
|
|
m_meander->SetEnd( closestPt );
|
|
|
|
|
|
|
|
m_preview.Add( m_meander );
|
|
|
|
}
|
|
|
|
else if( m_pickerItem && m_meander )
|
|
|
|
{
|
|
|
|
// Second click; we're done
|
|
|
|
BOARD_COMMIT commit( m_frame );
|
|
|
|
|
|
|
|
m_meander->EditStart( generatorTool, m_board, m_frame, &commit );
|
|
|
|
m_meander->Update( generatorTool, m_board, m_frame, &commit );
|
|
|
|
m_meander->EditPush( generatorTool, m_board, m_frame, &commit, _( "Tune" ) );
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2023-10-11 13:43:54 +00:00
|
|
|
else if( evt->IsClick( BUT_RIGHT ) )
|
|
|
|
{
|
|
|
|
PCB_SELECTION dummy;
|
|
|
|
m_menu.ShowContextMenu( dummy );
|
|
|
|
}
|
2023-10-11 14:50:15 +00:00
|
|
|
else if( evt->IsAction( &PCB_ACTIONS::spacingIncrease )
|
|
|
|
|| evt->IsAction( &PCB_ACTIONS::spacingDecrease ) )
|
|
|
|
{
|
|
|
|
if( m_meander )
|
|
|
|
{
|
|
|
|
auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
|
|
|
|
|
|
|
|
placer->SpacingStep( evt->IsAction( &PCB_ACTIONS::spacingIncrease ) ? 1 : -1 );
|
|
|
|
m_meander->SetSpacing( placer->MeanderSettings().m_spacing );
|
|
|
|
updateMeander();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( evt->IsAction( &PCB_ACTIONS::amplIncrease )
|
|
|
|
|| evt->IsAction( &PCB_ACTIONS::amplDecrease ) )
|
|
|
|
{
|
|
|
|
if( m_meander )
|
|
|
|
{
|
|
|
|
auto* placer = static_cast<PNS::MEANDER_PLACER_BASE*>( router->Placer() );
|
|
|
|
|
|
|
|
placer->AmplitudeStep( evt->IsAction( &PCB_ACTIONS::amplIncrease ) ? 1 : -1 );
|
|
|
|
m_meander->SetMaxAmplitude( placer->MeanderSettings().m_maxAmplitude );
|
|
|
|
updateMeander();
|
|
|
|
}
|
|
|
|
}
|
2023-10-11 13:43:54 +00:00
|
|
|
// TODO: It'd be nice to be able to say "don't allow any non-trivial editing actions",
|
|
|
|
// but we don't at present have that, so we just knock out some of the egregious ones.
|
|
|
|
else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
|
|
|
|
{
|
|
|
|
wxBell();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
evt->SetPassEvent();
|
|
|
|
}
|
2023-10-09 14:22:16 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
controls->CaptureCursor( m_meander != nullptr );
|
|
|
|
controls->SetAutoPan( m_meander != nullptr );
|
|
|
|
}
|
2023-10-09 20:46:24 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
controls->CaptureCursor( false );
|
|
|
|
controls->SetAutoPan( false );
|
|
|
|
controls->ForceCursorPosition( false );
|
|
|
|
controls->ShowCursor( false );
|
|
|
|
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
|
2023-10-09 20:55:07 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
canvas()->SetStatusPopup( nullptr );
|
|
|
|
m_statusPopup->Hide();
|
2023-10-09 20:46:24 +00:00
|
|
|
|
2023-10-11 16:10:48 +00:00
|
|
|
generatorTool->HighlightNets( nullptr );
|
2023-10-11 13:09:53 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
m_preview.Clear();
|
|
|
|
m_view->Remove( &m_preview );
|
2023-10-11 13:09:53 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
m_frame->GetCanvas()->Refresh();
|
2023-10-09 14:22:16 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
if( m_meander )
|
|
|
|
selectionTool->AddItemToSel( m_meander );
|
2023-10-07 03:54:14 +00:00
|
|
|
|
2023-10-11 13:43:54 +00:00
|
|
|
m_frame->PopTool( aEvent );
|
2023-10-07 03:54:14 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static struct PCB_GENERATOR_MEANDERS_DESC
|
|
|
|
{
|
|
|
|
PCB_GENERATOR_MEANDERS_DESC()
|
|
|
|
{
|
|
|
|
ENUM_MAP<LENGTH_TUNING_MODE>::Instance()
|
|
|
|
.Map( LENGTH_TUNING_MODE::SINGLE, _HKI( "Single track" ) )
|
|
|
|
/*.Map( LENGTH_TUNING_MODE::DIFF_PAIR, _HKI( "Diff. pair" ) )*/ // Not supported
|
|
|
|
.Map( LENGTH_TUNING_MODE::DIFF_PAIR_SKEW, _HKI( "Diff. pair Skew" ) );
|
|
|
|
|
|
|
|
ENUM_MAP<PNS::MEANDER_SIDE>::Instance()
|
|
|
|
.Map( PNS::MEANDER_SIDE_LEFT, _HKI( "Left" ) )
|
|
|
|
.Map( PNS::MEANDER_SIDE_RIGHT, _HKI( "Right" ) )
|
|
|
|
.Map( PNS::MEANDER_SIDE_DEFAULT, _HKI( "Default" ) );
|
|
|
|
|
|
|
|
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
|
|
|
|
REGISTER_TYPE( PCB_GENERATOR_MEANDERS );
|
|
|
|
propMgr.AddTypeCast( new TYPE_CAST<PCB_GENERATOR_MEANDERS, PCB_GENERATOR> );
|
|
|
|
propMgr.AddTypeCast( new TYPE_CAST<PCB_GENERATOR_MEANDERS, BOARD_ITEM> );
|
|
|
|
propMgr.InheritsAfter( TYPE_HASH( PCB_GENERATOR_MEANDERS ), TYPE_HASH( PCB_GENERATOR ) );
|
|
|
|
propMgr.InheritsAfter( TYPE_HASH( PCB_GENERATOR_MEANDERS ), TYPE_HASH( BOARD_ITEM ) );
|
|
|
|
|
|
|
|
const wxString groupTab = _HKI( "Meander Properties" );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, int>(
|
|
|
|
_HKI( "End X" ), &PCB_GENERATOR_MEANDERS::SetEndX,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetEndX, PROPERTY_DISPLAY::PT_SIZE,
|
|
|
|
ORIGIN_TRANSFORMS::ABS_X_COORD ),
|
|
|
|
groupTab );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, int>(
|
|
|
|
_HKI( "End Y" ), &PCB_GENERATOR_MEANDERS::SetEndY,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetEndY, PROPERTY_DISPLAY::PT_SIZE,
|
|
|
|
ORIGIN_TRANSFORMS::ABS_Y_COORD ),
|
|
|
|
groupTab );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY_ENUM<PCB_GENERATOR_MEANDERS, LENGTH_TUNING_MODE>(
|
|
|
|
_HKI( "Tuning mode" ),
|
|
|
|
&PCB_GENERATOR_MEANDERS::SetTuningMode,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetTuningMode ),
|
|
|
|
groupTab );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, int>(
|
|
|
|
_HKI( "Min amplitude" ),
|
|
|
|
&PCB_GENERATOR_MEANDERS::SetMinAmplitude,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetMinAmplitude,
|
|
|
|
PROPERTY_DISPLAY::PT_SIZE, ORIGIN_TRANSFORMS::ABS_X_COORD ),
|
|
|
|
groupTab );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, int>(
|
|
|
|
_HKI( "Max amplitude" ),
|
|
|
|
&PCB_GENERATOR_MEANDERS::SetMaxAmplitude,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetMaxAmplitude,
|
|
|
|
PROPERTY_DISPLAY::PT_SIZE, ORIGIN_TRANSFORMS::ABS_X_COORD ),
|
|
|
|
groupTab );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY_ENUM<PCB_GENERATOR_MEANDERS, PNS::MEANDER_SIDE>(
|
|
|
|
_HKI( "Initial side" ),
|
|
|
|
&PCB_GENERATOR_MEANDERS::SetInitialSide,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetInitialSide ),
|
|
|
|
groupTab );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, int>(
|
|
|
|
_HKI( "Min spacing" ), &PCB_GENERATOR_MEANDERS::SetSpacing,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetSpacing, PROPERTY_DISPLAY::PT_SIZE,
|
|
|
|
ORIGIN_TRANSFORMS::ABS_X_COORD ),
|
|
|
|
groupTab );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, int>(
|
|
|
|
_HKI( "Corner radius %" ),
|
|
|
|
&PCB_GENERATOR_MEANDERS::SetCornerRadiusPercentage,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetCornerRadiusPercentage,
|
|
|
|
PROPERTY_DISPLAY::PT_DEFAULT, ORIGIN_TRANSFORMS::NOT_A_COORD ),
|
|
|
|
groupTab );
|
|
|
|
|
2023-10-11 13:09:53 +00:00
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, long long int>(
|
2023-10-07 03:54:14 +00:00
|
|
|
_HKI( "Target length" ),
|
|
|
|
&PCB_GENERATOR_MEANDERS::SetTargetLength,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetTargetLength,
|
|
|
|
PROPERTY_DISPLAY::PT_SIZE, ORIGIN_TRANSFORMS::ABS_X_COORD ),
|
|
|
|
groupTab );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, int>(
|
|
|
|
_HKI( "Target skew" ), &PCB_GENERATOR_MEANDERS::SetTargetSkew,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetTargetSkew,
|
|
|
|
PROPERTY_DISPLAY::PT_SIZE, ORIGIN_TRANSFORMS::ABS_X_COORD ),
|
|
|
|
groupTab );
|
|
|
|
|
2023-10-11 10:48:43 +00:00
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, bool>(
|
|
|
|
_HKI( "Override custom rules" ),
|
|
|
|
&PCB_GENERATOR_MEANDERS::SetOverrideCustomRules,
|
|
|
|
&PCB_GENERATOR_MEANDERS::GetOverrideCustomRules ),
|
|
|
|
groupTab );
|
|
|
|
|
2023-10-07 03:54:14 +00:00
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, bool>(
|
|
|
|
_HKI( "Single-sided" ),
|
|
|
|
&PCB_GENERATOR_MEANDERS::SetSingleSided,
|
|
|
|
&PCB_GENERATOR_MEANDERS::IsSingleSided ),
|
|
|
|
groupTab );
|
|
|
|
|
|
|
|
propMgr.AddProperty( new PROPERTY<PCB_GENERATOR_MEANDERS, bool>(
|
|
|
|
_HKI( "Rounded" ), &PCB_GENERATOR_MEANDERS::SetRounded,
|
|
|
|
&PCB_GENERATOR_MEANDERS::IsRounded ),
|
|
|
|
groupTab );
|
|
|
|
}
|
|
|
|
} _PCB_GENERATOR_MEANDERS_DESC;
|
|
|
|
|
|
|
|
ENUM_TO_WXANY( LENGTH_TUNING_MODE )
|
|
|
|
ENUM_TO_WXANY( PNS::MEANDER_SIDE )
|
|
|
|
|
|
|
|
static GENERATORS_MGR::REGISTER<PCB_GENERATOR_MEANDERS> registerMe;
|