2021-11-02 10:18:58 +01:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2021 Jean-Pierre Charras, jp.charras at wanadoo.fr
|
2025-01-01 13:30:11 -08:00
|
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
2021-11-02 10:18:58 +01:00
|
|
|
*
|
|
|
|
* 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 <confirm.h>
|
|
|
|
|
|
|
|
#include <board_design_settings.h>
|
|
|
|
#include <pcb_track.h>
|
|
|
|
#include <pad.h>
|
|
|
|
#include <zone_filler.h>
|
|
|
|
#include <board_commit.h>
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
#include <connectivity/connectivity_data.h>
|
|
|
|
#include <teardrop/teardrop.h>
|
|
|
|
#include <drc/drc_rtree.h>
|
2021-11-02 10:18:58 +01:00
|
|
|
#include <geometry/shape_line_chain.h>
|
2023-05-12 22:03:54 +01:00
|
|
|
#include <geometry/rtree.h>
|
2021-11-02 10:18:58 +01:00
|
|
|
#include <convert_basic_shapes_to_polygon.h>
|
|
|
|
#include <bezier_curves.h>
|
|
|
|
|
|
|
|
#include <wx/log.h>
|
|
|
|
|
2021-12-19 10:30:31 +01:00
|
|
|
// The first priority level of a teardrop area (arbitrary value)
|
|
|
|
#define MAGIC_TEARDROP_ZONE_ID 30000
|
|
|
|
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
TEARDROP_MANAGER::TEARDROP_MANAGER( BOARD* aBoard, TOOL_MANAGER* aToolManager ) :
|
|
|
|
m_board( aBoard ),
|
|
|
|
m_toolManager( aToolManager )
|
2021-11-02 10:18:58 +01:00
|
|
|
{
|
2023-05-11 14:53:28 +01:00
|
|
|
m_prmsList = m_board->GetDesignSettings().GetTeadropParamsList();
|
2022-01-23 12:57:06 +01:00
|
|
|
m_tolerance = 0;
|
2021-11-02 10:18:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZONE* TEARDROP_MANAGER::createTeardrop( TEARDROP_VARIANT aTeardropVariant,
|
2023-05-11 15:46:58 +01:00
|
|
|
std::vector<VECTOR2I>& aPoints, PCB_TRACK* aTrack ) const
|
2021-11-02 10:18:58 +01:00
|
|
|
{
|
|
|
|
ZONE* teardrop = new ZONE( m_board );
|
|
|
|
|
2022-08-07 08:52:40 +02:00
|
|
|
// teardrop settings are the last zone settings used by a zone dialog.
|
|
|
|
// override them by default.
|
2023-06-10 12:03:18 +02:00
|
|
|
ZONE_SETTINGS::GetDefaultSettings().ExportSetting( *teardrop, false );
|
2022-08-07 08:52:40 +02:00
|
|
|
|
2021-11-02 10:18:58 +01:00
|
|
|
// Add zone properties (priority will be fixed later)
|
2023-05-12 22:03:54 +01:00
|
|
|
teardrop->SetTeardropAreaType( aTeardropVariant == TD_TYPE_PADVIA ? TEARDROP_TYPE::TD_VIAPAD
|
|
|
|
: TEARDROP_TYPE::TD_TRACKEND );
|
2021-11-02 10:18:58 +01:00
|
|
|
teardrop->SetLayer( aTrack->GetLayer() );
|
|
|
|
teardrop->SetNetCode( aTrack->GetNetCode() );
|
|
|
|
teardrop->SetLocalClearance( 0 );
|
2022-09-16 07:33:56 -04:00
|
|
|
teardrop->SetMinThickness( pcbIUScale.mmToIU( 0.0254 ) ); // The minimum zone thickness
|
2021-11-02 10:18:58 +01:00
|
|
|
teardrop->SetPadConnection( ZONE_CONNECTION::FULL );
|
|
|
|
teardrop->SetIsFilled( false );
|
|
|
|
teardrop->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
|
2025-04-25 09:36:17 +01:00
|
|
|
teardrop->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER, 0, false );
|
2023-05-12 22:03:54 +01:00
|
|
|
|
2021-11-02 10:18:58 +01:00
|
|
|
SHAPE_POLY_SET* outline = teardrop->Outline();
|
|
|
|
outline->NewOutline();
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
for( const VECTOR2I& pt: aPoints )
|
|
|
|
outline->Append( pt.x, pt.y );
|
|
|
|
|
|
|
|
// Until we know better (ie: pay for a potentially very expensive zone refill), the teardrop
|
|
|
|
// fill is the same as its outline.
|
|
|
|
teardrop->SetFilledPolysList( aTrack->GetLayer(), *teardrop->Outline() );
|
|
|
|
teardrop->SetIsFilled( true );
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2022-08-07 08:52:40 +02:00
|
|
|
// Used in priority calculations:
|
2021-11-02 10:18:58 +01:00
|
|
|
teardrop->CalculateFilledArea();
|
|
|
|
|
|
|
|
return teardrop;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-04-25 09:36:17 +01:00
|
|
|
ZONE* TEARDROP_MANAGER::createTeardropMask( TEARDROP_VARIANT aTeardropVariant,
|
|
|
|
std::vector<VECTOR2I>& aPoints, PCB_TRACK* aTrack ) const
|
|
|
|
{
|
|
|
|
ZONE* teardrop = new ZONE( m_board );
|
|
|
|
|
|
|
|
teardrop->SetTeardropAreaType( aTeardropVariant == TD_TYPE_PADVIA ? TEARDROP_TYPE::TD_VIAPAD
|
|
|
|
: TEARDROP_TYPE::TD_TRACKEND );
|
|
|
|
teardrop->SetLayer( aTrack->GetLayer() == F_Cu ? F_Mask : B_Mask );
|
|
|
|
teardrop->SetMinThickness( pcbIUScale.mmToIU( 0.0254 ) ); // The minimum zone thickness
|
|
|
|
teardrop->SetIsFilled( false );
|
|
|
|
teardrop->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
|
|
|
|
teardrop->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::INVISIBLE_BORDER, 0, false );
|
|
|
|
|
|
|
|
SHAPE_POLY_SET* outline = teardrop->Outline();
|
|
|
|
outline->NewOutline();
|
|
|
|
|
|
|
|
for( const VECTOR2I& pt: aPoints )
|
|
|
|
outline->Append( pt.x, pt.y );
|
|
|
|
|
|
|
|
if( int expansion = aTrack->GetSolderMaskExpansion() )
|
|
|
|
{
|
|
|
|
// The zone-min-thickness deflate/reinflate is going to round corners, so it's more
|
|
|
|
// efficient to allow acute corners on the solder mask expansion here, and delegate the
|
|
|
|
// rounding to the deflate/reinflate.
|
|
|
|
teardrop->SetMinThickness( std::max( teardrop->GetMinThickness(), expansion ) );
|
|
|
|
|
|
|
|
outline->Inflate( expansion, CORNER_STRATEGY::ALLOW_ACUTE_CORNERS,
|
|
|
|
m_board->GetDesignSettings().m_MaxError );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Until we know better (ie: pay for a potentially very expensive zone refill), the teardrop
|
|
|
|
// fill is the same as its outline.
|
|
|
|
teardrop->SetFilledPolysList( teardrop->GetLayer(), *teardrop->Outline() );
|
|
|
|
teardrop->SetIsFilled( true );
|
|
|
|
|
|
|
|
return teardrop;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-08-26 10:02:15 -07:00
|
|
|
void TEARDROP_MANAGER::createAndAddTeardropWithMask( BOARD_COMMIT& aCommit,
|
|
|
|
TEARDROP_VARIANT aTeardropVariant,
|
|
|
|
std::vector<VECTOR2I>& aPoints,
|
|
|
|
PCB_TRACK* aTrack )
|
|
|
|
{
|
|
|
|
ZONE* new_teardrop = createTeardrop( aTeardropVariant, aPoints, aTrack );
|
|
|
|
m_board->Add( new_teardrop, ADD_MODE::BULK_INSERT );
|
|
|
|
m_createdTdList.push_back( new_teardrop );
|
|
|
|
|
|
|
|
aCommit.Added( new_teardrop );
|
|
|
|
|
|
|
|
if( aTrack->HasSolderMask() && IsExternalCopperLayer( aTrack->GetLayer() ) )
|
|
|
|
{
|
|
|
|
ZONE* new_teardrop_mask = createTeardropMask( aTeardropVariant, aPoints, aTrack );
|
|
|
|
m_board->Add( new_teardrop_mask, ADD_MODE::BULK_INSERT );
|
|
|
|
aCommit.Added( new_teardrop_mask );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool TEARDROP_MANAGER::tryCreateTrackTeardrop( BOARD_COMMIT& aCommit,
|
|
|
|
const TEARDROP_PARAMETERS& aParams,
|
|
|
|
TEARDROP_MANAGER::TEARDROP_VARIANT aTeardropVariant,
|
|
|
|
PCB_TRACK* aTrack, BOARD_ITEM* aCandidate,
|
|
|
|
const VECTOR2I& aPos )
|
|
|
|
{
|
|
|
|
std::vector<VECTOR2I> points;
|
|
|
|
|
|
|
|
if( computeTeardropPolygon( aParams, points, aTrack, aCandidate, aPos ) )
|
|
|
|
{
|
|
|
|
createAndAddTeardropWithMask( aCommit, aTeardropVariant, points, aTrack );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-08-15 10:46:31 +01:00
|
|
|
void TEARDROP_MANAGER::RemoveTeardrops( BOARD_COMMIT& aCommit,
|
2023-05-12 22:03:54 +01:00
|
|
|
const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
|
2023-08-15 10:46:31 +01:00
|
|
|
const std::set<PCB_TRACK*>* dirtyTracks )
|
2021-11-02 10:18:58 +01:00
|
|
|
{
|
2023-05-12 22:03:54 +01:00
|
|
|
std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2025-02-12 11:32:16 +00:00
|
|
|
auto isStale =
|
|
|
|
[&]( ZONE* zone )
|
|
|
|
{
|
|
|
|
std::vector<PAD*> connectedPads;
|
|
|
|
std::vector<PCB_VIA*> connectedVias;
|
2022-01-16 15:00:40 +01:00
|
|
|
|
2025-02-12 11:32:16 +00:00
|
|
|
connectivity->GetConnectedPadsAndVias( zone, &connectedPads, &connectedVias );
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2025-02-12 11:32:16 +00:00
|
|
|
for( PAD* pad : connectedPads )
|
2023-08-15 10:46:31 +01:00
|
|
|
{
|
2025-02-12 11:32:16 +00:00
|
|
|
if( alg::contains( *dirtyPadsAndVias, pad ) )
|
|
|
|
return true;
|
2023-08-15 10:46:31 +01:00
|
|
|
}
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
for( PCB_VIA* via : connectedVias )
|
2023-08-15 10:46:31 +01:00
|
|
|
{
|
|
|
|
if( alg::contains( *dirtyPadsAndVias, via ) )
|
2025-02-12 11:32:16 +00:00
|
|
|
return true;
|
2023-08-15 10:46:31 +01:00
|
|
|
}
|
2023-05-12 22:03:54 +01:00
|
|
|
|
2025-02-12 11:32:16 +00:00
|
|
|
for( PCB_TRACK* track : connectivity->GetConnectedTracks( zone ) )
|
|
|
|
{
|
|
|
|
if( alg::contains( *dirtyTracks, track ) )
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
for( ZONE* zone : m_board->Zones() )
|
|
|
|
{
|
|
|
|
if( zone->IsTeardropArea() && isStale( zone ) )
|
2025-02-12 12:13:52 +00:00
|
|
|
zone->SetFlags( STRUCT_DELETED );
|
2021-11-02 10:18:58 +01:00
|
|
|
}
|
|
|
|
|
2025-02-12 12:13:52 +00:00
|
|
|
m_board->BulkRemoveStaleTeardrops( aCommit );
|
2023-08-15 10:46:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TEARDROP_MANAGER::UpdateTeardrops( BOARD_COMMIT& aCommit,
|
|
|
|
const std::vector<BOARD_ITEM*>* dirtyPadsAndVias,
|
|
|
|
const std::set<PCB_TRACK*>* dirtyTracks,
|
|
|
|
bool aForceFullUpdate )
|
|
|
|
{
|
|
|
|
if( m_board->LegacyTeardrops() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Init parameters:
|
|
|
|
m_tolerance = pcbIUScale.mmToIU( 0.01 );
|
|
|
|
|
2025-08-26 10:02:15 -07:00
|
|
|
BuildTrackCaches();
|
2023-08-15 10:46:31 +01:00
|
|
|
|
|
|
|
// Old teardrops must be removed, to ensure a clean teardrop rebuild
|
|
|
|
if( aForceFullUpdate )
|
|
|
|
{
|
|
|
|
for( ZONE* zone : m_board->Zones() )
|
|
|
|
{
|
|
|
|
if( zone->IsTeardropArea() )
|
2025-02-12 12:13:52 +00:00
|
|
|
zone->SetFlags( STRUCT_DELETED );
|
2023-08-15 10:46:31 +01:00
|
|
|
}
|
|
|
|
|
2025-02-12 12:13:52 +00:00
|
|
|
m_board->BulkRemoveStaleTeardrops( aCommit );
|
2023-08-15 10:46:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
|
2021-11-02 10:18:58 +01:00
|
|
|
|
|
|
|
for( PCB_TRACK* track : m_board->Tracks() )
|
|
|
|
{
|
2023-05-12 22:03:54 +01:00
|
|
|
if( ! ( track->Type() == PCB_TRACE_T || track->Type() == PCB_ARC_T ) )
|
2021-11-02 10:18:58 +01:00
|
|
|
continue;
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
std::vector<PAD*> connectedPads;
|
|
|
|
std::vector<PCB_VIA*> connectedVias;
|
|
|
|
|
|
|
|
connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
|
|
|
|
|
2024-07-03 21:24:49 +03:00
|
|
|
bool forceUpdate = aForceFullUpdate || dirtyTracks->contains( track );
|
2023-05-12 22:03:54 +01:00
|
|
|
|
|
|
|
for( PAD* pad : connectedPads )
|
2021-11-02 10:18:58 +01:00
|
|
|
{
|
2023-05-12 22:03:54 +01:00
|
|
|
if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, pad ) )
|
2022-01-16 10:24:23 +01:00
|
|
|
continue;
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
TEARDROP_PARAMETERS& tdParams = pad->GetTeardropParams();
|
2024-09-15 13:26:01 -04:00
|
|
|
VECTOR2I padSize = pad->GetSize( track->GetLayer() );
|
|
|
|
int annularWidth = std::min( padSize.x, padSize.y );
|
2022-01-17 16:28:28 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
if( !tdParams.m_Enabled )
|
|
|
|
continue;
|
2022-01-16 10:24:23 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
// Ensure a teardrop shape can be built: track width must be < teardrop width and
|
|
|
|
// filter width
|
|
|
|
if( track->GetWidth() >= tdParams.m_TdMaxWidth
|
|
|
|
|| track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
|
|
|
|
|| track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
|
2023-05-11 15:46:58 +01:00
|
|
|
{
|
2021-11-02 10:18:58 +01:00
|
|
|
continue;
|
2023-05-11 15:46:58 +01:00
|
|
|
}
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2025-08-27 11:03:00 +02:00
|
|
|
bool startHitsPad = pad->HitTest( track->GetStart(), 0, track->GetLayer() );
|
|
|
|
bool endHitsPad = pad->HitTest( track->GetEnd(), 0, track->GetLayer() );
|
2025-08-26 10:02:15 -07:00
|
|
|
|
|
|
|
// The track is entirely inside the pad; cannot create a teardrop
|
|
|
|
if( startHitsPad && endHitsPad )
|
2022-01-30 15:32:38 +01:00
|
|
|
continue;
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
// Skip case where pad and the track are within a copper zone with the same net
|
|
|
|
// (and the pad can be connected to the zone)
|
|
|
|
if( !tdParams.m_TdOnPadsInZones && areItemsInSameZone( pad, track ) )
|
2021-11-02 10:18:58 +01:00
|
|
|
continue;
|
|
|
|
|
2025-08-26 10:02:15 -07:00
|
|
|
tryCreateTrackTeardrop( aCommit, tdParams, TEARDROP_MANAGER::TD_TYPE_PADVIA, track, pad, pad->GetPosition() );
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2025-08-26 10:02:15 -07:00
|
|
|
if( !startHitsPad && !endHitsPad )
|
2021-11-02 10:18:58 +01:00
|
|
|
{
|
2025-08-26 10:02:15 -07:00
|
|
|
PCB_TRACK reversed( *track );
|
|
|
|
reversed.SetStart( track->GetEnd() );
|
|
|
|
reversed.SetEnd( pad->GetPosition() );
|
|
|
|
tryCreateTrackTeardrop( aCommit, tdParams, TEARDROP_MANAGER::TD_TYPE_PADVIA, &reversed, pad, pad->GetPosition() );
|
|
|
|
reversed.SetStart( track->GetStart() );
|
|
|
|
tryCreateTrackTeardrop( aCommit, tdParams, TEARDROP_MANAGER::TD_TYPE_PADVIA, &reversed, pad, pad->GetPosition() );
|
2021-11-02 10:18:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
for( PCB_VIA* via : connectedVias )
|
|
|
|
{
|
|
|
|
if( !forceUpdate && !alg::contains( *dirtyPadsAndVias, via ) )
|
|
|
|
continue;
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
TEARDROP_PARAMETERS tdParams = via->GetTeardropParams();
|
2024-10-06 16:34:15 -04:00
|
|
|
int annularWidth = via->GetWidth( track->GetLayer() );
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
if( !tdParams.m_Enabled )
|
|
|
|
continue;
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
// Ensure a teardrop shape can be built: track width must be < teardrop width and
|
|
|
|
// filter width
|
|
|
|
if( track->GetWidth() >= tdParams.m_TdMaxWidth
|
|
|
|
|| track->GetWidth() >= annularWidth * tdParams.m_BestWidthRatio
|
|
|
|
|| track->GetWidth() >= annularWidth * tdParams.m_WidthtoSizeFilterRatio )
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2023-05-11 15:46:58 +01:00
|
|
|
|
2025-08-26 10:02:15 -07:00
|
|
|
bool startHitsVia = via->HitTest( track->GetStart() );
|
|
|
|
bool endHitsVia = via->HitTest( track->GetEnd() );
|
|
|
|
|
|
|
|
// The track is entirely inside the via; cannot create a teardrop
|
|
|
|
if( startHitsVia && endHitsVia )
|
2023-05-12 22:03:54 +01:00
|
|
|
continue;
|
2023-05-12 10:06:20 +02:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
|
2025-08-26 10:02:15 -07:00
|
|
|
tryCreateTrackTeardrop( aCommit, tdParams, TEARDROP_MANAGER::TD_TYPE_PADVIA, track, via,
|
|
|
|
via->GetPosition() );
|
2023-05-12 10:06:20 +02:00
|
|
|
|
2025-08-26 10:02:15 -07:00
|
|
|
if( !startHitsVia && !endHitsVia )
|
|
|
|
{
|
|
|
|
PCB_TRACK reversed( *track );
|
|
|
|
reversed.SetStart( track->GetEnd() );
|
|
|
|
reversed.SetEnd( via->GetPosition() );
|
|
|
|
tryCreateTrackTeardrop( aCommit, tdParams, TEARDROP_MANAGER::TD_TYPE_PADVIA, &reversed, via, via->GetPosition() );
|
|
|
|
reversed.SetStart( track->GetStart() );
|
|
|
|
tryCreateTrackTeardrop( aCommit, tdParams, TEARDROP_MANAGER::TD_TYPE_PADVIA, &reversed, via, via->GetPosition() );
|
2023-05-12 22:03:54 +01:00
|
|
|
}
|
2023-05-11 15:46:58 +01:00
|
|
|
}
|
2023-05-12 22:03:54 +01:00
|
|
|
}
|
2023-05-11 15:46:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
if( ( aForceFullUpdate || !dirtyTracks->empty() )
|
|
|
|
&& m_prmsList->GetParameters( TARGET_TRACK )->m_Enabled )
|
|
|
|
{
|
|
|
|
AddTeardropsOnTracks( aCommit, dirtyTracks, aForceFullUpdate );
|
|
|
|
}
|
2023-05-11 15:46:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
// Now set priority of teardrops now all teardrops are added
|
|
|
|
setTeardropPriorities();
|
|
|
|
}
|
2023-05-11 15:46:58 +01:00
|
|
|
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
void TEARDROP_MANAGER::DeleteTrackToTrackTeardrops( BOARD_COMMIT& aCommit )
|
|
|
|
{
|
|
|
|
for( ZONE* zone : m_board->Zones() )
|
|
|
|
{
|
|
|
|
if( zone->IsTeardropArea() && zone->GetTeardropAreaType() == TEARDROP_TYPE::TD_TRACKEND )
|
2025-02-12 12:13:52 +00:00
|
|
|
zone->SetFlags( STRUCT_DELETED );
|
2021-11-02 10:18:58 +01:00
|
|
|
}
|
|
|
|
|
2025-02-12 12:13:52 +00:00
|
|
|
m_board->BulkRemoveStaleTeardrops( aCommit );
|
2021-11-02 10:18:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void TEARDROP_MANAGER::setTeardropPriorities()
|
|
|
|
{
|
2022-03-11 11:02:45 +01:00
|
|
|
// Note: a teardrop area is on only one layer, so using GetFirstLayer() is OK
|
|
|
|
// to know the zone layer of a teardrop
|
|
|
|
|
2021-11-02 10:18:58 +01:00
|
|
|
int priority_base = MAGIC_TEARDROP_ZONE_ID;
|
|
|
|
|
|
|
|
// The sort function to sort by increasing copper layers. Group by layers.
|
|
|
|
// For same layers sort by decreasing areas
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
bool operator()(ZONE* a, ZONE* b) const
|
|
|
|
{
|
2022-03-11 11:02:45 +01:00
|
|
|
if( a->GetFirstLayer() == b->GetFirstLayer() )
|
2021-11-02 10:18:58 +01:00
|
|
|
return a->GetOutlineArea() > b->GetOutlineArea();
|
|
|
|
|
2022-03-11 11:02:45 +01:00
|
|
|
return a->GetFirstLayer() < b->GetFirstLayer();
|
2021-11-02 10:18:58 +01:00
|
|
|
}
|
|
|
|
} compareLess;
|
|
|
|
|
|
|
|
for( ZONE* td: m_createdTdList )
|
|
|
|
td->CalculateOutlineArea();
|
|
|
|
|
|
|
|
std::sort( m_createdTdList.begin(), m_createdTdList.end(), compareLess );
|
|
|
|
|
|
|
|
int curr_layer = -1;
|
|
|
|
|
|
|
|
for( ZONE* td: m_createdTdList )
|
|
|
|
{
|
2022-03-11 11:02:45 +01:00
|
|
|
if( td->GetFirstLayer() != curr_layer )
|
2021-11-02 10:18:58 +01:00
|
|
|
{
|
2022-03-11 11:02:45 +01:00
|
|
|
curr_layer = td->GetFirstLayer();
|
2021-11-02 10:18:58 +01:00
|
|
|
priority_base = MAGIC_TEARDROP_ZONE_ID;
|
|
|
|
}
|
|
|
|
|
2022-03-01 14:53:35 +00:00
|
|
|
td->SetAssignedPriority( priority_base++ );
|
2021-11-02 10:18:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
void TEARDROP_MANAGER::AddTeardropsOnTracks( BOARD_COMMIT& aCommit,
|
|
|
|
const std::set<PCB_TRACK*>* aTracks,
|
|
|
|
bool aForceFullUpdate )
|
2021-11-02 10:18:58 +01:00
|
|
|
{
|
2023-05-12 22:03:54 +01:00
|
|
|
std::shared_ptr<CONNECTIVITY_DATA> connectivity = m_board->GetConnectivity();
|
|
|
|
TEARDROP_PARAMETERS params = *m_prmsList->GetParameters( TARGET_TRACK );
|
2021-11-02 10:18:58 +01:00
|
|
|
|
|
|
|
// Explore groups (a group is a set of tracks on the same layer and the same net):
|
2023-05-12 22:03:54 +01:00
|
|
|
for( auto& grp : m_trackLookupList.GetBuffer() )
|
2021-11-02 10:18:58 +01:00
|
|
|
{
|
|
|
|
int layer, netcode;
|
|
|
|
TRACK_BUFFER::GetNetcodeAndLayerFromIndex( grp.first, &layer, &netcode );
|
|
|
|
|
|
|
|
std::vector<PCB_TRACK*>* sublist = grp.second;
|
|
|
|
|
|
|
|
if( sublist->size() <= 1 ) // We need at least 2 track segments
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// The sort function to sort by increasing track widths
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
bool operator()(PCB_TRACK* a, PCB_TRACK* b) const
|
|
|
|
{ return a->GetWidth() < b->GetWidth(); }
|
|
|
|
} compareLess;
|
|
|
|
|
|
|
|
std::sort( sublist->begin(), sublist->end(), compareLess );
|
|
|
|
int min_width = sublist->front()->GetWidth();
|
|
|
|
int max_width = sublist->back()->GetWidth();
|
|
|
|
|
|
|
|
// Skip groups having the same track thickness
|
|
|
|
if( max_width == min_width )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for( unsigned ii = 0; ii < sublist->size()-1; ii++ )
|
|
|
|
{
|
|
|
|
PCB_TRACK* track = (*sublist)[ii];
|
2023-05-12 22:03:54 +01:00
|
|
|
int track_len = (int) track->GetLength();
|
|
|
|
bool track_needs_update = aForceFullUpdate || alg::contains( *aTracks, track );
|
2021-11-02 10:18:58 +01:00
|
|
|
min_width = track->GetWidth();
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
// to avoid creating a teardrop between 2 tracks having similar widths give a threshold
|
|
|
|
params.m_WidthtoSizeFilterRatio = std::max( params.m_WidthtoSizeFilterRatio, 0.1 );
|
|
|
|
const double th = 1.0 / params.m_WidthtoSizeFilterRatio;
|
|
|
|
min_width = KiROUND( min_width * th );
|
2021-11-02 10:18:58 +01:00
|
|
|
|
|
|
|
for( unsigned jj = ii+1; jj < sublist->size(); jj++ )
|
|
|
|
{
|
Fix typos in pcbnew sub-directory
Found via `codespell -q 3 -S *.po,./thirdparty,./Documentation/changelogs -L aactual,acount,aline,alocation,alog,anormal,anumber,aother,apoints,aparent,aray,ba,busses,dout,einstance,leaded,modul,ontext,ot,overide,serie,te,,tesselate,tesselator,tht`
2022-06-29 16:21:10 -04:00
|
|
|
// Search candidates with thickness > curr thickness
|
2021-11-02 10:18:58 +01:00
|
|
|
PCB_TRACK* candidate = (*sublist)[jj];
|
|
|
|
|
|
|
|
if( min_width >= candidate->GetWidth() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Cannot build a teardrop on a too short track segment.
|
|
|
|
// The min len is > candidate radius
|
|
|
|
if( track_len <= candidate->GetWidth() /2 )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Now test end to end connection:
|
|
|
|
EDA_ITEM_FLAGS match_points; // to return the end point EDA_ITEM_FLAGS:
|
|
|
|
// 0, STARTPOINT, ENDPOINT
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
VECTOR2I pos = candidate->GetStart();
|
|
|
|
match_points = track->IsPointOnEnds( pos, m_tolerance );
|
2021-11-02 10:18:58 +01:00
|
|
|
|
|
|
|
if( !match_points )
|
|
|
|
{
|
2023-05-12 22:03:54 +01:00
|
|
|
pos = candidate->GetEnd();
|
|
|
|
match_points = track->IsPointOnEnds( pos, m_tolerance );
|
2021-11-02 10:18:58 +01:00
|
|
|
}
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
if( !match_points )
|
|
|
|
continue;
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
if( !track_needs_update && alg::contains( *aTracks, candidate ) )
|
|
|
|
continue;
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
// Pads/vias have priority for teardrops; ensure there isn't one at our position
|
|
|
|
bool existingPadOrVia = false;
|
|
|
|
std::vector<PAD*> connectedPads;
|
|
|
|
std::vector<PCB_VIA*> connectedVias;
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
connectivity->GetConnectedPadsAndVias( track, &connectedPads, &connectedVias );
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
for( PAD* pad : connectedPads )
|
|
|
|
{
|
|
|
|
if( pad->HitTest( pos ) )
|
|
|
|
existingPadOrVia = true;
|
|
|
|
}
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
for( PCB_VIA* via : connectedVias )
|
|
|
|
{
|
|
|
|
if( via->HitTest( pos ) )
|
|
|
|
existingPadOrVia = true;
|
|
|
|
}
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
if( existingPadOrVia )
|
|
|
|
continue;
|
2021-11-02 10:18:58 +01:00
|
|
|
|
2025-08-26 10:02:15 -07:00
|
|
|
tryCreateTrackTeardrop( aCommit, params, TEARDROP_MANAGER::TD_TYPE_TRACKEND, track, candidate, pos );
|
2023-05-12 22:03:54 +01:00
|
|
|
}
|
|
|
|
}
|
2021-11-02 10:18:58 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-12 22:03:54 +01:00
|
|
|
|