ADDED: Backdrill support
- Allow setting backdrill (from B.Cu up) and tertiary drill (from F.Cu down) width and target layer (backdrill is inclusive) - Allow setting post machining options (counterbore, countersink) - Update properties for vias and THT pads - Add output for Excellon and Gerber drill files as separated files for different drill depths - Add drill map output calling out post-machined holes - Add STEP export support - Add 3d-viewer support - Add 2581 support using backdrill property. Post machining called out in comments - Add ODB++ support using separate drill file for each depth - Add DRC check for tracks connected to pad layers that are backdrilled or post-machined Fixes https://gitlab.com/kicad/code/kicad/-/issues/18836
This commit is contained in:
@@ -305,6 +305,96 @@ public:
|
||||
return m_viaTH_ODPolys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backdrill and tertiary drill polygons.
|
||||
*
|
||||
*/
|
||||
const SHAPE_POLY_SET& GetBackdrillPolys() const noexcept
|
||||
{
|
||||
return m_BackdrillPolys;
|
||||
}
|
||||
|
||||
const SHAPE_POLY_SET& GetTertiarydrillPolys() const noexcept
|
||||
{
|
||||
return m_TertiarydrillPolys;
|
||||
}
|
||||
|
||||
const BVH_CONTAINER_2D& GetBackdrillCutouts() const noexcept
|
||||
{
|
||||
return m_backdrillCutouts;
|
||||
}
|
||||
|
||||
const BVH_CONTAINER_2D& GetTertiarydrillCutouts() const noexcept
|
||||
{
|
||||
return m_tertiarydrillCutouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the container of counterbore cutout geometry for the front (top) side.
|
||||
* These are circles representing the counterbore diameter for cutting into the board.
|
||||
*/
|
||||
const BVH_CONTAINER_2D& GetFrontCounterboreCutouts() const noexcept
|
||||
{
|
||||
return m_frontCounterboreCutouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the container of counterbore cutout geometry for the back (bottom) side.
|
||||
*/
|
||||
const BVH_CONTAINER_2D& GetBackCounterboreCutouts() const noexcept
|
||||
{
|
||||
return m_backCounterboreCutouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the container of countersink cutout geometry for the front (top) side.
|
||||
* These are circles representing the countersink outer diameter for cutting into the board.
|
||||
*/
|
||||
const BVH_CONTAINER_2D& GetFrontCountersinkCutouts() const noexcept
|
||||
{
|
||||
return m_frontCountersinkCutouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the container of countersink cutout geometry for the back (bottom) side.
|
||||
*/
|
||||
const BVH_CONTAINER_2D& GetBackCountersinkCutouts() const noexcept
|
||||
{
|
||||
return m_backCountersinkCutouts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the polygon set of counterbore outer diameters for the front (top) side.
|
||||
*/
|
||||
const SHAPE_POLY_SET& GetFrontCounterborePolys() const noexcept
|
||||
{
|
||||
return m_frontCounterborePolys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the polygon set of counterbore outer diameters for the back (bottom) side.
|
||||
*/
|
||||
const SHAPE_POLY_SET& GetBackCounterborePolys() const noexcept
|
||||
{
|
||||
return m_backCounterborePolys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the polygon set of countersink outer diameters for the front (top) side.
|
||||
*/
|
||||
const SHAPE_POLY_SET& GetFrontCountersinkPolys() const noexcept
|
||||
{
|
||||
return m_frontCountersinkPolys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the polygon set of countersink outer diameters for the back (bottom) side.
|
||||
*/
|
||||
const SHAPE_POLY_SET& GetBackCountersinkPolys() const noexcept
|
||||
{
|
||||
return m_backCountersinkPolys;
|
||||
}
|
||||
|
||||
unsigned int GetViaCount() const noexcept { return m_viaCount; }
|
||||
unsigned int GetHoleCount() const noexcept { return m_holeCount; }
|
||||
|
||||
@@ -472,11 +562,19 @@ private:
|
||||
MAP_POLY m_layerHoleOdPolys; ///< Hole outer diameters (per layer)
|
||||
MAP_POLY m_layerHoleIdPolys; ///< Hole inner diameters (per layer)
|
||||
|
||||
SHAPE_POLY_SET m_BackdrillPolys; ///< Board backdrill polygons B.Cu->in
|
||||
SHAPE_POLY_SET m_TertiarydrillPolys; ///< Board tertiary drill polygons F.Cu->in
|
||||
|
||||
SHAPE_POLY_SET m_NPTH_ODPolys; ///< NPTH outer diameters
|
||||
SHAPE_POLY_SET m_TH_ODPolys; ///< PTH outer diameters
|
||||
SHAPE_POLY_SET m_viaTH_ODPolys; ///< Via hole outer diameters
|
||||
SHAPE_POLY_SET m_viaAnnuliPolys; ///< Via annular ring outer diameters
|
||||
|
||||
SHAPE_POLY_SET m_frontCounterborePolys; ///< Counterbore outer diameters on front
|
||||
SHAPE_POLY_SET m_backCounterborePolys; ///< Counterbore outer diameters on back
|
||||
SHAPE_POLY_SET m_frontCountersinkPolys; ///< Countersink outer diameters on front
|
||||
SHAPE_POLY_SET m_backCountersinkPolys; ///< Countersink outer diameters on back
|
||||
|
||||
SHAPE_POLY_SET m_board_poly; ///< Board outline polygon.
|
||||
|
||||
MAP_CONTAINER_2D_BASE m_layerMap; ///< 2D elements for each layer.
|
||||
@@ -492,6 +590,14 @@ private:
|
||||
BVH_CONTAINER_2D m_viaAnnuli; ///< List of via annular rings
|
||||
BVH_CONTAINER_2D m_viaTH_ODs; ///< List of via hole outer diameters
|
||||
|
||||
BVH_CONTAINER_2D m_backdrillCutouts; ///< Backdrill cutouts
|
||||
BVH_CONTAINER_2D m_tertiarydrillCutouts; ///< Tertiary drill cutouts
|
||||
|
||||
BVH_CONTAINER_2D m_frontCounterboreCutouts; ///< Counterbore cutouts on front (top)
|
||||
BVH_CONTAINER_2D m_backCounterboreCutouts; ///< Counterbore cutouts on back (bottom)
|
||||
BVH_CONTAINER_2D m_frontCountersinkCutouts; ///< Countersink cutouts on front (top)
|
||||
BVH_CONTAINER_2D m_backCountersinkCutouts; ///< Countersink cutouts on back (bottom)
|
||||
|
||||
unsigned int m_copperLayersCount;
|
||||
|
||||
double m_biuTo3Dunits; ///< Scale factor to convert board internal units
|
||||
|
||||
@@ -174,6 +174,11 @@ void BOARD_ADAPTER::destroyLayers()
|
||||
m_viaTH_ODPolys.RemoveAllContours();
|
||||
m_viaAnnuliPolys.RemoveAllContours();
|
||||
|
||||
m_frontCounterborePolys.RemoveAllContours();
|
||||
m_backCounterborePolys.RemoveAllContours();
|
||||
m_frontCountersinkPolys.RemoveAllContours();
|
||||
m_backCountersinkPolys.RemoveAllContours();
|
||||
|
||||
DELETE_AND_FREE_MAP( m_layerMap )
|
||||
DELETE_AND_FREE_MAP( m_layerHoleMap )
|
||||
|
||||
@@ -186,6 +191,13 @@ void BOARD_ADAPTER::destroyLayers()
|
||||
m_TH_IDs.Clear();
|
||||
m_viaAnnuli.Clear();
|
||||
m_viaTH_ODs.Clear();
|
||||
|
||||
m_frontCounterboreCutouts.Clear();
|
||||
m_backCounterboreCutouts.Clear();
|
||||
m_frontCountersinkCutouts.Clear();
|
||||
m_backCountersinkCutouts.Clear();
|
||||
m_backdrillCutouts.Clear();
|
||||
m_tertiarydrillCutouts.Clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -318,6 +330,12 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
// Create VIAS and THTs objects and add it to holes containers
|
||||
for( PCB_LAYER_ID layer : layer_ids )
|
||||
{
|
||||
// Check if the layer is already created
|
||||
if( !m_layerHoleMap.contains( layer ) )
|
||||
{
|
||||
m_layerHoleMap[layer] = new BVH_CONTAINER_2D;
|
||||
}
|
||||
|
||||
// ADD TRACKS
|
||||
unsigned int nTracks = trackList.size();
|
||||
|
||||
@@ -349,19 +367,7 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
{
|
||||
// Add hole objects
|
||||
BVH_CONTAINER_2D *layerHoleContainer = nullptr;
|
||||
|
||||
// Check if the layer is already created
|
||||
if( !m_layerHoleMap.contains( layer ) )
|
||||
{
|
||||
// not found, create a new container
|
||||
layerHoleContainer = new BVH_CONTAINER_2D;
|
||||
m_layerHoleMap[layer] = layerHoleContainer;
|
||||
}
|
||||
else
|
||||
{
|
||||
// found
|
||||
layerHoleContainer = m_layerHoleMap[layer];
|
||||
}
|
||||
layerHoleContainer = m_layerHoleMap[layer];
|
||||
|
||||
// Add a hole for this layer
|
||||
layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius + thickness,
|
||||
@@ -379,6 +385,88 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
if( hole_inner_radius > 0.0 )
|
||||
m_TH_IDs.Add( new FILLED_CIRCLE_2D( via_center, hole_inner_radius, *track ) );
|
||||
}
|
||||
|
||||
// Add counterbore/countersink cutouts for vias
|
||||
const auto frontMode = via->GetFrontPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
|
||||
const double frontRadius = via->GetFrontPostMachiningSize() * m_biuTo3Dunits / 2.0;
|
||||
|
||||
if( frontMode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontMode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN
|
||||
&& frontRadius > hole_inner_radius )
|
||||
{
|
||||
if( layer == layer_ids[0] ) // only add once for front layer
|
||||
{
|
||||
if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
m_frontCounterboreCutouts.Add( new FILLED_CIRCLE_2D( via_center, frontRadius, *track ) );
|
||||
else if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
m_frontCountersinkCutouts.Add( new FILLED_CIRCLE_2D( via_center, frontRadius, *track ) );
|
||||
}
|
||||
|
||||
BVH_CONTAINER_2D *layerHoleContainer = m_layerHoleMap[layer];
|
||||
layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, frontRadius + thickness, *track ) );
|
||||
}
|
||||
|
||||
const auto backMode = via->GetBackPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
|
||||
const double backRadius = via->GetBackPostMachiningSize() * m_biuTo3Dunits / 2.0;
|
||||
|
||||
if( backMode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backMode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN
|
||||
&& backRadius > hole_inner_radius )
|
||||
{
|
||||
|
||||
if( layer == layer_ids.back() ) // only add once for back layer
|
||||
{
|
||||
if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
m_backCounterboreCutouts.Add( new FILLED_CIRCLE_2D( via_center, backRadius, *track ) );
|
||||
else if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
m_backCountersinkCutouts.Add( new FILLED_CIRCLE_2D( via_center, backRadius, *track ) );
|
||||
}
|
||||
|
||||
BVH_CONTAINER_2D *layerHoleContainer = m_layerHoleMap[layer];
|
||||
layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, backRadius + thickness, *track ) );
|
||||
}
|
||||
|
||||
// Add backdrill cutouts for vias - affects specific layers only
|
||||
float backdrillRadius = via->GetSecondaryDrillSize().value_or( 0 ) * m_biuTo3Dunits / 2.0f ;
|
||||
|
||||
// Only add if backdrill is larger than original hole
|
||||
if( backdrillRadius > hole_inner_radius + thickness )
|
||||
{
|
||||
PCB_LAYER_ID secStart = via->GetSecondaryDrillStartLayer();
|
||||
PCB_LAYER_ID secEnd = via->GetSecondaryDrillEndLayer();
|
||||
|
||||
if( LAYER_RANGE( secStart, secEnd, m_copperLayersCount ).Contains( layer ) )
|
||||
{
|
||||
// Add to layer hole map for this layer
|
||||
BVH_CONTAINER_2D* layerHoleContainer = m_layerHoleMap[layer];
|
||||
|
||||
layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, backdrillRadius, *track ) );
|
||||
}
|
||||
|
||||
if( layer == secStart )
|
||||
m_backdrillCutouts.Add( new FILLED_CIRCLE_2D( via_center, backdrillRadius, *track ) );
|
||||
}
|
||||
|
||||
// Add tertiary drill cutouts for vias - affects specific layers only
|
||||
float tertiaryDrillRadius = via->GetTertiaryDrillSize().value_or( 0 ) * m_biuTo3Dunits / 2.0f ;
|
||||
|
||||
// Only add if tertiary drill is larger than original hole
|
||||
if( tertiaryDrillRadius > hole_inner_radius + thickness )
|
||||
{
|
||||
PCB_LAYER_ID terStart = via->GetTertiaryDrillStartLayer();
|
||||
PCB_LAYER_ID terEnd = via->GetTertiaryDrillEndLayer();
|
||||
|
||||
if( LAYER_RANGE( terStart, terEnd, m_copperLayersCount ).Contains( layer ) )
|
||||
{
|
||||
// Add to layer hole map for this layer
|
||||
BVH_CONTAINER_2D* layerHoleContainer = m_layerHoleMap[layer];
|
||||
|
||||
layerHoleContainer->Add( new FILLED_CIRCLE_2D( via_center, tertiaryDrillRadius, *track ) );
|
||||
}
|
||||
|
||||
if( layer == terStart )
|
||||
m_tertiarydrillCutouts.Add( new FILLED_CIRCLE_2D( via_center, tertiaryDrillRadius, *track ) );
|
||||
}
|
||||
}
|
||||
|
||||
if( cfg.DifferentiatePlatedCopper() && layer == F_Cu )
|
||||
@@ -397,6 +485,16 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
// Create VIAS and THTs objects and add it to holes containers
|
||||
for( PCB_LAYER_ID layer : layer_ids )
|
||||
{
|
||||
if( !m_layerHoleOdPolys.contains( layer ) )
|
||||
{
|
||||
m_layerHoleOdPolys[layer] = new SHAPE_POLY_SET;
|
||||
}
|
||||
|
||||
if( !m_layerHoleIdPolys.contains( layer ) )
|
||||
{
|
||||
m_layerHoleIdPolys[layer] = new SHAPE_POLY_SET;
|
||||
}
|
||||
|
||||
// ADD TRACKS
|
||||
const unsigned int nTracks = trackList.size();
|
||||
|
||||
@@ -413,39 +511,23 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
|
||||
const VIATYPE viatype = via->GetViaType();
|
||||
|
||||
// Add outer holes of VIAs
|
||||
SHAPE_POLY_SET *layerOuterHolesPoly = nullptr;
|
||||
SHAPE_POLY_SET *layerInnerHolesPoly = nullptr;
|
||||
|
||||
// found
|
||||
wxASSERT( m_layerHoleOdPolys.contains( layer ) );
|
||||
wxASSERT( m_layerHoleIdPolys.contains( layer ) );
|
||||
|
||||
layerOuterHolesPoly = m_layerHoleOdPolys[layer];
|
||||
layerInnerHolesPoly = m_layerHoleIdPolys[layer];
|
||||
|
||||
const int holediameter = via->GetDrillValue();
|
||||
const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
|
||||
|
||||
if( viatype != VIATYPE::THROUGH )
|
||||
{
|
||||
// Add PCB_VIA hole contours
|
||||
|
||||
// Add outer holes of VIAs
|
||||
SHAPE_POLY_SET *layerOuterHolesPoly = nullptr;
|
||||
SHAPE_POLY_SET *layerInnerHolesPoly = nullptr;
|
||||
|
||||
// Check if the layer is already created
|
||||
if( !m_layerHoleOdPolys.contains( layer ) )
|
||||
{
|
||||
// not found, create a new container
|
||||
layerOuterHolesPoly = new SHAPE_POLY_SET;
|
||||
m_layerHoleOdPolys[layer] = layerOuterHolesPoly;
|
||||
|
||||
wxASSERT( !m_layerHoleIdPolys.contains( layer ) );
|
||||
|
||||
layerInnerHolesPoly = new SHAPE_POLY_SET;
|
||||
m_layerHoleIdPolys[layer] = layerInnerHolesPoly;
|
||||
}
|
||||
else
|
||||
{
|
||||
// found
|
||||
layerOuterHolesPoly = m_layerHoleOdPolys[layer];
|
||||
|
||||
wxASSERT( m_layerHoleIdPolys.contains( layer ) );
|
||||
|
||||
layerInnerHolesPoly = m_layerHoleIdPolys[layer];
|
||||
}
|
||||
|
||||
const int holediameter = via->GetDrillValue();
|
||||
const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
|
||||
|
||||
TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(), hole_outer_radius,
|
||||
via->GetMaxError(), ERROR_INSIDE );
|
||||
|
||||
@@ -454,8 +536,6 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
}
|
||||
else if( layer == layer_ids[0] ) // it only adds once the THT holes
|
||||
{
|
||||
const int holediameter = via->GetDrillValue();
|
||||
const int hole_outer_radius = (holediameter / 2) + GetHolePlatingThickness();
|
||||
const int hole_outer_ring_radius = KiROUND( via->GetWidth( layer ) / 2.0 );
|
||||
|
||||
// Add through hole contours
|
||||
@@ -472,6 +552,142 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
via->GetMaxError(), ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
|
||||
// Add counterbore/countersink polygons for vias
|
||||
const auto frontMode = via->GetFrontPostMachining();
|
||||
|
||||
if( frontMode.has_value()
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const int frontRadiusBIU = via->GetFrontPostMachiningSize() / 2;
|
||||
|
||||
if( frontRadiusBIU > holediameter / 2 )
|
||||
{
|
||||
int frontRadiusOuterBIU = frontRadiusBIU + GetHolePlatingThickness();
|
||||
|
||||
if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
TransformCircleToPolygon( m_frontCounterborePolys, via->GetStart(),
|
||||
frontRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
else if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
TransformCircleToPolygon( m_frontCountersinkPolys, via->GetStart(),
|
||||
frontRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
|
||||
TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(),
|
||||
frontRadiusOuterBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(),
|
||||
frontRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
|
||||
const auto backMode = via->GetBackPostMachining();
|
||||
|
||||
if( backMode.has_value()
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const int backRadiusBIU = via->GetBackPostMachiningSize() / 2;
|
||||
|
||||
if( backRadiusBIU > holediameter / 2 )
|
||||
{
|
||||
int backRadiusOuterBIU = backRadiusBIU + GetHolePlatingThickness();
|
||||
|
||||
if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
TransformCircleToPolygon( m_backCounterborePolys, via->GetStart(),
|
||||
backRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
else if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
TransformCircleToPolygon( m_backCountersinkPolys, via->GetStart(),
|
||||
backRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
|
||||
TransformCircleToPolygon( *layerOuterHolesPoly, via->GetStart(),
|
||||
backRadiusOuterBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
TransformCircleToPolygon( *layerInnerHolesPoly, via->GetStart(),
|
||||
backRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
|
||||
// Add backdrill polygons for vias - affects specific layers only
|
||||
const auto secondaryDrillSize = via->GetSecondaryDrillSize();
|
||||
|
||||
if( secondaryDrillSize.has_value() && secondaryDrillSize.value() > 0 )
|
||||
{
|
||||
const int backdrillRadiusBIU = secondaryDrillSize.value() / 2;
|
||||
const int backdrillOuterRadiusBIU = backdrillRadiusBIU + GetHolePlatingThickness();
|
||||
const int holeOuterRadiusBIU = hole_outer_radius;
|
||||
|
||||
// Only add if backdrill is larger than original hole outer diameter
|
||||
if( backdrillOuterRadiusBIU > holeOuterRadiusBIU )
|
||||
{
|
||||
PCB_LAYER_ID secStart = via->GetSecondaryDrillStartLayer();
|
||||
PCB_LAYER_ID secEnd = via->GetSecondaryDrillEndLayer();
|
||||
|
||||
// Iterate through layers affected by backdrill
|
||||
if( LAYER_RANGE( secStart, secEnd, m_copperLayersCount ).Contains( layer ) )
|
||||
{
|
||||
TransformCircleToPolygon( *m_layerHoleOdPolys[layer], via->GetStart(),
|
||||
backdrillOuterRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
|
||||
TransformCircleToPolygon( *m_layerHoleIdPolys[layer], via->GetStart(),
|
||||
backdrillRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
|
||||
TransformCircleToPolygon( m_BackdrillPolys, via->GetStart(),
|
||||
backdrillOuterRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add backdrill polygons for vias - affects specific layers only
|
||||
const auto tertiaryDrillSize = via->GetTertiaryDrillSize();
|
||||
|
||||
if( tertiaryDrillSize.has_value() && tertiaryDrillSize.value() > 0 )
|
||||
{
|
||||
const int backdrillRadiusBIU = tertiaryDrillSize.value() / 2;
|
||||
const int backdrillOuterRadiusBIU = backdrillRadiusBIU + GetHolePlatingThickness();
|
||||
const int holeOuterRadiusBIU = hole_outer_radius;
|
||||
|
||||
// Only add if backdrill is larger than original hole outer diameter
|
||||
if( backdrillOuterRadiusBIU > holeOuterRadiusBIU )
|
||||
{
|
||||
PCB_LAYER_ID terStart = via->GetTertiaryDrillStartLayer();
|
||||
PCB_LAYER_ID terEnd = via->GetTertiaryDrillEndLayer();
|
||||
|
||||
if( LAYER_RANGE( terStart, terEnd, m_copperLayersCount ).Contains( layer ) )
|
||||
{
|
||||
PCB_LAYER_ID backdrillLayer = layer;
|
||||
|
||||
TransformCircleToPolygon( *m_layerHoleOdPolys[backdrillLayer], via->GetStart(),
|
||||
backdrillOuterRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
|
||||
TransformCircleToPolygon( *m_layerHoleIdPolys[backdrillLayer], via->GetStart(),
|
||||
backdrillRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
|
||||
TransformCircleToPolygon( m_TertiarydrillPolys, via->GetStart(),
|
||||
backdrillOuterRadiusBIU, via->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -536,6 +752,103 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
createPadHoleShape( pad, &m_viaAnnuli, inflate );
|
||||
|
||||
createPadHoleShape( pad, &m_TH_IDs, 0 );
|
||||
|
||||
// Add counterbore/countersink cutouts for pads
|
||||
const float holeDiameterUnits = static_cast<float>(
|
||||
( pad->GetDrillSize().x + pad->GetDrillSize().y ) / 2.0 * m_biuTo3Dunits );
|
||||
const float holeInnerRadius = holeDiameterUnits / 2.0f;
|
||||
const SFVEC2F padCenter( pad->GetPosition().x * static_cast<float>( m_biuTo3Dunits ),
|
||||
-pad->GetPosition().y * static_cast<float>( m_biuTo3Dunits ) );
|
||||
|
||||
const auto frontMode = pad->GetFrontPostMachining();
|
||||
|
||||
if( frontMode.has_value()
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const float frontRadius = pad->GetFrontPostMachiningSize() * 0.5f
|
||||
* static_cast<float>( m_biuTo3Dunits );
|
||||
|
||||
if( frontRadius > holeInnerRadius )
|
||||
{
|
||||
if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
m_frontCounterboreCutouts.Add(
|
||||
new FILLED_CIRCLE_2D( padCenter, frontRadius, *pad ) );
|
||||
}
|
||||
else if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
m_frontCountersinkCutouts.Add(
|
||||
new FILLED_CIRCLE_2D( padCenter, frontRadius, *pad ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto backMode = pad->GetBackPostMachining();
|
||||
|
||||
if( backMode.has_value()
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const float backRadius = pad->GetBackPostMachiningSize() * 0.5f
|
||||
* static_cast<float>( m_biuTo3Dunits );
|
||||
|
||||
if( backRadius > holeInnerRadius )
|
||||
{
|
||||
if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
m_backCounterboreCutouts.Add(
|
||||
new FILLED_CIRCLE_2D( padCenter, backRadius, *pad ) );
|
||||
}
|
||||
else if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
m_backCountersinkCutouts.Add(
|
||||
new FILLED_CIRCLE_2D( padCenter, backRadius, *pad ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add backdrill cutouts for pads - affects specific layers only
|
||||
const VECTOR2I& secDrillSize = pad->GetSecondaryDrillSize();
|
||||
|
||||
if( secDrillSize.x > 0 || secDrillSize.y > 0 )
|
||||
{
|
||||
const float backdrillRadius = ( secDrillSize.x + secDrillSize.y ) * 0.25f
|
||||
* static_cast<float>( m_biuTo3Dunits );
|
||||
|
||||
// The hole outer diameter with plating
|
||||
const float holeOuterRadius = holeInnerRadius
|
||||
+ static_cast<float>( GetHolePlatingThickness()
|
||||
* m_biuTo3Dunits );
|
||||
|
||||
// Only add if backdrill is larger than original hole outer diameter
|
||||
if( backdrillRadius > holeOuterRadius )
|
||||
{
|
||||
PCB_LAYER_ID secStart = pad->GetSecondaryDrillStartLayer();
|
||||
PCB_LAYER_ID secEnd = pad->GetSecondaryDrillEndLayer();
|
||||
|
||||
// Iterate through layers affected by backdrill
|
||||
for( PCB_LAYER_ID backdrillLayer : LAYER_RANGE( secStart, secEnd,
|
||||
m_copperLayersCount ) )
|
||||
{
|
||||
// Add to layer hole map for this layer
|
||||
BVH_CONTAINER_2D* layerHoleContainer = nullptr;
|
||||
|
||||
if( !m_layerHoleMap.contains( backdrillLayer ) )
|
||||
{
|
||||
layerHoleContainer = new BVH_CONTAINER_2D;
|
||||
m_layerHoleMap[backdrillLayer] = layerHoleContainer;
|
||||
}
|
||||
else
|
||||
{
|
||||
layerHoleContainer = m_layerHoleMap[backdrillLayer];
|
||||
}
|
||||
|
||||
layerHoleContainer->Add(
|
||||
new FILLED_CIRCLE_2D( padCenter, backdrillRadius, *pad ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,6 +881,103 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
|
||||
pad->TransformHoleToPolygon( m_NPTH_ODPolys, 0, pad->GetMaxError(), ERROR_INSIDE );
|
||||
}
|
||||
|
||||
|
||||
// Add counterbore/countersink polygons for pads
|
||||
const double holeDiameter = ( pad->GetDrillSize().x + pad->GetDrillSize().y ) / 2.0;
|
||||
const int holeRadius = KiROUND( holeDiameter / 2.0 );
|
||||
|
||||
const auto frontMode = pad->GetFrontPostMachining();
|
||||
|
||||
if( frontMode.has_value()
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const int frontRadiusBIU = pad->GetFrontPostMachiningSize() / 2;
|
||||
|
||||
if( frontRadiusBIU > holeRadius )
|
||||
{
|
||||
if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
TransformCircleToPolygon( m_frontCounterborePolys, pad->GetPosition(),
|
||||
frontRadiusBIU, pad->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
else if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
TransformCircleToPolygon( m_frontCountersinkPolys, pad->GetPosition(),
|
||||
frontRadiusBIU, pad->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto backMode = pad->GetBackPostMachining();
|
||||
|
||||
if( backMode.has_value()
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const int backRadiusBIU = pad->GetBackPostMachiningSize() / 2;
|
||||
|
||||
if( backRadiusBIU > holeRadius )
|
||||
{
|
||||
if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
TransformCircleToPolygon( m_backCounterborePolys, pad->GetPosition(),
|
||||
backRadiusBIU, pad->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
else if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
TransformCircleToPolygon( m_backCountersinkPolys, pad->GetPosition(),
|
||||
backRadiusBIU, pad->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add backdrill polygons for pads - affects specific layers only
|
||||
const VECTOR2I& secDrillSize = pad->GetSecondaryDrillSize();
|
||||
|
||||
if( secDrillSize.x > 0 || secDrillSize.y > 0 )
|
||||
{
|
||||
const int backdrillRadiusBIU = ( secDrillSize.x + secDrillSize.y ) / 4;
|
||||
const int holeOuterRadiusBIU = holeRadius + inflate;
|
||||
|
||||
// Only add if backdrill is larger than original hole outer diameter
|
||||
if( backdrillRadiusBIU > holeOuterRadiusBIU )
|
||||
{
|
||||
PCB_LAYER_ID secStart = pad->GetSecondaryDrillStartLayer();
|
||||
PCB_LAYER_ID secEnd = pad->GetSecondaryDrillEndLayer();
|
||||
|
||||
TransformCircleToPolygon( m_BackdrillPolys, pad->GetPosition(),
|
||||
backdrillRadiusBIU, pad->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
|
||||
// Iterate through layers affected by backdrill
|
||||
for( PCB_LAYER_ID backdrillLayer : LAYER_RANGE( secStart, secEnd,
|
||||
m_copperLayersCount ) )
|
||||
{
|
||||
// Add polygon to per-layer hole polys
|
||||
SHAPE_POLY_SET* layerHolePoly = nullptr;
|
||||
|
||||
if( !m_layerHoleOdPolys.contains( backdrillLayer ) )
|
||||
{
|
||||
layerHolePoly = new SHAPE_POLY_SET;
|
||||
m_layerHoleOdPolys[backdrillLayer] = layerHolePoly;
|
||||
}
|
||||
else
|
||||
{
|
||||
layerHolePoly = m_layerHoleOdPolys[backdrillLayer];
|
||||
}
|
||||
|
||||
TransformCircleToPolygon( *layerHolePoly, pad->GetPosition(),
|
||||
backdrillRadiusBIU, pad->GetMaxError(),
|
||||
ERROR_INSIDE );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,6 +1261,35 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
m_viaTH_ODPolys.Simplify();
|
||||
m_viaAnnuliPolys.Simplify();
|
||||
|
||||
m_frontCounterborePolys.Simplify();
|
||||
m_backCounterborePolys.Simplify();
|
||||
m_frontCountersinkPolys.Simplify();
|
||||
m_backCountersinkPolys.Simplify();
|
||||
|
||||
// Remove counterbore/countersink/backdrill cutouts from via annuli (both front and back)
|
||||
// The via annuli are used for silkscreen clipping and shouldn't include areas removed
|
||||
// by counterbore, countersink, or backdrill operations
|
||||
if( m_viaAnnuliPolys.OutlineCount() > 0 )
|
||||
{
|
||||
if( m_frontCounterborePolys.OutlineCount() > 0 )
|
||||
m_viaAnnuliPolys.BooleanSubtract( m_frontCounterborePolys );
|
||||
|
||||
if( m_backCounterborePolys.OutlineCount() > 0 )
|
||||
m_viaAnnuliPolys.BooleanSubtract( m_backCounterborePolys );
|
||||
|
||||
if( m_frontCountersinkPolys.OutlineCount() > 0 )
|
||||
m_viaAnnuliPolys.BooleanSubtract( m_frontCountersinkPolys );
|
||||
|
||||
if( m_backCountersinkPolys.OutlineCount() > 0 )
|
||||
m_viaAnnuliPolys.BooleanSubtract( m_backCountersinkPolys );
|
||||
|
||||
if( m_BackdrillPolys.OutlineCount() > 0 )
|
||||
m_viaAnnuliPolys.BooleanSubtract( m_BackdrillPolys );
|
||||
|
||||
if( m_TertiarydrillPolys.OutlineCount() > 0 )
|
||||
m_viaAnnuliPolys.BooleanSubtract( m_TertiarydrillPolys );
|
||||
}
|
||||
|
||||
// Build Tech layers
|
||||
// Based on:
|
||||
// https://github.com/KiCad/kicad-source-mirror/blob/master/3d-viewer/3d_draw.cpp#L1059
|
||||
@@ -1329,6 +1768,13 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
|
||||
m_TH_ODs.BuildBVH();
|
||||
m_viaAnnuli.BuildBVH();
|
||||
|
||||
m_frontCounterboreCutouts.BuildBVH();
|
||||
m_backCounterboreCutouts.BuildBVH();
|
||||
m_frontCountersinkCutouts.BuildBVH();
|
||||
m_backCountersinkCutouts.BuildBVH();
|
||||
m_backdrillCutouts.BuildBVH();
|
||||
m_tertiarydrillCutouts.BuildBVH();
|
||||
|
||||
if( !m_layerHoleMap.empty() )
|
||||
{
|
||||
for( std::pair<const PCB_LAYER_ID, BVH_CONTAINER_2D*>& hole : m_layerHoleMap )
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -80,6 +80,7 @@ RENDER_3D_OPENGL::RENDER_3D_OPENGL( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdap
|
||||
m_lastGridType = GRID3D_TYPE::NONE;
|
||||
m_currentRollOverItem = nullptr;
|
||||
m_boardWithHoles = nullptr;
|
||||
m_postMachinePlugs = nullptr;
|
||||
|
||||
m_3dModelMap.clear();
|
||||
|
||||
@@ -482,6 +483,16 @@ void RENDER_3D_OPENGL::renderBoardBody( bool aSkipRenderHoles )
|
||||
ogl_disp_list->SetItIsTransparent( true );
|
||||
ogl_disp_list->DrawAll();
|
||||
}
|
||||
|
||||
// Also render post-machining plugs (board material that remains after backdrill/counterbore/countersink)
|
||||
if( !aSkipRenderHoles && m_postMachinePlugs )
|
||||
{
|
||||
m_postMachinePlugs->ApplyScalePosition( -m_boardAdapter.GetBoardBodyThickness() / 2.0f,
|
||||
m_boardAdapter.GetBoardBodyThickness() );
|
||||
|
||||
m_postMachinePlugs->SetItIsTransparent( true );
|
||||
m_postMachinePlugs->DrawAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -941,6 +952,7 @@ void RENDER_3D_OPENGL::freeAllLists()
|
||||
|
||||
DELETE_AND_FREE( m_board )
|
||||
DELETE_AND_FREE( m_boardWithHoles )
|
||||
DELETE_AND_FREE( m_postMachinePlugs )
|
||||
DELETE_AND_FREE( m_antiBoard )
|
||||
|
||||
DELETE_AND_FREE( m_outerThroughHoles )
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
|
||||
#include "3d_cache/3d_info.h"
|
||||
|
||||
#include <geometry/eda_angle.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
typedef std::map< PCB_LAYER_ID, OPENGL_RENDER_LIST* > MAP_OGL_DISP_LISTS;
|
||||
@@ -123,6 +125,10 @@ private:
|
||||
float aZtop, float aZbot, unsigned int aNr_sides_per_circle,
|
||||
TRIANGLE_DISPLAY_LIST* aDstLayer );
|
||||
|
||||
void generateInvCone( const SFVEC2F& aCenter, float aInnerRadius, float aOuterRadius,
|
||||
float aZtop, float aZbot, unsigned int aNr_sides_per_circle,
|
||||
TRIANGLE_DISPLAY_LIST* aDstLayer, EDA_ANGLE aAngle );
|
||||
|
||||
void generateDisk( const SFVEC2F& aCenter, float aRadius, float aZ,
|
||||
unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST* aDstLayer,
|
||||
bool aTop );
|
||||
@@ -133,6 +139,24 @@ private:
|
||||
|
||||
void generateViasAndPads();
|
||||
|
||||
bool appendPostMachiningGeometry( TRIANGLE_DISPLAY_LIST* aDstLayer,
|
||||
const SFVEC2F& aHoleCenter,
|
||||
PAD_DRILL_POST_MACHINING_MODE aMode,
|
||||
int aSizeIU,
|
||||
int aDepthIU,
|
||||
float aHoleInnerRadius,
|
||||
float aZSurface,
|
||||
bool aIsFront,
|
||||
float aPlatingThickness3d,
|
||||
float aUnitScale,
|
||||
float* aZEnd );
|
||||
|
||||
void generateViaBarrels( float aPlatingThickness3d, float aUnitScale );
|
||||
|
||||
void generatePlatedHoleShells( int aPlatingThickness, float aUnitScale );
|
||||
|
||||
void generateViaCovers( float aPlatingThickness3d, float aUnitScale );
|
||||
|
||||
/**
|
||||
* Load footprint models from the cache and load it to openGL lists in the form of
|
||||
* #MODEL_3D objects.
|
||||
@@ -201,6 +225,14 @@ private:
|
||||
bool initializeOpenGL();
|
||||
OPENGL_RENDER_LIST* createBoard( const SHAPE_POLY_SET& aBoardPoly,
|
||||
const BVH_CONTAINER_2D* aThroughHoles = nullptr );
|
||||
|
||||
/**
|
||||
* Create ring-shaped plugs for holes that have backdrill or post-machining.
|
||||
* These plugs represent the board material that remains in the hole where
|
||||
* the backdrill or post-machining didn't reach.
|
||||
*/
|
||||
void backfillPostMachine();
|
||||
|
||||
void reload( REPORTER* aStatusReporter, REPORTER* aWarningReporter );
|
||||
|
||||
void setArrowMaterial();
|
||||
@@ -231,6 +263,7 @@ private:
|
||||
MAP_OGL_DISP_LISTS m_innerLayerHoles;
|
||||
OPENGL_RENDER_LIST* m_board;
|
||||
OPENGL_RENDER_LIST* m_boardWithHoles;
|
||||
OPENGL_RENDER_LIST* m_postMachinePlugs; ///< Board material plugs for backdrill/counterbore/countersink
|
||||
OPENGL_RENDER_LIST* m_antiBoard;
|
||||
OPENGL_RENDER_LIST* m_outerThroughHoles;
|
||||
OPENGL_RENDER_LIST* m_outerViaThroughHoles;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "shapes3D/round_segment_3d.h"
|
||||
#include "shapes3D/layer_item_3d.h"
|
||||
#include "shapes3D/cylinder_3d.h"
|
||||
#include "shapes3D/frustum_3d.h"
|
||||
#include "shapes3D/triangle_3d.h"
|
||||
#include "shapes2D/layer_item_2d.h"
|
||||
#include "shapes2D/ring_2d.h"
|
||||
@@ -293,6 +294,33 @@ void RENDER_3D_RAYTRACE_BASE::createItemsFromContainer( const BVH_CONTAINER_2D*
|
||||
for( const OBJECT_2D* hole2d : intersecting )
|
||||
object2d_B->push_back( hole2d );
|
||||
}
|
||||
|
||||
// Clip counterbore/countersink cutouts from copper layers
|
||||
// Front cutouts affect F_Cu, back cutouts affect B_Cu
|
||||
auto clipCutouts = [this, &object2d_A, &object2d_B]( const BVH_CONTAINER_2D& cutouts )
|
||||
{
|
||||
if( !cutouts.GetList().empty() )
|
||||
{
|
||||
CONST_LIST_OBJECT2D intersecting;
|
||||
cutouts.GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
|
||||
|
||||
for( const OBJECT_2D* cutout : intersecting )
|
||||
object2d_B->push_back( cutout );
|
||||
}
|
||||
};
|
||||
|
||||
if( aLayer_id == F_Cu )
|
||||
{
|
||||
clipCutouts( m_boardAdapter.GetFrontCounterboreCutouts() );
|
||||
clipCutouts( m_boardAdapter.GetFrontCountersinkCutouts() );
|
||||
clipCutouts( m_boardAdapter.GetTertiarydrillCutouts() );
|
||||
}
|
||||
else if( aLayer_id == B_Cu )
|
||||
{
|
||||
clipCutouts( m_boardAdapter.GetBackCounterboreCutouts() );
|
||||
clipCutouts( m_boardAdapter.GetBackCountersinkCutouts() );
|
||||
clipCutouts( m_boardAdapter.GetBackdrillCutouts() );
|
||||
}
|
||||
}
|
||||
|
||||
if( !m_antioutlineBoard2dObjects->GetList().empty() )
|
||||
@@ -463,6 +491,60 @@ void RENDER_3D_RAYTRACE_BASE::Reload( REPORTER* aStatusReporter, REPORTER* aWarn
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract counterbore/countersink cutouts from board body
|
||||
auto addCutoutsFromContainer =
|
||||
[&]( const BVH_CONTAINER_2D& aContainer )
|
||||
{
|
||||
if( !aContainer.GetList().empty() )
|
||||
{
|
||||
CONST_LIST_OBJECT2D intersecting;
|
||||
aContainer.GetIntersectingObjects( object2d_A->GetBBox(),
|
||||
intersecting );
|
||||
|
||||
for( const OBJECT_2D* cutout : intersecting )
|
||||
{
|
||||
if( object2d_A->Intersects( cutout->GetBBox() ) )
|
||||
object2d_B->push_back( cutout );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
addCutoutsFromContainer( m_boardAdapter.GetFrontCounterboreCutouts() );
|
||||
addCutoutsFromContainer( m_boardAdapter.GetBackCounterboreCutouts() );
|
||||
addCutoutsFromContainer( m_boardAdapter.GetFrontCountersinkCutouts() );
|
||||
addCutoutsFromContainer( m_boardAdapter.GetBackCountersinkCutouts() );
|
||||
|
||||
// Subtract backdrill holes (which are in layerHoleMap for F_Cu and B_Cu)
|
||||
const MAP_CONTAINER_2D_BASE& layerHolesMap = m_boardAdapter.GetLayerHoleMap();
|
||||
|
||||
if( layerHolesMap.find( F_Cu ) != layerHolesMap.end() )
|
||||
{
|
||||
const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( F_Cu );
|
||||
CONST_LIST_OBJECT2D intersecting;
|
||||
|
||||
holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
|
||||
|
||||
for( const OBJECT_2D* hole2d : intersecting )
|
||||
{
|
||||
if( object2d_A->Intersects( hole2d->GetBBox() ) )
|
||||
object2d_B->push_back( hole2d );
|
||||
}
|
||||
}
|
||||
|
||||
if( layerHolesMap.find( B_Cu ) != layerHolesMap.end() )
|
||||
{
|
||||
const BVH_CONTAINER_2D* holes2d = layerHolesMap.at( B_Cu );
|
||||
CONST_LIST_OBJECT2D intersecting;
|
||||
|
||||
holes2d->GetIntersectingObjects( object2d_A->GetBBox(), intersecting );
|
||||
|
||||
for( const OBJECT_2D* hole2d : intersecting )
|
||||
{
|
||||
if( object2d_A->Intersects( hole2d->GetBBox() ) )
|
||||
object2d_B->push_back( hole2d );
|
||||
}
|
||||
}
|
||||
|
||||
if( !m_antioutlineBoard2dObjects->GetList().empty() )
|
||||
{
|
||||
CONST_LIST_OBJECT2D intersecting;
|
||||
@@ -557,6 +639,9 @@ void RENDER_3D_RAYTRACE_BASE::Reload( REPORTER* aStatusReporter, REPORTER* aWarn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create plugs for backdrilled and post-machined areas
|
||||
backfillPostMachine();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -978,6 +1063,503 @@ void RENDER_3D_RAYTRACE_BASE::Reload( REPORTER* aStatusReporter, REPORTER* aWarn
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_RAYTRACE_BASE::addCounterborePlating( const BOARD_ITEM& aSource,
|
||||
const SFVEC2F& aCenter,
|
||||
float aInnerRadius, float aDepth,
|
||||
float aSurfaceZ, bool aIsFront )
|
||||
{
|
||||
const float platingThickness = m_boardAdapter.GetHolePlatingThickness()
|
||||
* m_boardAdapter.BiuTo3dUnits();
|
||||
|
||||
if( platingThickness <= 0.0f || aInnerRadius <= 0.0f || aDepth <= 0.0f )
|
||||
return;
|
||||
|
||||
const float outerRadius = aInnerRadius + platingThickness;
|
||||
const float zOther = aIsFront ? ( aSurfaceZ - aDepth ) : ( aSurfaceZ + aDepth );
|
||||
const float zMin = std::min( aSurfaceZ, zOther );
|
||||
const float zMax = std::max( aSurfaceZ, zOther );
|
||||
|
||||
RING_2D* ring = new RING_2D( aCenter, aInnerRadius, outerRadius, aSource );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, zMin, zMax );
|
||||
objPtr->SetMaterial( &m_materials.m_Copper );
|
||||
objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_CopperColor ) );
|
||||
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_RAYTRACE_BASE::addCountersinkPlating( const SFVEC2F& aCenter,
|
||||
float aTopInnerRadius,
|
||||
float aBottomInnerRadius,
|
||||
float aSurfaceZ, float aDepth,
|
||||
bool aIsFront )
|
||||
{
|
||||
const float platingThickness = m_boardAdapter.GetHolePlatingThickness()
|
||||
* m_boardAdapter.BiuTo3dUnits();
|
||||
|
||||
if( platingThickness <= 0.0f || aTopInnerRadius <= 0.0f || aBottomInnerRadius <= 0.0f
|
||||
|| aDepth <= 0.0f )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const float topOuterRadius = aTopInnerRadius + platingThickness;
|
||||
const float bottomOuterRadius = aBottomInnerRadius + platingThickness;
|
||||
|
||||
const float zOther = aIsFront ? ( aSurfaceZ - aDepth ) : ( aSurfaceZ + aDepth );
|
||||
const float zTop = std::max( aSurfaceZ, zOther );
|
||||
const float zBot = std::min( aSurfaceZ, zOther );
|
||||
|
||||
if( topOuterRadius <= 0.0f || bottomOuterRadius <= 0.0f )
|
||||
return;
|
||||
|
||||
const float largestDiameter = 2.0f * std::max( aTopInnerRadius, aBottomInnerRadius );
|
||||
unsigned int segments = std::max( 12u, m_boardAdapter.GetCircleSegmentCount( largestDiameter ) );
|
||||
|
||||
const SFVEC3F copperColor = ConvertSRGBToLinear( m_boardAdapter.m_CopperColor );
|
||||
|
||||
auto addQuad = [&]( const SFVEC3F& p0, const SFVEC3F& p1,
|
||||
const SFVEC3F& p2, const SFVEC3F& p3 )
|
||||
{
|
||||
TRIANGLE* tri1 = new TRIANGLE( p0, p1, p2 );
|
||||
TRIANGLE* tri2 = new TRIANGLE( p0, p2, p3 );
|
||||
|
||||
tri1->SetMaterial( &m_materials.m_Copper );
|
||||
tri2->SetMaterial( &m_materials.m_Copper );
|
||||
tri1->SetColor( copperColor );
|
||||
tri2->SetColor( copperColor );
|
||||
|
||||
m_objectContainer.Add( tri1 );
|
||||
m_objectContainer.Add( tri2 );
|
||||
};
|
||||
|
||||
auto makePoint = [&]( float radius, float angle, float z )
|
||||
{
|
||||
return SFVEC3F( aCenter.x + cosf( angle ) * radius,
|
||||
aCenter.y + sinf( angle ) * radius,
|
||||
z );
|
||||
};
|
||||
|
||||
const float step = 2.0f * glm::pi<float>() / (float) segments;
|
||||
|
||||
SFVEC3F innerTopPrev = makePoint( aTopInnerRadius, 0.0f, zTop );
|
||||
SFVEC3F innerBotPrev = makePoint( aBottomInnerRadius, 0.0f, zBot );
|
||||
SFVEC3F outerTopPrev = makePoint( topOuterRadius, 0.0f, zTop );
|
||||
SFVEC3F outerBotPrev = makePoint( bottomOuterRadius, 0.0f, zBot );
|
||||
|
||||
const SFVEC3F innerTopFirst = innerTopPrev;
|
||||
const SFVEC3F innerBotFirst = innerBotPrev;
|
||||
const SFVEC3F outerTopFirst = outerTopPrev;
|
||||
const SFVEC3F outerBotFirst = outerBotPrev;
|
||||
|
||||
for( unsigned int i = 1; i <= segments; ++i )
|
||||
{
|
||||
const float angle = ( i == segments ) ? 0.0f : step * i;
|
||||
|
||||
const SFVEC3F innerTopCurr = ( i == segments ) ? innerTopFirst
|
||||
: makePoint( aTopInnerRadius, angle, zTop );
|
||||
const SFVEC3F innerBotCurr = ( i == segments ) ? innerBotFirst
|
||||
: makePoint( aBottomInnerRadius, angle, zBot );
|
||||
const SFVEC3F outerTopCurr = ( i == segments ) ? outerTopFirst
|
||||
: makePoint( topOuterRadius, angle, zTop );
|
||||
const SFVEC3F outerBotCurr = ( i == segments ) ? outerBotFirst
|
||||
: makePoint( bottomOuterRadius, angle, zBot );
|
||||
|
||||
// Inner wall
|
||||
addQuad( innerTopPrev, innerTopCurr, innerBotCurr, innerBotPrev );
|
||||
|
||||
// Outer wall
|
||||
addQuad( outerTopPrev, outerBotPrev, outerBotCurr, outerTopCurr );
|
||||
|
||||
// Top rim
|
||||
addQuad( outerTopPrev, outerTopCurr, innerTopCurr, innerTopPrev );
|
||||
|
||||
// Bottom rim
|
||||
addQuad( outerBotPrev, innerBotPrev, innerBotCurr, outerBotCurr );
|
||||
|
||||
innerTopPrev = innerTopCurr;
|
||||
innerBotPrev = innerBotCurr;
|
||||
outerTopPrev = outerTopCurr;
|
||||
outerBotPrev = outerBotCurr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_RAYTRACE_BASE::backfillPostMachine()
|
||||
{
|
||||
if( !m_boardAdapter.GetBoard() )
|
||||
return;
|
||||
|
||||
const float unitScale = m_boardAdapter.BiuTo3dUnits();
|
||||
const int platingThickness = m_boardAdapter.GetHolePlatingThickness();
|
||||
const float platingThickness3d = platingThickness * unitScale;
|
||||
const SFVEC3F boardColor = ConvertSRGBToLinear( m_boardAdapter.m_BoardBodyColor );
|
||||
|
||||
const float boardZTop = m_boardAdapter.GetLayerBottomZPos( F_Cu );
|
||||
const float boardZBot = m_boardAdapter.GetLayerBottomZPos( B_Cu );
|
||||
|
||||
// Process vias for backdrill and post-machining plugs
|
||||
for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
|
||||
{
|
||||
if( track->Type() != PCB_VIA_T )
|
||||
continue;
|
||||
|
||||
const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
|
||||
|
||||
const float holeDiameter = via->GetDrillValue() * unitScale;
|
||||
const float holeInnerRadius = holeDiameter / 2.0f;
|
||||
const float holeOuterRadius = holeInnerRadius + platingThickness3d;
|
||||
const SFVEC2F center( via->GetStart().x * unitScale, -via->GetStart().y * unitScale );
|
||||
|
||||
PCB_LAYER_ID topLayer, bottomLayer;
|
||||
via->LayerPair( &topLayer, &bottomLayer );
|
||||
|
||||
const float viaZTop = m_boardAdapter.GetLayerBottomZPos( topLayer );
|
||||
const float viaZBot = m_boardAdapter.GetLayerBottomZPos( bottomLayer );
|
||||
|
||||
// Handle backdrill plugs
|
||||
const auto secondaryDrillSize = via->GetSecondaryDrillSize();
|
||||
|
||||
if( secondaryDrillSize.has_value() && secondaryDrillSize.value() > 0 )
|
||||
{
|
||||
const float backdrillRadius = secondaryDrillSize.value() * 0.5f * unitScale;
|
||||
|
||||
if( backdrillRadius > holeOuterRadius )
|
||||
{
|
||||
PCB_LAYER_ID secStart = via->GetSecondaryDrillStartLayer();
|
||||
PCB_LAYER_ID secEnd = via->GetSecondaryDrillEndLayer();
|
||||
|
||||
// Calculate where the backdrill ends and plug should start
|
||||
const float secEndZ = m_boardAdapter.GetLayerBottomZPos( secEnd );
|
||||
|
||||
float plugZTop, plugZBot;
|
||||
|
||||
if( secStart == F_Cu )
|
||||
{
|
||||
// Backdrill from top: plug goes from below backdrill end to via bottom
|
||||
plugZTop = secEndZ;
|
||||
plugZBot = viaZBot;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Backdrill from bottom: plug goes from via top to above backdrill end
|
||||
plugZTop = viaZTop;
|
||||
plugZBot = secEndZ;
|
||||
}
|
||||
|
||||
if( plugZTop > plugZBot )
|
||||
{
|
||||
// Create a ring from holeOuterRadius to backdrillRadius
|
||||
RING_2D* ring = new RING_2D( center, holeOuterRadius, backdrillRadius, *via );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, plugZTop );
|
||||
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
objPtr->SetColor( boardColor );
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle front post-machining plugs
|
||||
const auto frontMode = via->GetFrontPostMachining();
|
||||
|
||||
if( frontMode.has_value()
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const float frontRadius = via->GetFrontPostMachiningSize() * 0.5f * unitScale;
|
||||
const float frontDepth = via->GetFrontPostMachiningDepth() * unitScale;
|
||||
|
||||
if( frontRadius > holeOuterRadius && frontDepth > 0 )
|
||||
{
|
||||
// Plug goes from bottom of post-machining to bottom of via
|
||||
const float pmBottomZ = viaZTop - frontDepth;
|
||||
const float plugZBot = viaZBot;
|
||||
|
||||
if( pmBottomZ > plugZBot )
|
||||
{
|
||||
if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
// For countersink, use a frustum (truncated cone)
|
||||
EDA_ANGLE angle( via->GetFrontPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
|
||||
float angleRad = angle.AsRadians();
|
||||
if( angleRad < 0.01f )
|
||||
angleRad = 0.01f;
|
||||
|
||||
float radialDiff = frontRadius - holeOuterRadius;
|
||||
float innerHeight = radialDiff / tanf( angleRad );
|
||||
float totalHeight = pmBottomZ - plugZBot;
|
||||
|
||||
if( innerHeight > totalHeight )
|
||||
innerHeight = totalHeight;
|
||||
|
||||
float zInnerTop = plugZBot + innerHeight;
|
||||
|
||||
// Create frustum from holeOuterRadius at zInnerTop to frontRadius at pmBottomZ
|
||||
TRUNCATED_CONE* frustum = new TRUNCATED_CONE( center, zInnerTop, pmBottomZ,
|
||||
holeOuterRadius, frontRadius );
|
||||
frustum->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
frustum->SetColor( boardColor );
|
||||
m_objectContainer.Add( frustum );
|
||||
|
||||
// If there's a cylindrical portion below the cone
|
||||
if( zInnerTop > plugZBot )
|
||||
{
|
||||
RING_2D* ring = new RING_2D( center, holeOuterRadius, frontRadius, *via );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, zInnerTop );
|
||||
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
objPtr->SetColor( boardColor );
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RING_2D* ring = new RING_2D( center, holeOuterRadius, frontRadius, *via );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, pmBottomZ );
|
||||
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
objPtr->SetColor( boardColor );
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle back post-machining plugs
|
||||
const auto backMode = via->GetBackPostMachining();
|
||||
|
||||
if( backMode.has_value()
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const float backRadius = via->GetBackPostMachiningSize() * 0.5f * unitScale;
|
||||
const float backDepth = via->GetBackPostMachiningDepth() * unitScale;
|
||||
|
||||
if( backRadius > holeOuterRadius && backDepth > 0 )
|
||||
{
|
||||
// Plug goes from top of via to top of post-machining
|
||||
const float plugZTop = viaZTop;
|
||||
const float pmTopZ = viaZBot + backDepth;
|
||||
|
||||
if( plugZTop > pmTopZ )
|
||||
{
|
||||
if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
// For countersink, use a frustum (truncated cone)
|
||||
EDA_ANGLE angle( via->GetBackPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
|
||||
float angleRad = angle.AsRadians();
|
||||
if( angleRad < 0.01f )
|
||||
angleRad = 0.01f;
|
||||
|
||||
float radialDiff = backRadius - holeOuterRadius;
|
||||
float innerHeight = radialDiff / tanf( angleRad );
|
||||
float totalHeight = plugZTop - pmTopZ;
|
||||
|
||||
if( innerHeight > totalHeight )
|
||||
innerHeight = totalHeight;
|
||||
|
||||
float zInnerBot = plugZTop - innerHeight;
|
||||
|
||||
// Create frustum from holeOuterRadius at zInnerBot to backRadius at pmTopZ
|
||||
TRUNCATED_CONE* frustum = new TRUNCATED_CONE( center, pmTopZ, zInnerBot,
|
||||
backRadius, holeOuterRadius );
|
||||
frustum->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
frustum->SetColor( boardColor );
|
||||
m_objectContainer.Add( frustum );
|
||||
|
||||
// If there's a cylindrical portion above the cone
|
||||
if( zInnerBot < plugZTop )
|
||||
{
|
||||
RING_2D* ring = new RING_2D( center, holeOuterRadius, backRadius, *via );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, zInnerBot, plugZTop );
|
||||
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
objPtr->SetColor( boardColor );
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RING_2D* ring = new RING_2D( center, holeOuterRadius, backRadius, *via );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, pmTopZ, plugZTop );
|
||||
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
objPtr->SetColor( boardColor );
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process pads for post-machining plugs
|
||||
for( const FOOTPRINT* footprint : m_boardAdapter.GetBoard()->Footprints() )
|
||||
{
|
||||
for( const PAD* pad : footprint->Pads() )
|
||||
{
|
||||
if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
||||
continue;
|
||||
|
||||
if( !pad->HasHole() )
|
||||
continue;
|
||||
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
continue;
|
||||
|
||||
const SFVEC2F padCenter( pad->GetPosition().x * unitScale,
|
||||
-pad->GetPosition().y * unitScale );
|
||||
const float holeInnerRadius = pad->GetDrillSize().x * 0.5f * unitScale;
|
||||
const float holeOuterRadius = holeInnerRadius + platingThickness3d;
|
||||
|
||||
const float padZTop = boardZTop;
|
||||
const float padZBot = boardZBot;
|
||||
|
||||
// Handle front post-machining plugs for pads
|
||||
const auto frontMode = pad->GetFrontPostMachining();
|
||||
|
||||
if( frontMode.has_value()
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const float frontRadius = pad->GetFrontPostMachiningSize() * 0.5f * unitScale;
|
||||
const float frontDepth = pad->GetFrontPostMachiningDepth() * unitScale;
|
||||
|
||||
if( frontRadius > holeOuterRadius && frontDepth > 0 )
|
||||
{
|
||||
const float pmBottomZ = padZTop - frontDepth;
|
||||
const float plugZBot = padZBot;
|
||||
|
||||
if( pmBottomZ > plugZBot )
|
||||
{
|
||||
if( frontMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
// For countersink, use a frustum (truncated cone)
|
||||
EDA_ANGLE angle( pad->GetFrontPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
|
||||
float angleRad = angle.AsRadians();
|
||||
if( angleRad < 0.01f )
|
||||
angleRad = 0.01f;
|
||||
|
||||
float radialDiff = frontRadius - holeOuterRadius;
|
||||
float innerHeight = radialDiff / tanf( angleRad );
|
||||
float totalHeight = pmBottomZ - plugZBot;
|
||||
|
||||
if( innerHeight > totalHeight )
|
||||
innerHeight = totalHeight;
|
||||
|
||||
float zInnerTop = plugZBot + innerHeight;
|
||||
|
||||
// Create frustum from holeOuterRadius at zInnerTop to frontRadius at pmBottomZ
|
||||
TRUNCATED_CONE* frustum = new TRUNCATED_CONE( padCenter, zInnerTop, pmBottomZ,
|
||||
holeOuterRadius, frontRadius );
|
||||
frustum->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
frustum->SetColor( boardColor );
|
||||
m_objectContainer.Add( frustum );
|
||||
|
||||
// If there's a cylindrical portion below the cone
|
||||
if( zInnerTop > plugZBot )
|
||||
{
|
||||
RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, frontRadius, *pad );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, zInnerTop );
|
||||
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
objPtr->SetColor( boardColor );
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, frontRadius, *pad );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, plugZBot, pmBottomZ );
|
||||
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
objPtr->SetColor( boardColor );
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle back post-machining plugs for pads
|
||||
const auto backMode = pad->GetBackPostMachining();
|
||||
|
||||
if( backMode.has_value()
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backMode.value() != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
const float backRadius = pad->GetBackPostMachiningSize() * 0.5f * unitScale;
|
||||
const float backDepth = pad->GetBackPostMachiningDepth() * unitScale;
|
||||
|
||||
if( backRadius > holeOuterRadius && backDepth > 0 )
|
||||
{
|
||||
const float plugZTop = padZTop;
|
||||
const float pmTopZ = padZBot + backDepth;
|
||||
|
||||
if( plugZTop > pmTopZ )
|
||||
{
|
||||
if( backMode.value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
// For countersink, use a frustum (truncated cone)
|
||||
EDA_ANGLE angle( pad->GetBackPostMachiningAngle(), TENTHS_OF_A_DEGREE_T );
|
||||
float angleRad = angle.AsRadians();
|
||||
if( angleRad < 0.01f )
|
||||
angleRad = 0.01f;
|
||||
|
||||
float radialDiff = backRadius - holeOuterRadius;
|
||||
float innerHeight = radialDiff / tanf( angleRad );
|
||||
float totalHeight = plugZTop - pmTopZ;
|
||||
|
||||
if( innerHeight > totalHeight )
|
||||
innerHeight = totalHeight;
|
||||
|
||||
float zInnerBot = plugZTop - innerHeight;
|
||||
|
||||
// Create frustum from holeOuterRadius at zInnerBot to backRadius at pmTopZ
|
||||
TRUNCATED_CONE* frustum = new TRUNCATED_CONE( padCenter, pmTopZ, zInnerBot,
|
||||
backRadius, holeOuterRadius );
|
||||
frustum->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
frustum->SetColor( boardColor );
|
||||
m_objectContainer.Add( frustum );
|
||||
|
||||
// If there's a cylindrical portion above the cone
|
||||
if( zInnerBot < plugZTop )
|
||||
{
|
||||
RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, backRadius, *pad );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, zInnerBot, plugZTop );
|
||||
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
objPtr->SetColor( boardColor );
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RING_2D* ring = new RING_2D( padCenter, holeOuterRadius, backRadius, *pad );
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
LAYER_ITEM* objPtr = new LAYER_ITEM( ring, pmTopZ, plugZTop );
|
||||
objPtr->SetMaterial( &m_materials.m_EpoxyBoard );
|
||||
objPtr->SetColor( boardColor );
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RENDER_3D_RAYTRACE_BASE::insertHole( const PCB_VIA* aVia )
|
||||
{
|
||||
PCB_LAYER_ID top_layer, bottom_layer;
|
||||
@@ -985,18 +1567,26 @@ void RENDER_3D_RAYTRACE_BASE::insertHole( const PCB_VIA* aVia )
|
||||
|
||||
aVia->LayerPair( &top_layer, &bottom_layer );
|
||||
|
||||
float frontDepth = 0.0f;
|
||||
float backDepth = 0.0f;
|
||||
|
||||
if( aVia->Padstack().FrontPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED ) != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
|
||||
frontDepth = aVia->Padstack().FrontPostMachining().depth * m_boardAdapter.BiuTo3dUnits();
|
||||
|
||||
if( aVia->Padstack().BackPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED ) != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
|
||||
backDepth = aVia->Padstack().BackPostMachining().depth * m_boardAdapter.BiuTo3dUnits();
|
||||
|
||||
float topZ = m_boardAdapter.GetLayerBottomZPos( top_layer )
|
||||
+ m_boardAdapter.GetFrontCopperThickness();
|
||||
+ m_boardAdapter.GetFrontCopperThickness() - frontDepth;
|
||||
|
||||
float botZ = m_boardAdapter.GetLayerBottomZPos( bottom_layer )
|
||||
- m_boardAdapter.GetBackCopperThickness();
|
||||
- m_boardAdapter.GetBackCopperThickness() + backDepth;
|
||||
|
||||
const SFVEC2F center = SFVEC2F( aVia->GetStart().x * m_boardAdapter.BiuTo3dUnits(),
|
||||
-aVia->GetStart().y * m_boardAdapter.BiuTo3dUnits() );
|
||||
const float unitScale = m_boardAdapter.BiuTo3dUnits();
|
||||
const SFVEC2F center = SFVEC2F( aVia->GetStart().x * unitScale, -aVia->GetStart().y * unitScale );
|
||||
|
||||
RING_2D* ring = new RING_2D( center, radiusBUI * m_boardAdapter.BiuTo3dUnits(),
|
||||
( radiusBUI + m_boardAdapter.GetHolePlatingThickness() )
|
||||
* m_boardAdapter.BiuTo3dUnits(), *aVia );
|
||||
RING_2D* ring = new RING_2D( center, radiusBUI * unitScale,
|
||||
( radiusBUI + m_boardAdapter.GetHolePlatingThickness() ) * unitScale, *aVia );
|
||||
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
@@ -1006,6 +1596,44 @@ void RENDER_3D_RAYTRACE_BASE::insertHole( const PCB_VIA* aVia )
|
||||
objPtr->SetColor( ConvertSRGBToLinear( m_boardAdapter.m_CopperColor ) );
|
||||
|
||||
m_objectContainer.Add( objPtr );
|
||||
|
||||
const float holeInnerRadius = radiusBUI * unitScale;
|
||||
const float frontSurface = topZ + frontDepth;
|
||||
const float backSurface = botZ - backDepth;
|
||||
|
||||
const PAD_DRILL_POST_MACHINING_MODE frontMode =
|
||||
aVia->GetFrontPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
|
||||
const float frontRadius = 0.5f * aVia->GetFrontPostMachiningSize() * unitScale;
|
||||
|
||||
if( frontDepth > 0.0f && frontRadius > holeInnerRadius )
|
||||
{
|
||||
if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
addCounterborePlating( *aVia, center, frontRadius, frontDepth, frontSurface, true );
|
||||
}
|
||||
else if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
addCountersinkPlating( center, frontRadius, holeInnerRadius, frontSurface, frontDepth,
|
||||
true );
|
||||
}
|
||||
}
|
||||
|
||||
const PAD_DRILL_POST_MACHINING_MODE backMode =
|
||||
aVia->GetBackPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
|
||||
const float backRadius = 0.5f * aVia->GetBackPostMachiningSize() * unitScale;
|
||||
|
||||
if( backDepth > 0.0f && backRadius > holeInnerRadius )
|
||||
{
|
||||
if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
addCounterborePlating( *aVia, center, backRadius, backDepth, backSurface, false );
|
||||
}
|
||||
else if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
addCountersinkPlating( center, backRadius, holeInnerRadius, backSurface, backDepth,
|
||||
false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1016,28 +1644,42 @@ void RENDER_3D_RAYTRACE_BASE::insertHole( const PAD* aPad )
|
||||
SFVEC3F objColor = m_boardAdapter.m_CopperColor;
|
||||
const VECTOR2I drillsize = aPad->GetDrillSize();
|
||||
const bool hasHole = drillsize.x && drillsize.y;
|
||||
const float unitScale = m_boardAdapter.BiuTo3dUnits();
|
||||
const bool isRoundHole = drillsize.x == drillsize.y;
|
||||
SFVEC2F holeCenter = SFVEC2F( 0.0f, 0.0f );
|
||||
float holeInnerRadius = 0.0f;
|
||||
|
||||
if( !hasHole )
|
||||
return;
|
||||
|
||||
CONST_LIST_OBJECT2D antiOutlineIntersectionList;
|
||||
|
||||
float frontDepth = 0.0f;
|
||||
float backDepth = 0.0f;
|
||||
|
||||
if( aPad->GetFrontPostMachining() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
|
||||
frontDepth = aPad->GetFrontPostMachiningDepth() * m_boardAdapter.BiuTo3dUnits();
|
||||
|
||||
if( aPad->GetBackPostMachining() != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
|
||||
backDepth = aPad->GetBackPostMachiningDepth() * m_boardAdapter.BiuTo3dUnits();
|
||||
|
||||
const float topZ = m_boardAdapter.GetLayerBottomZPos( F_Cu )
|
||||
+ m_boardAdapter.GetFrontCopperThickness() * 0.99f;
|
||||
+ m_boardAdapter.GetFrontCopperThickness() * 0.99f - frontDepth;
|
||||
|
||||
const float botZ = m_boardAdapter.GetLayerBottomZPos( B_Cu )
|
||||
- m_boardAdapter.GetBackCopperThickness() * 0.99f;
|
||||
- m_boardAdapter.GetBackCopperThickness() * 0.99f + backDepth;
|
||||
|
||||
if( drillsize.x == drillsize.y ) // usual round hole
|
||||
if( isRoundHole ) // usual round hole
|
||||
{
|
||||
SFVEC2F center = SFVEC2F( aPad->GetPosition().x * m_boardAdapter.BiuTo3dUnits(),
|
||||
-aPad->GetPosition().y * m_boardAdapter.BiuTo3dUnits() );
|
||||
holeCenter = SFVEC2F( aPad->GetPosition().x * unitScale,
|
||||
-aPad->GetPosition().y * unitScale );
|
||||
|
||||
int innerRadius = drillsize.x / 2;
|
||||
int outerRadius = innerRadius + m_boardAdapter.GetHolePlatingThickness();
|
||||
holeInnerRadius = innerRadius * unitScale;
|
||||
|
||||
RING_2D* ring = new RING_2D( center, innerRadius * m_boardAdapter.BiuTo3dUnits(),
|
||||
outerRadius * m_boardAdapter.BiuTo3dUnits(), *aPad );
|
||||
RING_2D* ring = new RING_2D( holeCenter, innerRadius * unitScale,
|
||||
outerRadius * unitScale, *aPad );
|
||||
|
||||
m_containerWithObjectsToDelete.Add( ring );
|
||||
|
||||
@@ -1053,11 +1695,11 @@ void RENDER_3D_RAYTRACE_BASE::insertHole( const PAD* aPad )
|
||||
|
||||
if( !antiOutlineIntersectionList.empty() )
|
||||
{
|
||||
FILLED_CIRCLE_2D* innerCircle = new FILLED_CIRCLE_2D(
|
||||
center, innerRadius * m_boardAdapter.BiuTo3dUnits(), *aPad );
|
||||
FILLED_CIRCLE_2D* innerCircle = new FILLED_CIRCLE_2D(
|
||||
holeCenter, innerRadius * unitScale, *aPad );
|
||||
|
||||
FILLED_CIRCLE_2D* outterCircle = new FILLED_CIRCLE_2D(
|
||||
center, outerRadius * m_boardAdapter.BiuTo3dUnits(), *aPad );
|
||||
FILLED_CIRCLE_2D* outterCircle = new FILLED_CIRCLE_2D(
|
||||
holeCenter, outerRadius * unitScale, *aPad );
|
||||
std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
|
||||
object2d_B->push_back( innerCircle );
|
||||
|
||||
@@ -1093,19 +1735,19 @@ void RENDER_3D_RAYTRACE_BASE::insertHole( const PAD* aPad )
|
||||
VECTOR2I end = VECTOR2I( aPad->GetPosition() ) - ends_offset;
|
||||
|
||||
ROUND_SEGMENT_2D* innerSeg =
|
||||
new ROUND_SEGMENT_2D( SFVEC2F( start.x * m_boardAdapter.BiuTo3dUnits(),
|
||||
-start.y * m_boardAdapter.BiuTo3dUnits() ),
|
||||
SFVEC2F( end.x * m_boardAdapter.BiuTo3dUnits(),
|
||||
-end.y * m_boardAdapter.BiuTo3dUnits() ),
|
||||
width * m_boardAdapter.BiuTo3dUnits(), *aPad );
|
||||
new ROUND_SEGMENT_2D( SFVEC2F( start.x * unitScale,
|
||||
-start.y * unitScale ),
|
||||
SFVEC2F( end.x * unitScale,
|
||||
-end.y * unitScale ),
|
||||
width * unitScale, *aPad );
|
||||
|
||||
ROUND_SEGMENT_2D* outerSeg =
|
||||
new ROUND_SEGMENT_2D( SFVEC2F( start.x * m_boardAdapter.BiuTo3dUnits(),
|
||||
-start.y * m_boardAdapter.BiuTo3dUnits() ),
|
||||
SFVEC2F( end.x * m_boardAdapter.BiuTo3dUnits(),
|
||||
-end.y * m_boardAdapter.BiuTo3dUnits() ),
|
||||
( width + m_boardAdapter.GetHolePlatingThickness() * 2 )
|
||||
* m_boardAdapter.BiuTo3dUnits(), *aPad );
|
||||
new ROUND_SEGMENT_2D( SFVEC2F( start.x * unitScale,
|
||||
-start.y * unitScale ),
|
||||
SFVEC2F( end.x * unitScale,
|
||||
-end.y * unitScale ),
|
||||
( width + m_boardAdapter.GetHolePlatingThickness() * 2 )
|
||||
* unitScale, *aPad );
|
||||
|
||||
// NOTE: the round segment width is the "diameter", so we double the thickness
|
||||
std::vector<const OBJECT_2D*>* object2d_B = new std::vector<const OBJECT_2D*>();
|
||||
@@ -1177,6 +1819,48 @@ void RENDER_3D_RAYTRACE_BASE::insertHole( const PAD* aPad )
|
||||
m_objectContainer.Add( objPtr );
|
||||
}
|
||||
}
|
||||
|
||||
if( object2d_A && isRoundHole )
|
||||
{
|
||||
const float frontSurface = topZ + frontDepth;
|
||||
const float backSurface = botZ - backDepth;
|
||||
|
||||
const PAD_DRILL_POST_MACHINING_MODE frontMode =
|
||||
aPad->GetFrontPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
|
||||
const float frontRadius = 0.5f * aPad->GetFrontPostMachiningSize() * unitScale;
|
||||
|
||||
if( frontDepth > 0.0f && frontRadius > holeInnerRadius )
|
||||
{
|
||||
if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
addCounterborePlating( *aPad, holeCenter, frontRadius, frontDepth, frontSurface,
|
||||
true );
|
||||
}
|
||||
else if( frontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
addCountersinkPlating( holeCenter, frontRadius, holeInnerRadius, frontSurface,
|
||||
frontDepth, true );
|
||||
}
|
||||
}
|
||||
|
||||
const PAD_DRILL_POST_MACHINING_MODE backMode =
|
||||
aPad->GetBackPostMachining().value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
|
||||
const float backRadius = 0.5f * aPad->GetBackPostMachiningSize() * unitScale;
|
||||
|
||||
if( backDepth > 0.0f && backRadius > holeInnerRadius )
|
||||
{
|
||||
if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
addCounterborePlating( *aPad, holeCenter, backRadius, backDepth, backSurface,
|
||||
false );
|
||||
}
|
||||
else if( backMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
addCountersinkPlating( holeCenter, backRadius, holeInnerRadius, backSurface,
|
||||
backDepth, false );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -110,6 +110,13 @@ protected:
|
||||
void addPadsAndVias();
|
||||
void insertHole( const PCB_VIA* aVia );
|
||||
void insertHole( const PAD* aPad );
|
||||
void addCounterborePlating( const BOARD_ITEM& aSource, const SFVEC2F& aCenter,
|
||||
float aInnerRadius, float aDepth, float aSurfaceZ,
|
||||
bool aIsFront );
|
||||
void addCountersinkPlating( const SFVEC2F& aCenter, float aTopInnerRadius,
|
||||
float aBottomInnerRadius, float aSurfaceZ, float aDepth,
|
||||
bool aIsFront );
|
||||
void backfillPostMachine();
|
||||
void load3DModels( CONTAINER_3D& aDstContainer, bool aSkipMaterialInformation );
|
||||
void addModels( CONTAINER_3D& aDstContainer, const S3DMODEL* a3DModel,
|
||||
const glm::mat4& aModelMatrix, float aFPOpacity,
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file frustum_3d.cpp
|
||||
* @brief Implementation of truncated cone ray intersection.
|
||||
*
|
||||
* The truncated cone is a vertical cone truncated at zMin and zMax with different radii.
|
||||
* Ray-cone intersection uses the implicit equation of a cone.
|
||||
*/
|
||||
|
||||
#include "3d_fastmath.h"
|
||||
#include "frustum_3d.h"
|
||||
#include <cmath>
|
||||
|
||||
|
||||
TRUNCATED_CONE::TRUNCATED_CONE( SFVEC2F aCenterPoint, float aZmin, float aZmax, float aRadiusMin,
|
||||
float aRadiusMax )
|
||||
: OBJECT_3D( OBJECT_3D_TYPE::CYLINDER ) // Reuse CYLINDER type for now
|
||||
{
|
||||
m_center = aCenterPoint;
|
||||
m_zMin = aZmin;
|
||||
m_zMax = aZmax;
|
||||
m_radiusMin = aRadiusMin;
|
||||
m_radiusMax = aRadiusMax;
|
||||
m_height = aZmax - aZmin;
|
||||
m_slope = ( m_height > 0.0f ) ? ( aRadiusMax - aRadiusMin ) / m_height : 0.0f;
|
||||
|
||||
// Bounding box uses the larger radius
|
||||
const float maxRadius = std::max( aRadiusMin, aRadiusMax );
|
||||
m_bbox.Set( SFVEC3F( aCenterPoint.x - maxRadius, aCenterPoint.y - maxRadius, aZmin ),
|
||||
SFVEC3F( aCenterPoint.x + maxRadius, aCenterPoint.y + maxRadius, aZmax ) );
|
||||
m_bbox.ScaleNextUp();
|
||||
m_centroid = m_bbox.GetCenter();
|
||||
}
|
||||
|
||||
|
||||
bool TRUNCATED_CONE::Intersect( const RAY& aRay, HITINFO& aHitInfo ) const
|
||||
{
|
||||
// Ray-cone intersection for a vertical cone with apex on the Z axis.
|
||||
// The cone surface is defined by: (x-cx)^2 + (y-cy)^2 = r(z)^2
|
||||
// where r(z) = radiusMin + slope * (z - zMin)
|
||||
//
|
||||
// Substituting the ray equation P = O + t*D:
|
||||
// (Ox + t*Dx - cx)^2 + (Oy + t*Dy - cy)^2 = (radiusMin + slope*(Oz + t*Dz - zMin))^2
|
||||
|
||||
const double OCx = aRay.m_Origin.x - m_center.x;
|
||||
const double OCy = aRay.m_Origin.y - m_center.y;
|
||||
const double OCz = aRay.m_Origin.z - m_zMin; // Z relative to cone base
|
||||
|
||||
const double Dx = aRay.m_Dir.x;
|
||||
const double Dy = aRay.m_Dir.y;
|
||||
const double Dz = aRay.m_Dir.z;
|
||||
|
||||
const double r0 = m_radiusMin;
|
||||
const double k = m_slope; // dr/dz
|
||||
|
||||
// Expanding the equation gives us a quadratic: a*t^2 + 2*b*t + c = 0
|
||||
// Note: we use 2*b to simplify the quadratic formula
|
||||
const double a = Dx * Dx + Dy * Dy - k * k * Dz * Dz;
|
||||
const double b = OCx * Dx + OCy * Dy - k * ( r0 + k * OCz ) * Dz;
|
||||
const double c = OCx * OCx + OCy * OCy - ( r0 + k * OCz ) * ( r0 + k * OCz );
|
||||
|
||||
const double discriminant = b * b - a * c;
|
||||
|
||||
if( discriminant < 0.0 )
|
||||
return false;
|
||||
|
||||
bool hitResult = false;
|
||||
float tHit = aHitInfo.m_tHit;
|
||||
SFVEC3F hitNormal;
|
||||
|
||||
const double sqrtDisc = std::sqrt( discriminant );
|
||||
|
||||
// Check both roots
|
||||
for( int i = 0; i < 2; ++i )
|
||||
{
|
||||
double t;
|
||||
if( std::abs( a ) < 1e-10 )
|
||||
{
|
||||
// Nearly linear case
|
||||
if( std::abs( b ) < 1e-10 )
|
||||
continue;
|
||||
t = -c / ( 2.0 * b );
|
||||
if( i == 1 )
|
||||
continue; // Only one solution in linear case
|
||||
}
|
||||
else
|
||||
{
|
||||
t = ( -b + ( i == 0 ? -sqrtDisc : sqrtDisc ) ) / a;
|
||||
}
|
||||
|
||||
if( t <= 0.0f || t >= tHit )
|
||||
continue;
|
||||
|
||||
const float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
|
||||
|
||||
if( z < m_zMin || z > m_zMax )
|
||||
continue;
|
||||
|
||||
// Valid hit on cone surface
|
||||
tHit = t;
|
||||
hitResult = true;
|
||||
|
||||
const SFVEC3F hitPoint = aRay.at( t );
|
||||
const float hitRadius = m_radiusMin + m_slope * ( z - m_zMin );
|
||||
|
||||
if( hitRadius > 1e-6f )
|
||||
{
|
||||
// Normal points outward from cone surface
|
||||
// For a cone, the normal has both radial and vertical components
|
||||
const float invR = 1.0f / hitRadius;
|
||||
const float nx = -( hitPoint.x - m_center.x ) * invR;
|
||||
const float ny = -( hitPoint.y - m_center.y ) * invR;
|
||||
// The vertical component depends on the slope
|
||||
const float nz = m_slope / std::sqrt( 1.0f + m_slope * m_slope );
|
||||
|
||||
hitNormal = glm::normalize( SFVEC3F( nx, ny, nz ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
hitNormal = SFVEC3F( 0.0f, 0.0f, -1.0f );
|
||||
}
|
||||
|
||||
break; // Take the first valid hit (closer one)
|
||||
}
|
||||
|
||||
if( hitResult )
|
||||
{
|
||||
aHitInfo.m_tHit = tHit;
|
||||
aHitInfo.m_HitPoint = aRay.at( tHit );
|
||||
aHitInfo.m_HitNormal = hitNormal;
|
||||
|
||||
m_material->Generate( aHitInfo.m_HitNormal, aRay, aHitInfo );
|
||||
|
||||
aHitInfo.pHitObject = this;
|
||||
}
|
||||
|
||||
return hitResult;
|
||||
}
|
||||
|
||||
|
||||
bool TRUNCATED_CONE::IntersectP( const RAY& aRay, float aMaxDistance ) const
|
||||
{
|
||||
const double OCx = aRay.m_Origin.x - m_center.x;
|
||||
const double OCy = aRay.m_Origin.y - m_center.y;
|
||||
const double OCz = aRay.m_Origin.z - m_zMin;
|
||||
|
||||
const double Dx = aRay.m_Dir.x;
|
||||
const double Dy = aRay.m_Dir.y;
|
||||
const double Dz = aRay.m_Dir.z;
|
||||
|
||||
const double r0 = m_radiusMin;
|
||||
const double k = m_slope;
|
||||
|
||||
const double a = Dx * Dx + Dy * Dy - k * k * Dz * Dz;
|
||||
const double b = OCx * Dx + OCy * Dy - k * ( r0 + k * OCz ) * Dz;
|
||||
const double c = OCx * OCx + OCy * OCy - ( r0 + k * OCz ) * ( r0 + k * OCz );
|
||||
|
||||
const double discriminant = b * b - a * c;
|
||||
|
||||
if( discriminant < 0.0 )
|
||||
return false;
|
||||
|
||||
const double sqrtDisc = std::sqrt( discriminant );
|
||||
|
||||
for( int i = 0; i < 2; ++i )
|
||||
{
|
||||
double t;
|
||||
if( std::abs( a ) < 1e-10 )
|
||||
{
|
||||
if( std::abs( b ) < 1e-10 )
|
||||
continue;
|
||||
t = -c / ( 2.0 * b );
|
||||
if( i == 1 )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
t = ( -b + ( i == 0 ? -sqrtDisc : sqrtDisc ) ) / a;
|
||||
}
|
||||
|
||||
if( t <= 0.0f || t >= aMaxDistance )
|
||||
continue;
|
||||
|
||||
const float z = aRay.m_Origin.z + t * aRay.m_Dir.z;
|
||||
|
||||
if( z >= m_zMin && z <= m_zMax )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool TRUNCATED_CONE::Intersects( const BBOX_3D& aBBox ) const
|
||||
{
|
||||
// Simple bounding box check
|
||||
return m_bbox.Intersects( aBBox );
|
||||
}
|
||||
|
||||
|
||||
SFVEC3F TRUNCATED_CONE::GetDiffuseColor( const HITINFO& /* aHitInfo */ ) const
|
||||
{
|
||||
return m_diffusecolor;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file frustum_3d.h
|
||||
* @brief A truncated cone for raytracing, used for countersink visualization.
|
||||
*/
|
||||
|
||||
#ifndef _TRUNCATED_CONE_3D_H_
|
||||
#define _TRUNCATED_CONE_3D_H_
|
||||
|
||||
#include "object_3d.h"
|
||||
|
||||
/**
|
||||
* A vertical truncated cone with different radii at top and bottom.
|
||||
* Used for visualizing countersink hole shapes.
|
||||
*/
|
||||
class TRUNCATED_CONE : public OBJECT_3D
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @param aCenterPoint = position of the vertical cone axis in the XY plane
|
||||
* @param aZmin = bottom position (Z axis)
|
||||
* @param aZmax = top position (Z axis)
|
||||
* @param aRadiusMin = radius at aZmin
|
||||
* @param aRadiusMax = radius at aZmax
|
||||
*/
|
||||
TRUNCATED_CONE( SFVEC2F aCenterPoint, float aZmin, float aZmax, float aRadiusMin,
|
||||
float aRadiusMax );
|
||||
|
||||
void SetColor( SFVEC3F aObjColor ) { m_diffusecolor = aObjColor; }
|
||||
|
||||
bool Intersect( const RAY& aRay, HITINFO& aHitInfo ) const override;
|
||||
bool IntersectP( const RAY& aRay, float aMaxDistance ) const override;
|
||||
bool Intersects( const BBOX_3D& aBBox ) const override;
|
||||
SFVEC3F GetDiffuseColor( const HITINFO& aHitInfo ) const override;
|
||||
|
||||
private:
|
||||
SFVEC2F m_center;
|
||||
float m_radiusMin; // Radius at Zmin
|
||||
float m_radiusMax; // Radius at Zmax
|
||||
float m_zMin;
|
||||
float m_zMax;
|
||||
float m_height; // zMax - zMin
|
||||
float m_slope; // (radiusMax - radiusMin) / height
|
||||
SFVEC3F m_diffusecolor;
|
||||
};
|
||||
|
||||
#endif // _TRUNCATED_CONE_3D_H_
|
||||
@@ -66,6 +66,7 @@ set(3D-VIEWER_SRCS
|
||||
${DIR_RAY_3D}/bbox_3d_ray.cpp
|
||||
${DIR_RAY_3D}/cylinder_3d.cpp
|
||||
${DIR_RAY_3D}/dummy_block_3d.cpp
|
||||
${DIR_RAY_3D}/frustum_3d.cpp
|
||||
${DIR_RAY_3D}/layer_item_3d.cpp
|
||||
${DIR_RAY_3D}/object_3d.cpp
|
||||
${DIR_RAY_3D}/plane_3d.cpp
|
||||
|
||||
@@ -368,6 +368,14 @@ enum ViaDrillFillingMode
|
||||
VDFM_FROM_DESIGN_RULES = 3;
|
||||
}
|
||||
|
||||
enum ViaDrillPostMachiningMode
|
||||
{
|
||||
VDPM_UNKNOWN = 0;
|
||||
VDPM_NOT_POST_MACHINED = 1;
|
||||
VDPM_COUNTERBORE = 2;
|
||||
VDPM_COUNTERSINK = 3;
|
||||
}
|
||||
|
||||
message DrillProperties
|
||||
{
|
||||
// Lowest (closest to F_Cu) layer this drill exists on.
|
||||
@@ -385,6 +393,15 @@ message DrillProperties
|
||||
ViaDrillCappingMode capped = 5;
|
||||
|
||||
ViaDrillFillingMode filled = 6;
|
||||
|
||||
}
|
||||
|
||||
message PostMachiningProperties
|
||||
{
|
||||
ViaDrillPostMachiningMode mode = 1;
|
||||
int32 size = 2;
|
||||
int32 depth = 3;
|
||||
int32 angle = 4;
|
||||
}
|
||||
|
||||
// A pad stack definition for a multilayer pad or via.
|
||||
@@ -415,6 +432,15 @@ message PadStack
|
||||
|
||||
// Controls for how copper zones connect to the padstack
|
||||
ZoneConnectionSettings zone_settings = 9;
|
||||
|
||||
// Optional secondary drill hit for backdrilling
|
||||
DrillProperties secondary_drill = 10;
|
||||
|
||||
// Optional tertiary drill hit for backdrilling
|
||||
DrillProperties tertiary_drill = 11;
|
||||
|
||||
PostMachiningProperties front_post_machining = 12;
|
||||
PostMachiningProperties back_post_machining = 13;
|
||||
}
|
||||
|
||||
enum ViaType
|
||||
|
||||
@@ -264,6 +264,10 @@ std::string GBR_APERTURE_METADATA::FormatAttribute( GBR_APERTURE_ATTRIB aAttribu
|
||||
attribute_string = "TA.AperFunction,ViaDrill";
|
||||
break;
|
||||
|
||||
case GBR_APERTURE_ATTRIB_BACKDRILL:
|
||||
attribute_string = "TA.AperFunction,BackDrill";
|
||||
break;
|
||||
|
||||
case GBR_APERTURE_ATTRIB_CMP_DRILL: // print info associated to a component
|
||||
// round pad hole in drill files
|
||||
attribute_string = "TA.AperFunction,ComponentDrill";
|
||||
|
||||
@@ -48,6 +48,7 @@ attr
|
||||
autoplace_cost90
|
||||
autoplace_cost180
|
||||
aux_axis_origin
|
||||
backdrill
|
||||
back
|
||||
barcode
|
||||
best_length_ratio
|
||||
@@ -92,6 +93,8 @@ outline
|
||||
convexhull
|
||||
copper_line_width
|
||||
copper_text_dims
|
||||
counterbore
|
||||
countersink
|
||||
courtyard_line_width
|
||||
creepage
|
||||
data
|
||||
@@ -286,6 +289,9 @@ plus
|
||||
point
|
||||
polygon
|
||||
portrait
|
||||
post_machining
|
||||
front_post_machining
|
||||
back_post_machining
|
||||
precision
|
||||
prefix
|
||||
primitives
|
||||
@@ -345,6 +351,7 @@ teardrop
|
||||
teardrops
|
||||
tedit
|
||||
tenting
|
||||
tertiary_drill
|
||||
covering
|
||||
plugging
|
||||
filling
|
||||
@@ -416,3 +423,4 @@ zone_layer_connections
|
||||
zone_type
|
||||
zone_defaults
|
||||
zones
|
||||
depth
|
||||
|
||||
@@ -609,6 +609,10 @@ wxString PGPROPERTY_ANGLE::ValueToString( wxVariant& aVariant, int aArgFlags ) c
|
||||
static_cast<EDA_ANGLE_VARIANT_DATA*>( aVariant.GetData() )->Write( ret );
|
||||
return ret;
|
||||
}
|
||||
else if( aVariant.GetType() == wxPG_VARIANT_TYPE_LONG )
|
||||
{
|
||||
return wxString::Format( wxS( "%g\u00B0" ), (double) aVariant.GetLong() / m_scale );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxCHECK_MSG( false, wxEmptyString, wxS( "Unexpected variant type in PGPROPERTY_ANGLE" ) );
|
||||
|
||||
@@ -141,8 +141,14 @@ public:
|
||||
/// this is similar to GBR_APERTURE_ATTRIB_COMPONENTPAD with optional PressFit field
|
||||
GBR_APERTURE_ATTRIB_PRESSFITDRILL,
|
||||
|
||||
GBR_APERTURE_ATTRIB_VIADRILL, ///< Aperture used for via holes in drill files.
|
||||
GBR_APERTURE_ATTRIB_CMP_DRILL, ///< Aperture used for pad holes in drill files.
|
||||
///< Aperture used for via holes in drill files.
|
||||
GBR_APERTURE_ATTRIB_VIADRILL,
|
||||
|
||||
///< Aperture used for backdrill holes in drill files.
|
||||
GBR_APERTURE_ATTRIB_BACKDRILL,
|
||||
|
||||
///< Aperture used for pad holes in drill files.
|
||||
GBR_APERTURE_ATTRIB_CMP_DRILL,
|
||||
|
||||
/// Aperture used for pads oblong holes in drill files.
|
||||
GBR_APERTURE_ATTRIB_CMP_OBLONG_DRILL,
|
||||
|
||||
@@ -372,6 +372,7 @@ set( PCBNEW_CLASS_SRCS
|
||||
load_select_footprint.cpp
|
||||
menubar_footprint_editor.cpp
|
||||
menubar_pcb_editor.cpp
|
||||
padstack.cpp
|
||||
pcb_base_edit_frame.cpp
|
||||
pcb_design_block_utils.cpp
|
||||
pcb_layer_box_selector.cpp
|
||||
|
||||
@@ -757,6 +757,10 @@ void CN_VISITOR::checkZoneItemConnection( CN_ZONE_LAYER* aZoneLayer, CN_ITEM* aI
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't connect zones to pads on backdrilled or post-machined layers
|
||||
if( pad->IsBackdrilledOrPostMachined( layer ) )
|
||||
return;
|
||||
}
|
||||
else if( item->Type() == PCB_VIA_T )
|
||||
{
|
||||
@@ -767,6 +771,10 @@ void CN_VISITOR::checkZoneItemConnection( CN_ZONE_LAYER* aZoneLayer, CN_ITEM* aI
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't connect zones to vias on backdrilled or post-machined layers
|
||||
if( via->IsBackdrilledOrPostMachined( layer ) )
|
||||
return;
|
||||
}
|
||||
|
||||
for( int i = 0; i < aItem->AnchorCount(); ++i )
|
||||
|
||||
@@ -48,10 +48,12 @@
|
||||
#include <settings/color_settings.h>
|
||||
#include <view/view_controls.h>
|
||||
#include <widgets/net_selector.h>
|
||||
#include <pcb_layer_box_selector.h>
|
||||
#include <tool/tool_manager.h>
|
||||
#include <tools/pad_tool.h>
|
||||
#include <advanced_config.h> // for pad property feature management
|
||||
#include <wx/choicdlg.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
|
||||
int DIALOG_PAD_PROPERTIES::m_page = 0; // remember the last open page during session
|
||||
@@ -146,7 +148,13 @@ DIALOG_PAD_PROPERTIES::DIALOG_PAD_PROPERTIES( PCB_BASE_FRAME* aParent, PAD* aPad
|
||||
m_spokeAngle( aParent, m_spokeAngleLabel, m_spokeAngleCtrl, m_spokeAngleUnits ),
|
||||
m_pad_orientation( aParent, m_PadOrientText, m_cb_padrotation, m_orientationUnits ),
|
||||
m_teardropMaxLenSetting( aParent, m_stMaxLen, m_tcTdMaxLen, m_stMaxLenUnits ),
|
||||
m_teardropMaxHeightSetting( aParent, m_stTdMaxSize, m_tcMaxHeight, m_stMaxHeightUnits )
|
||||
m_teardropMaxHeightSetting( aParent, m_stTdMaxSize, m_tcMaxHeight, m_stMaxHeightUnits ),
|
||||
m_topPostMachineSize1Binder( aParent, m_topPostMachineSize1Label, m_topPostmachineSize1, m_topPostMachineSize1Units ),
|
||||
m_topPostMachineSize2Binder( aParent, m_topPostMachineSize2Label, m_topPostMachineSize2, m_topPostMachineSize2Units ),
|
||||
m_bottomPostMachineSize1Binder( aParent, m_bottomPostMachineSize1Label, m_bottomPostMachineSize1, m_bottomPostMachineSize1Units ),
|
||||
m_bottomPostMachineSize2Binder( aParent, m_bottomPostMachineSize2Label, m_bottomPostMachineSize2, m_bottomPostMachineSize2Units ),
|
||||
m_backDrillTopSizeBinder( aParent, m_backDrillTopSizeLabel, m_backDrillTopSize, m_backDrillTopSizeUnits ),
|
||||
m_backDrillBottomSizeBinder( aParent, m_backDrillBottomSizeLabel, m_backDrillBottomSize, m_backDrillBottomSizeUnits )
|
||||
{
|
||||
SetName( PAD_PROPERTIES_DLG_NAME );
|
||||
m_isFpEditor = aParent->GetFrameType() == FRAME_FOOTPRINT_EDITOR;
|
||||
@@ -817,6 +825,87 @@ void DIALOG_PAD_PROPERTIES::initValues()
|
||||
else
|
||||
m_holeShapeCtrl->SetSelection( 1 );
|
||||
|
||||
// Backdrill properties
|
||||
const PADSTACK::DRILL_PROPS& secondaryDrill = m_previewPad->Padstack().SecondaryDrill();
|
||||
const PADSTACK::DRILL_PROPS& tertiaryDrill = m_previewPad->Padstack().TertiaryDrill();
|
||||
bool hasBackdrill = secondaryDrill.start != UNDEFINED_LAYER;
|
||||
bool hasTertiaryDrill = tertiaryDrill.start != UNDEFINED_LAYER;
|
||||
|
||||
int selection = hasBackdrill
|
||||
? ( hasTertiaryDrill ? 3 : 2 )
|
||||
: ( hasTertiaryDrill ? 1 : 0 );
|
||||
m_backDrillChoice->SetSelection( selection );
|
||||
|
||||
if( !hasBackdrill )
|
||||
{
|
||||
m_backDrillBottomSizeBinder.SetValue( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_backDrillBottomSizeBinder.SetValue( secondaryDrill.size.x );
|
||||
|
||||
for( unsigned int i = 0; i < m_backDrillBottomLayer->GetCount(); ++i )
|
||||
{
|
||||
if( (PCB_LAYER_ID)(intptr_t)m_backDrillBottomLayer->GetClientData( i ) == secondaryDrill.end )
|
||||
{
|
||||
m_backDrillBottomLayer->SetSelection( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !hasTertiaryDrill )
|
||||
{
|
||||
m_backDrillTopSizeBinder.SetValue( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_backDrillTopSizeBinder.SetValue( tertiaryDrill.size.x );
|
||||
|
||||
for( unsigned int i = 0; i < m_backDrillTopLayer->GetCount(); ++i )
|
||||
{
|
||||
if( (PCB_LAYER_ID)(intptr_t)m_backDrillTopLayer->GetClientData( i ) == tertiaryDrill.end )
|
||||
{
|
||||
m_backDrillTopLayer->SetSelection( i );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Post machining
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPostMachining = m_previewPad->Padstack().FrontPostMachining();
|
||||
|
||||
if( frontPostMachining.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
m_topPostMachining->SetSelection( 2 );
|
||||
else if( frontPostMachining.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
m_topPostMachining->SetSelection( 1 );
|
||||
else
|
||||
m_topPostMachining->SetSelection( 0 );
|
||||
|
||||
m_topPostMachineSize1Binder.SetValue( frontPostMachining.size );
|
||||
|
||||
if( frontPostMachining.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
m_topPostMachineSize2Binder.SetValue( frontPostMachining.angle );
|
||||
else
|
||||
m_topPostMachineSize2Binder.SetValue( frontPostMachining.depth );
|
||||
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPostMachining = m_previewPad->Padstack().BackPostMachining();
|
||||
|
||||
if( backPostMachining.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
m_bottomPostMachining->SetSelection( 2 );
|
||||
else if( backPostMachining.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
m_bottomPostMachining->SetSelection( 1 );
|
||||
else
|
||||
m_bottomPostMachining->SetSelection( 0 );
|
||||
|
||||
m_bottomPostMachineSize1Binder.SetValue( backPostMachining.size );
|
||||
|
||||
if( backPostMachining.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
m_bottomPostMachineSize2Binder.SetValue( backPostMachining.angle );
|
||||
else
|
||||
m_bottomPostMachineSize2Binder.SetValue( backPostMachining.depth );
|
||||
|
||||
|
||||
updatePadLayersList( m_previewPad->GetLayerSet(), m_previewPad->GetRemoveUnconnected(),
|
||||
m_previewPad->GetKeepTopBottom() );
|
||||
|
||||
@@ -824,6 +913,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
|
||||
wxCommandEvent cmd_event;
|
||||
OnPadShapeSelection( cmd_event );
|
||||
OnOffsetCheckbox( cmd_event );
|
||||
updateHoleControls();
|
||||
|
||||
// Restore thermal spoke angle to its initial value, because it can be modified
|
||||
// by the call to OnPadShapeSelection()
|
||||
@@ -1111,6 +1201,23 @@ void DIALOG_PAD_PROPERTIES::OnPadShapeSelection( wxCommandEvent& event )
|
||||
|
||||
void DIALOG_PAD_PROPERTIES::OnDrillShapeSelected( wxCommandEvent& event )
|
||||
{
|
||||
if( m_holeShapeCtrl->GetSelection() != CHOICE_SHAPE_CIRCLE )
|
||||
{
|
||||
bool hasBackdrill = ( m_backDrillChoice->GetSelection() != 0 );
|
||||
bool hasTopPost = ( m_topPostMachining->GetSelection() != 0 );
|
||||
bool hasBottomPost = ( m_bottomPostMachining->GetSelection() != 0 );
|
||||
|
||||
if( hasBackdrill || hasTopPost || hasBottomPost )
|
||||
{
|
||||
if( wxMessageBox( _( "Switching to non-circular hole will disable backdrills and post-machining. Continue?" ),
|
||||
_( "Warning" ), wxOK | wxCANCEL | wxICON_WARNING, this ) != wxOK )
|
||||
{
|
||||
m_holeShapeCtrl->SetSelection( CHOICE_SHAPE_CIRCLE );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
transferDataToPad( m_previewPad );
|
||||
updateHoleControls();
|
||||
redraw();
|
||||
@@ -1164,6 +1271,19 @@ void DIALOG_PAD_PROPERTIES::UpdateLayersDropdown()
|
||||
m_rbCopperLayersSel->Append( _( "None" ) );
|
||||
break;
|
||||
}
|
||||
|
||||
m_backDrillTopLayer->Clear();
|
||||
m_backDrillBottomLayer->Clear();
|
||||
|
||||
for( PCB_LAYER_ID layerId : m_board->GetEnabledLayers().UIOrder() )
|
||||
{
|
||||
if( IsCopperLayer( layerId ) )
|
||||
{
|
||||
wxString layerName = m_board->GetLayerName( layerId );
|
||||
m_backDrillTopLayer->Append( layerName, wxBitmapBundle(), (void*)(intptr_t)layerId );
|
||||
m_backDrillBottomLayer->Append( layerName, wxBitmapBundle(), (void*)(intptr_t)layerId );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1701,7 +1821,9 @@ PAD_PROP DIALOG_PAD_PROPERTIES::getSelectedProperty()
|
||||
|
||||
void DIALOG_PAD_PROPERTIES::updateHoleControls()
|
||||
{
|
||||
if( m_holeShapeCtrl->GetSelection() == CHOICE_SHAPE_CIRCLE )
|
||||
bool isRound = ( m_holeShapeCtrl->GetSelection() == CHOICE_SHAPE_CIRCLE );
|
||||
|
||||
if( isRound )
|
||||
{
|
||||
m_holeXLabel->SetLabel( _( "Diameter:" ) );
|
||||
m_holeY.Show( false );
|
||||
@@ -1713,6 +1835,41 @@ void DIALOG_PAD_PROPERTIES::updateHoleControls()
|
||||
}
|
||||
|
||||
m_holeXLabel->GetParent()->Layout();
|
||||
|
||||
if( !isRound )
|
||||
{
|
||||
// Disable all
|
||||
m_backDrillChoice->Enable( false );
|
||||
m_backDrillTopLayer->Enable( false );
|
||||
m_backDrillTopLayerLabel->Enable( false );
|
||||
m_backDrillBottomLayer->Enable( false );
|
||||
m_backDrillBottomLayerLabel->Enable( false );
|
||||
|
||||
m_topPostMachining->Enable( false );
|
||||
m_topPostMachineSize1Binder.Enable( false );
|
||||
m_topPostMachineSize2Binder.Enable( false );
|
||||
m_topPostMachineSize1Label->Enable( false );
|
||||
m_topPostMachineSize2Label->Enable( false );
|
||||
|
||||
m_bottomPostMachining->Enable( false );
|
||||
m_bottomPostMachineSize1Binder.Enable( false );
|
||||
m_bottomPostMachineSize2Binder.Enable( false );
|
||||
m_bottomPostMachineSize1Label->Enable( false );
|
||||
m_bottomPostMachineSize2Label->Enable( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enable main choices
|
||||
m_backDrillChoice->Enable( true );
|
||||
m_topPostMachining->Enable( true );
|
||||
m_bottomPostMachining->Enable( true );
|
||||
|
||||
// Update sub-controls based on selection
|
||||
wxCommandEvent dummy;
|
||||
onBackDrillChoice( dummy );
|
||||
onTopPostMachining( dummy );
|
||||
onBottomPostMachining( dummy );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2111,10 +2268,167 @@ bool DIALOG_PAD_PROPERTIES::transferDataToPad( PAD* aPad )
|
||||
|
||||
aPad->SetLayerSet( padLayerMask );
|
||||
|
||||
// Save backdrill properties
|
||||
PADSTACK::DRILL_PROPS secondaryDrill;
|
||||
secondaryDrill.size = VECTOR2I( m_backDrillBottomSizeBinder.GetIntValue(),
|
||||
m_backDrillBottomSizeBinder.GetIntValue() );
|
||||
secondaryDrill.shape = PAD_DRILL_SHAPE::CIRCLE;
|
||||
|
||||
PADSTACK::DRILL_PROPS tertiaryDrill;
|
||||
tertiaryDrill.size = VECTOR2I( m_backDrillTopSizeBinder.GetIntValue(),
|
||||
m_backDrillTopSizeBinder.GetIntValue() );
|
||||
tertiaryDrill.shape = PAD_DRILL_SHAPE::CIRCLE;
|
||||
|
||||
if( !m_backDrillChoice->GetSelection() )
|
||||
{
|
||||
secondaryDrill.start = UNDEFINED_LAYER;
|
||||
secondaryDrill.end = UNDEFINED_LAYER;
|
||||
}
|
||||
|
||||
if( m_backDrillChoice->GetSelection() & 1 ) // Bottom
|
||||
{
|
||||
secondaryDrill.start = B_Cu;
|
||||
|
||||
if( m_backDrillBottomLayer->GetSelection() != wxNOT_FOUND )
|
||||
secondaryDrill.end = (PCB_LAYER_ID)(intptr_t)m_backDrillBottomLayer->GetClientData( m_backDrillBottomLayer->GetSelection() );
|
||||
else
|
||||
secondaryDrill.end = UNDEFINED_LAYER;
|
||||
}
|
||||
|
||||
if( m_backDrillChoice->GetSelection() & 2 ) // Top
|
||||
{
|
||||
tertiaryDrill.start = F_Cu;
|
||||
|
||||
if( m_backDrillTopLayer->GetSelection() != wxNOT_FOUND )
|
||||
tertiaryDrill.end = (PCB_LAYER_ID)(intptr_t)m_backDrillTopLayer->GetClientData( m_backDrillTopLayer->GetSelection() );
|
||||
else
|
||||
tertiaryDrill.end = UNDEFINED_LAYER;
|
||||
}
|
||||
|
||||
aPad->Padstack().SecondaryDrill() = secondaryDrill;
|
||||
aPad->Padstack().TertiaryDrill() = tertiaryDrill;
|
||||
|
||||
// Front Post Machining
|
||||
PADSTACK::POST_MACHINING_PROPS frontPostMachining;
|
||||
|
||||
switch( m_topPostMachining->GetSelection() )
|
||||
{
|
||||
case 1: frontPostMachining.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK; break;
|
||||
case 2: frontPostMachining.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE; break;
|
||||
default: frontPostMachining.mode = PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED; break;
|
||||
}
|
||||
|
||||
frontPostMachining.size = m_topPostMachineSize1Binder.GetIntValue();
|
||||
|
||||
if( frontPostMachining.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
frontPostMachining.angle = m_topPostMachineSize2Binder.GetIntValue();
|
||||
else
|
||||
frontPostMachining.depth = m_topPostMachineSize2Binder.GetIntValue();
|
||||
|
||||
aPad->Padstack().FrontPostMachining() = frontPostMachining;
|
||||
|
||||
// Back Post Machining
|
||||
PADSTACK::POST_MACHINING_PROPS backPostMachining;
|
||||
|
||||
switch( m_bottomPostMachining->GetSelection() )
|
||||
{
|
||||
case 1: backPostMachining.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK; break;
|
||||
case 2: backPostMachining.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE; break;
|
||||
default: backPostMachining.mode = PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED; break;
|
||||
}
|
||||
|
||||
backPostMachining.size = m_bottomPostMachineSize1Binder.GetIntValue();
|
||||
|
||||
if( backPostMachining.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
backPostMachining.angle = m_bottomPostMachineSize2Binder.GetIntValue();
|
||||
else
|
||||
backPostMachining.depth = m_bottomPostMachineSize2Binder.GetIntValue();
|
||||
|
||||
aPad->Padstack().BackPostMachining() = backPostMachining;
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void DIALOG_PAD_PROPERTIES::onBackDrillChoice( wxCommandEvent& event )
|
||||
{
|
||||
int selection = m_backDrillChoice->GetSelection();
|
||||
// 0: None, 1: Bottom, 2: Top, 3: Both
|
||||
|
||||
bool enableTop = ( selection == 2 || selection == 3 );
|
||||
bool enableBottom = ( selection == 1 || selection == 3 );
|
||||
|
||||
m_backDrillTopSizeBinder.Enable( enableTop );
|
||||
m_backDrillTopLayer->Enable( enableTop );
|
||||
m_backDrillTopLayerLabel->Enable( enableTop );
|
||||
|
||||
m_backDrillBottomSizeBinder.Enable( enableBottom );
|
||||
m_backDrillBottomLayer->Enable( enableBottom );
|
||||
m_backDrillBottomLayerLabel->Enable( enableBottom );
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_PAD_PROPERTIES::onTopPostMachining( wxCommandEvent& event )
|
||||
{
|
||||
int selection = m_topPostMachining->GetSelection();
|
||||
// 0: None, 1: Countersink, 2: Counterbore
|
||||
|
||||
bool enable = ( selection != 0 );
|
||||
m_topPostMachineSize1Binder.Enable( enable );
|
||||
m_topPostMachineSize2Binder.Enable( enable );
|
||||
m_topPostMachineSize1Label->Enable( enable );
|
||||
m_topPostMachineSize2Label->Enable( enable );
|
||||
|
||||
if( selection == 1 ) // Countersink
|
||||
{
|
||||
m_topPostMachineSize2Label->SetLabel( _( "Angle:" ) );
|
||||
m_topPostMachineSize2Units->SetLabel( _( "deg" ) );
|
||||
|
||||
if( m_topPostMachineSize2Binder.IsIndeterminate() || m_topPostMachineSize2Binder.GetDoubleValue() == 0 )
|
||||
{
|
||||
m_topPostMachineSize2Binder.SetValue( "82" );
|
||||
}
|
||||
}
|
||||
else if( selection == 2 ) // Counterbore
|
||||
{
|
||||
m_topPostMachineSize2Label->SetLabel( _( "Depth:" ) );
|
||||
m_topPostMachineSize2Units->SetLabel( EDA_UNIT_UTILS::GetLabel( m_parent->GetUserUnits() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_PAD_PROPERTIES::onBottomPostMachining( wxCommandEvent& event )
|
||||
{
|
||||
int selection = m_bottomPostMachining->GetSelection();
|
||||
// 0: None, 1: Countersink, 2: Counterbore
|
||||
|
||||
bool enable = ( selection != 0 );
|
||||
m_bottomPostMachineSize1Binder.Enable( enable );
|
||||
m_bottomPostMachineSize2Binder.Enable( enable );
|
||||
m_bottomPostMachineSize1Label->Enable( enable );
|
||||
m_bottomPostMachineSize2Label->Enable( enable );
|
||||
|
||||
if( selection == 1 ) // Countersink
|
||||
{
|
||||
m_bottomPostMachineSize2Label->SetLabel( _( "Angle:" ) );
|
||||
m_bottomPostMachineSize2Units->SetLabel( _( "deg" ) );
|
||||
|
||||
if( m_bottomPostMachineSize2Binder.IsIndeterminate() || m_bottomPostMachineSize2Binder.GetDoubleValue() == 0 )
|
||||
{
|
||||
m_bottomPostMachineSize2Binder.SetValue( "82" );
|
||||
}
|
||||
}
|
||||
else if( selection == 2 ) // Counterbore
|
||||
{
|
||||
m_bottomPostMachineSize2Label->SetLabel( _( "Depth:" ) );
|
||||
m_bottomPostMachineSize2Units->SetLabel( EDA_UNIT_UTILS::GetLabel( m_parent->GetUserUnits() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_PAD_PROPERTIES::OnOffsetCheckbox( wxCommandEvent& event )
|
||||
{
|
||||
if( m_offsetShapeOpt->GetValue() )
|
||||
|
||||
@@ -83,6 +83,10 @@ private:
|
||||
|
||||
void OnUpdateUINonCopperWarning( wxUpdateUIEvent& event ) override;
|
||||
|
||||
void onBackDrillChoice( wxCommandEvent& event ) override;
|
||||
void onTopPostMachining( wxCommandEvent& event ) override;
|
||||
void onBottomPostMachining( wxCommandEvent& event ) override;
|
||||
|
||||
void OnPadShapeSelection( wxCommandEvent& event ) override;
|
||||
void OnDrillShapeSelected( wxCommandEvent& event ) override;
|
||||
void onChangePadMode( wxCommandEvent& event ) override;
|
||||
@@ -182,6 +186,14 @@ private:
|
||||
UNIT_BINDER m_pad_orientation;
|
||||
UNIT_BINDER m_teardropMaxLenSetting;
|
||||
UNIT_BINDER m_teardropMaxHeightSetting;
|
||||
|
||||
UNIT_BINDER m_topPostMachineSize1Binder;
|
||||
UNIT_BINDER m_topPostMachineSize2Binder;
|
||||
UNIT_BINDER m_bottomPostMachineSize1Binder;
|
||||
UNIT_BINDER m_bottomPostMachineSize2Binder;
|
||||
|
||||
UNIT_BINDER m_backDrillTopSizeBinder;
|
||||
UNIT_BINDER m_backDrillBottomSizeBinder;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
|
||||
// C++ code generated with wxFormBuilder (version 4.2.1-115-g11c2dec8-dirty)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
@@ -96,7 +96,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
|
||||
wxString m_cbPadstackModeChoices[] = { _("Normal"), _("Front/Inner/Bottom"), _("Custom") };
|
||||
int m_cbPadstackModeNChoices = sizeof( m_cbPadstackModeChoices ) / sizeof( wxString );
|
||||
m_cbPadstackMode = new wxChoice( m_panelGeneral, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_cbPadstackModeNChoices, m_cbPadstackModeChoices, 0 );
|
||||
m_cbPadstackMode->SetSelection( 1 );
|
||||
m_cbPadstackMode->SetSelection( 2 );
|
||||
m_padstackControls->Add( m_cbPadstackMode, 0, wxALL, 5 );
|
||||
|
||||
|
||||
@@ -530,7 +530,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
|
||||
m_middleBoxSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
wxStaticBoxSizer* m_LayersSizer;
|
||||
m_LayersSizer = new wxStaticBoxSizer( new wxStaticBox( m_panelGeneral, wxID_ANY, wxEmptyString ), wxVERTICAL );
|
||||
m_LayersSizer = new wxStaticBoxSizer( wxVERTICAL, m_panelGeneral, wxEmptyString );
|
||||
|
||||
m_FlippedWarningSizer = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
@@ -626,7 +626,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
|
||||
m_panelGeneral->SetSizer( bGeneralSizer );
|
||||
m_panelGeneral->Layout();
|
||||
bGeneralSizer->Fit( m_panelGeneral );
|
||||
m_notebook->AddPage( m_panelGeneral, _("General"), true );
|
||||
m_notebook->AddPage( m_panelGeneral, _("General"), false );
|
||||
m_connectionsPanel = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
wxBoxSizer* bSizerPanelConnections;
|
||||
bSizerPanelConnections = new wxBoxSizer( wxVERTICAL );
|
||||
@@ -635,7 +635,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
|
||||
bSizerConnectionsMargins = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
wxStaticBoxSizer* bSizerTeardrops;
|
||||
bSizerTeardrops = new wxStaticBoxSizer( new wxStaticBox( m_connectionsPanel, wxID_ANY, _("Teardrops") ), wxVERTICAL );
|
||||
bSizerTeardrops = new wxStaticBoxSizer( wxVERTICAL, m_connectionsPanel, _("Teardrops") );
|
||||
|
||||
m_legacyTeardropsWarning = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
@@ -872,7 +872,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
|
||||
wxBoxSizer* bSizerConnectionsLower;
|
||||
bSizerConnectionsLower = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
m_sbSizerZonesSettings = new wxStaticBoxSizer( new wxStaticBox( m_connectionsPanel, wxID_ANY, _("Connection to Copper Zones") ), wxVERTICAL );
|
||||
m_sbSizerZonesSettings = new wxStaticBoxSizer( wxVERTICAL, m_connectionsPanel, _("Connection to Copper Zones") );
|
||||
|
||||
wxFlexGridSizer* fgSizerCopperZonesOpts;
|
||||
fgSizerCopperZonesOpts = new wxFlexGridSizer( 0, 2, 5, 0 );
|
||||
@@ -907,7 +907,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
|
||||
bSizerConnectionsLower->Add( m_sbSizerZonesSettings, 1, wxALL|wxEXPAND, 5 );
|
||||
|
||||
wxStaticBoxSizer* sbSizerThermalReliefs;
|
||||
sbSizerThermalReliefs = new wxStaticBoxSizer( new wxStaticBox( m_connectionsPanel, wxID_ANY, _("Thermal Relief Overrides") ), wxVERTICAL );
|
||||
sbSizerThermalReliefs = new wxStaticBoxSizer( wxVERTICAL, m_connectionsPanel, _("Thermal Relief Overrides") );
|
||||
|
||||
wxFlexGridSizer* fgSizerThermalReliefs;
|
||||
fgSizerThermalReliefs = new wxFlexGridSizer( 0, 3, 3, 0 );
|
||||
@@ -973,7 +973,7 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
|
||||
bSizerClearance = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
wxStaticBoxSizer* sbClearancesSizer;
|
||||
sbClearancesSizer = new wxStaticBoxSizer( new wxStaticBox( m_localSettingsPanel, wxID_ANY, _("Clearance Overrides") ), wxVERTICAL );
|
||||
sbClearancesSizer = new wxStaticBoxSizer( wxVERTICAL, m_localSettingsPanel, _("Clearance Overrides") );
|
||||
|
||||
wxStaticText* m_staticTextHint;
|
||||
m_staticTextHint = new wxStaticText( sbClearancesSizer->GetStaticBox(), wxID_ANY, _("Leave values blank to use parent footprint or netclass values."), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
@@ -1102,6 +1102,214 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
|
||||
m_localSettingsPanel->Layout();
|
||||
bSizerPanelClearance->Fit( m_localSettingsPanel );
|
||||
m_notebook->AddPage( m_localSettingsPanel, _("Clearance Overrides"), false );
|
||||
m_backDrillPanel = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
|
||||
wxBoxSizer* bSizer45;
|
||||
bSizer45 = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
wxBoxSizer* bSizer46;
|
||||
bSizer46 = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
wxStaticBoxSizer* sbSizer6;
|
||||
sbSizer6 = new wxStaticBoxSizer( wxVERTICAL, m_backDrillPanel, _("Hole post machining") );
|
||||
|
||||
wxBoxSizer* bSizer47;
|
||||
bSizer47 = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
wxFlexGridSizer* fgSizer11;
|
||||
fgSizer11 = new wxFlexGridSizer( 0, 3, 0, 0 );
|
||||
fgSizer11->AddGrowableCol( 1 );
|
||||
fgSizer11->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer11->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
m_topPostMachiningLabel = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("Top:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachiningLabel->Wrap( -1 );
|
||||
fgSizer11->Add( m_topPostMachiningLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
wxString m_topPostMachiningChoices[] = { _("None"), _("Countersink"), _("Counterbore") };
|
||||
int m_topPostMachiningNChoices = sizeof( m_topPostMachiningChoices ) / sizeof( wxString );
|
||||
m_topPostMachining = new wxChoice( sbSizer6->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_topPostMachiningNChoices, m_topPostMachiningChoices, 0 );
|
||||
m_topPostMachining->SetSelection( 0 );
|
||||
fgSizer11->Add( m_topPostMachining, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
fgSizer11->Add( 0, 0, 1, wxEXPAND, 5 );
|
||||
|
||||
m_topPostMachineSize1Label = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("Size:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachineSize1Label->Wrap( -1 );
|
||||
fgSizer11->Add( m_topPostMachineSize1Label, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_topPostmachineSize1 = new wxTextCtrl( sbSizer6->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer11->Add( m_topPostmachineSize1, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_topPostMachineSize1Units = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachineSize1Units->Wrap( -1 );
|
||||
fgSizer11->Add( m_topPostMachineSize1Units, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_topPostMachineSize2Label = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("Angle:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachineSize2Label->Wrap( -1 );
|
||||
fgSizer11->Add( m_topPostMachineSize2Label, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_topPostMachineSize2 = new wxTextCtrl( sbSizer6->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer11->Add( m_topPostMachineSize2, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_topPostMachineSize2Units = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("deg"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachineSize2Units->Wrap( -1 );
|
||||
fgSizer11->Add( m_topPostMachineSize2Units, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
|
||||
bSizer47->Add( fgSizer11, 1, wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizer47->Add( 0, 0, 0, wxEXPAND|wxLEFT|wxRIGHT, 25 );
|
||||
|
||||
wxFlexGridSizer* fgSizer12;
|
||||
fgSizer12 = new wxFlexGridSizer( 0, 3, 0, 0 );
|
||||
fgSizer12->AddGrowableCol( 1 );
|
||||
fgSizer12->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer12->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
m_bottomPostMachiningLabel = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("Bottom:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachiningLabel->Wrap( -1 );
|
||||
fgSizer12->Add( m_bottomPostMachiningLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
wxString m_bottomPostMachiningChoices[] = { _("None"), _("Countersink"), _("Counterbore") };
|
||||
int m_bottomPostMachiningNChoices = sizeof( m_bottomPostMachiningChoices ) / sizeof( wxString );
|
||||
m_bottomPostMachining = new wxChoice( sbSizer6->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_bottomPostMachiningNChoices, m_bottomPostMachiningChoices, 0 );
|
||||
m_bottomPostMachining->SetSelection( 0 );
|
||||
fgSizer12->Add( m_bottomPostMachining, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
fgSizer12->Add( 0, 0, 1, wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_bottomPostMachineSize1Label = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("Size:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachineSize1Label->Wrap( -1 );
|
||||
fgSizer12->Add( m_bottomPostMachineSize1Label, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_bottomPostMachineSize1 = new wxTextCtrl( sbSizer6->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer12->Add( m_bottomPostMachineSize1, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_bottomPostMachineSize1Units = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachineSize1Units->Wrap( -1 );
|
||||
fgSizer12->Add( m_bottomPostMachineSize1Units, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_bottomPostMachineSize2Label = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("Angle:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachineSize2Label->Wrap( -1 );
|
||||
fgSizer12->Add( m_bottomPostMachineSize2Label, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_bottomPostMachineSize2 = new wxTextCtrl( sbSizer6->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer12->Add( m_bottomPostMachineSize2, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_bottomPostMachineSize2Units = new wxStaticText( sbSizer6->GetStaticBox(), wxID_ANY, _("deg"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachineSize2Units->Wrap( -1 );
|
||||
fgSizer12->Add( m_bottomPostMachineSize2Units, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
|
||||
bSizer47->Add( fgSizer12, 1, wxEXPAND, 5 );
|
||||
|
||||
|
||||
sbSizer6->Add( bSizer47, 1, wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizer46->Add( sbSizer6, 0, wxEXPAND, 5 );
|
||||
|
||||
wxStaticBoxSizer* sbSizer7;
|
||||
sbSizer7 = new wxStaticBoxSizer( wxVERTICAL, m_backDrillPanel, _("Backdrill") );
|
||||
|
||||
wxBoxSizer* bSizer48;
|
||||
bSizer48 = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
wxFlexGridSizer* fgSizer13;
|
||||
fgSizer13 = new wxFlexGridSizer( 0, 2, 0, 0 );
|
||||
fgSizer13->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer13->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
wxString m_backDrillChoiceChoices[] = { _("None"), _("Bottom"), _("Top"), _("Both") };
|
||||
int m_backDrillChoiceNChoices = sizeof( m_backDrillChoiceChoices ) / sizeof( wxString );
|
||||
m_backDrillChoice = new wxChoice( sbSizer7->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_backDrillChoiceNChoices, m_backDrillChoiceChoices, 0 );
|
||||
m_backDrillChoice->SetSelection( 0 );
|
||||
fgSizer13->Add( m_backDrillChoice, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizer48->Add( fgSizer13, 1, wxEXPAND, 5 );
|
||||
|
||||
wxFlexGridSizer* fgSizer14;
|
||||
fgSizer14 = new wxFlexGridSizer( 0, 3, 0, 0 );
|
||||
fgSizer14->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer14->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
m_backDrillTopLayerLabel = new wxStaticText( sbSizer7->GetStaticBox(), wxID_ANY, _("Top ending layer:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_backDrillTopLayerLabel->Wrap( -1 );
|
||||
m_backDrillTopLayerLabel->SetToolTip( _("Backdrill will start at the pad's start layer and end at this layer") );
|
||||
|
||||
fgSizer14->Add( m_backDrillTopLayerLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_backDrillTopLayer = new wxBitmapComboBox( sbSizer7->GetStaticBox(), wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
|
||||
m_backDrillTopLayer->SetToolTip( _("Backdrill will start at the pad's start layer and end at this layer") );
|
||||
|
||||
fgSizer14->Add( m_backDrillTopLayer, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
fgSizer14->Add( 0, 0, 1, wxEXPAND, 5 );
|
||||
|
||||
m_backDrillTopSizeLabel = new wxStaticText( sbSizer7->GetStaticBox(), wxID_ANY, _("Size:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_backDrillTopSizeLabel->Wrap( -1 );
|
||||
fgSizer14->Add( m_backDrillTopSizeLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_backDrillTopSize = new wxTextCtrl( sbSizer7->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer14->Add( m_backDrillTopSize, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_backDrillTopSizeUnits = new wxStaticText( sbSizer7->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_backDrillTopSizeUnits->Wrap( -1 );
|
||||
fgSizer14->Add( m_backDrillTopSizeUnits, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
|
||||
bSizer48->Add( fgSizer14, 2, wxEXPAND, 5 );
|
||||
|
||||
wxFlexGridSizer* fgSizer15;
|
||||
fgSizer15 = new wxFlexGridSizer( 0, 3, 0, 0 );
|
||||
fgSizer15->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer15->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
m_backDrillBottomLayerLabel = new wxStaticText( sbSizer7->GetStaticBox(), wxID_ANY, _("Bottom ending layer:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_backDrillBottomLayerLabel->Wrap( -1 );
|
||||
fgSizer15->Add( m_backDrillBottomLayerLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_backDrillBottomLayer = new wxBitmapComboBox( sbSizer7->GetStaticBox(), wxID_ANY, _("dummy"), wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
|
||||
m_backDrillBottomLayer->SetToolTip( _("Backdrill will start at the pad's end layer and end at this layer") );
|
||||
|
||||
fgSizer15->Add( m_backDrillBottomLayer, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
fgSizer15->Add( 0, 0, 1, wxEXPAND, 5 );
|
||||
|
||||
m_backDrillBottomSizeLabel = new wxStaticText( sbSizer7->GetStaticBox(), wxID_ANY, _("Size:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_backDrillBottomSizeLabel->Wrap( -1 );
|
||||
fgSizer15->Add( m_backDrillBottomSizeLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_backDrillBottomSize = new wxTextCtrl( sbSizer7->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer15->Add( m_backDrillBottomSize, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_backDrillBottomSizeUnits = new wxStaticText( sbSizer7->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_backDrillBottomSizeUnits->Wrap( -1 );
|
||||
fgSizer15->Add( m_backDrillBottomSizeUnits, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
|
||||
bSizer48->Add( fgSizer15, 2, wxEXPAND, 5 );
|
||||
|
||||
|
||||
sbSizer7->Add( bSizer48, 1, wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizer46->Add( sbSizer7, 0, wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizer45->Add( bSizer46, 1, wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
m_backDrillPanel->SetSizer( bSizer45 );
|
||||
m_backDrillPanel->Layout();
|
||||
bSizer45->Fit( m_backDrillPanel );
|
||||
m_notebook->AddPage( m_backDrillPanel, _("Backdrill"), true );
|
||||
|
||||
bSizerUpper->Add( m_notebook, 0, wxEXPAND|wxTOP|wxBOTTOM, 12 );
|
||||
|
||||
@@ -1345,6 +1553,9 @@ DIALOG_PAD_PROPERTIES_BASE::DIALOG_PAD_PROPERTIES_BASE( wxWindow* parent, wxWind
|
||||
m_pasteMarginCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnValuesChanged ), NULL, this );
|
||||
m_pasteMarginRatioCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnValuesChanged ), NULL, this );
|
||||
m_nonCopperWarningBook->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnUpdateUINonCopperWarning ), NULL, this );
|
||||
m_topPostMachining->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onTopPostMachining ), NULL, this );
|
||||
m_bottomPostMachining->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onBottomPostMachining ), NULL, this );
|
||||
m_backDrillChoice->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onBackDrillChoice ), NULL, this );
|
||||
m_cbShowPadOutline->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onChangePadMode ), NULL, this );
|
||||
m_sdbSizerCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnCancel ), NULL, this );
|
||||
}
|
||||
@@ -1433,6 +1644,9 @@ DIALOG_PAD_PROPERTIES_BASE::~DIALOG_PAD_PROPERTIES_BASE()
|
||||
m_pasteMarginCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnValuesChanged ), NULL, this );
|
||||
m_pasteMarginRatioCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnValuesChanged ), NULL, this );
|
||||
m_nonCopperWarningBook->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnUpdateUINonCopperWarning ), NULL, this );
|
||||
m_topPostMachining->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onTopPostMachining ), NULL, this );
|
||||
m_bottomPostMachining->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onBottomPostMachining ), NULL, this );
|
||||
m_backDrillChoice->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onBackDrillChoice ), NULL, this );
|
||||
m_cbShowPadOutline->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::onChangePadMode ), NULL, this );
|
||||
m_sdbSizerCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES_BASE::OnCancel ), NULL, this );
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
|
||||
// C++ code generated with wxFormBuilder (version 4.2.1-115-g11c2dec8-dirty)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
@@ -35,6 +35,7 @@ class TEXT_CTRL_EVAL;
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/bmpcbox.h>
|
||||
#include <wx/notebook.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/dialog.h>
|
||||
@@ -239,6 +240,34 @@ class DIALOG_PAD_PROPERTIES_BASE : public DIALOG_SHIM
|
||||
wxStaticText* m_staticTextInfoPaste;
|
||||
wxStaticBitmap* m_nonCopperWarningIcon;
|
||||
wxStaticText* m_nonCopperWarningText;
|
||||
wxPanel* m_backDrillPanel;
|
||||
wxStaticText* m_topPostMachiningLabel;
|
||||
wxChoice* m_topPostMachining;
|
||||
wxStaticText* m_topPostMachineSize1Label;
|
||||
wxTextCtrl* m_topPostmachineSize1;
|
||||
wxStaticText* m_topPostMachineSize1Units;
|
||||
wxStaticText* m_topPostMachineSize2Label;
|
||||
wxTextCtrl* m_topPostMachineSize2;
|
||||
wxStaticText* m_topPostMachineSize2Units;
|
||||
wxStaticText* m_bottomPostMachiningLabel;
|
||||
wxChoice* m_bottomPostMachining;
|
||||
wxStaticText* m_bottomPostMachineSize1Label;
|
||||
wxTextCtrl* m_bottomPostMachineSize1;
|
||||
wxStaticText* m_bottomPostMachineSize1Units;
|
||||
wxStaticText* m_bottomPostMachineSize2Label;
|
||||
wxTextCtrl* m_bottomPostMachineSize2;
|
||||
wxStaticText* m_bottomPostMachineSize2Units;
|
||||
wxChoice* m_backDrillChoice;
|
||||
wxStaticText* m_backDrillTopLayerLabel;
|
||||
wxBitmapComboBox* m_backDrillTopLayer;
|
||||
wxStaticText* m_backDrillTopSizeLabel;
|
||||
wxTextCtrl* m_backDrillTopSize;
|
||||
wxStaticText* m_backDrillTopSizeUnits;
|
||||
wxStaticText* m_backDrillBottomLayerLabel;
|
||||
wxBitmapComboBox* m_backDrillBottomLayer;
|
||||
wxStaticText* m_backDrillBottomSizeLabel;
|
||||
wxTextCtrl* m_backDrillBottomSize;
|
||||
wxStaticText* m_backDrillBottomSizeUnits;
|
||||
wxSimplebook* m_stackupImagesBook;
|
||||
wxPanel* page0;
|
||||
wxStaticBitmap* m_stackupImage0;
|
||||
@@ -284,6 +313,9 @@ class DIALOG_PAD_PROPERTIES_BASE : public DIALOG_SHIM
|
||||
virtual void onTeardropsUpdateUi( wxUpdateUIEvent& event ) { event.Skip(); }
|
||||
virtual void onModify( wxSpinDoubleEvent& event ) { event.Skip(); }
|
||||
virtual void OnUpdateUINonCopperWarning( wxUpdateUIEvent& event ) { event.Skip(); }
|
||||
virtual void onTopPostMachining( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onBottomPostMachining( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onBackDrillChoice( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onChangePadMode( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void OnCancel( wxCommandEvent& event ) { event.Skip(); }
|
||||
|
||||
|
||||
@@ -62,6 +62,10 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_EDIT_FRAME* a
|
||||
m_viaY( aParent, m_ViaYLabel, m_ViaYCtrl, m_ViaYUnit ),
|
||||
m_viaDiameter( aParent, m_ViaDiameterLabel, m_ViaDiameterCtrl, m_ViaDiameterUnit ),
|
||||
m_viaDrill( aParent, m_ViaDrillLabel, m_ViaDrillCtrl, m_ViaDrillUnit ),
|
||||
m_topPostMachineSize1Binder( aParent, m_topPostMachineSize1Label, m_topPostMachineSize1, m_topPostMachineSize1Units ),
|
||||
m_topPostMachineSize2Binder( aParent, m_topPostMachineSize2Label, m_topPostMachineSize2, m_topPostMachineSize2Units ),
|
||||
m_bottomPostMachineSize1Binder( aParent, m_bottomPostMachineSize1Label, m_bottomPostMachineSize1, m_bottomPostMachineSize1Units ),
|
||||
m_bottomPostMachineSize2Binder( aParent, m_bottomPostMachineSize2Label, m_bottomPostMachineSize2, m_bottomPostMachineSize2Units ),
|
||||
m_teardropHDPercent( aParent, m_stHDRatio, m_tcHDRatio, m_stHDRatioUnits ),
|
||||
m_teardropLenPercent( aParent, m_stLenPercentLabel, m_tcLenPercent, nullptr ),
|
||||
m_teardropMaxLen( aParent, m_stMaxLen, m_tcTdMaxLen, m_stMaxLenUnits ),
|
||||
@@ -69,7 +73,10 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_EDIT_FRAME* a
|
||||
m_teardropMaxWidth( aParent, m_stMaxWidthLabel, m_tcMaxWidth, m_stMaxWidthUnits ),
|
||||
m_tracks( false ),
|
||||
m_vias( false ),
|
||||
m_editLayer( PADSTACK::ALL_LAYERS )
|
||||
m_editLayer( PADSTACK::ALL_LAYERS ),
|
||||
m_backdrillStartIndeterminate( false ),
|
||||
m_backdrillEndIndeterminate( false ),
|
||||
m_padstackDirty( false )
|
||||
{
|
||||
m_useCalculatedSize = true;
|
||||
|
||||
@@ -109,6 +116,18 @@ DIALOG_TRACK_VIA_PROPERTIES::DIALOG_TRACK_VIA_PROPERTIES( PCB_BASE_EDIT_FRAME* a
|
||||
m_ViaEndLayer->SetBoardFrame( aParent );
|
||||
m_ViaEndLayer->Resync();
|
||||
|
||||
m_backDrillFrontLayer->SetLayersHotkeys( false );
|
||||
m_backDrillFrontLayer->SetNotAllowedLayerSet( LSET::AllNonCuMask() );
|
||||
m_backDrillFrontLayer->SetBoardFrame( aParent );
|
||||
m_backDrillFrontLayer->SetUndefinedLayerName( _( "None" ) );
|
||||
m_backDrillFrontLayer->Resync();
|
||||
|
||||
m_ViaStartLayer11->SetLayersHotkeys( false );
|
||||
m_ViaStartLayer11->SetNotAllowedLayerSet( LSET::AllNonCuMask() );
|
||||
m_ViaStartLayer11->SetBoardFrame( aParent );
|
||||
m_ViaStartLayer11->SetUndefinedLayerName( _( "None" ) );
|
||||
m_ViaStartLayer11->Resync();
|
||||
|
||||
wxFont infoFont = KIUI::GetSmallInfoFont( this );
|
||||
m_techLayersLabel->SetFont( infoFont );
|
||||
|
||||
@@ -160,6 +179,23 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataToWindow()
|
||||
// The selection layer for tracks
|
||||
int track_selection_layer = -1;
|
||||
|
||||
int backdrill_start_layer = UNDEFINED_LAYER;
|
||||
int backdrill_end_layer = UNDEFINED_LAYER;
|
||||
bool backdrill_start_layer_set = false;
|
||||
bool backdrill_end_layer_set = false;
|
||||
bool backdrill_start_layer_mixed = false;
|
||||
bool backdrill_end_layer_mixed = false;
|
||||
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> primary_post_machining_value;
|
||||
bool primary_post_machining_set = false;
|
||||
bool primary_post_machining_mixed = false;
|
||||
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> secondary_post_machining_value;
|
||||
bool secondary_post_machining_set = false;
|
||||
bool secondary_post_machining_mixed = false;
|
||||
|
||||
m_padstackDirty = false;
|
||||
|
||||
auto getAnnularRingSelection =
|
||||
[]( const PCB_VIA* via ) -> int
|
||||
{
|
||||
@@ -261,6 +297,20 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataToWindow()
|
||||
m_viaNotFree->SetValue( !v->GetIsFree() );
|
||||
m_annularRingsCtrl->SetSelection( getAnnularRingSelection( v ) );
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secondaryDrill = v->Padstack().SecondaryDrill();
|
||||
|
||||
primary_post_machining_value = v->Padstack().FrontPostMachining().mode;
|
||||
primary_post_machining_set = true;
|
||||
|
||||
secondary_post_machining_value = v->Padstack().BackPostMachining().mode;
|
||||
secondary_post_machining_set = true;
|
||||
|
||||
backdrill_start_layer = secondaryDrill.start;
|
||||
backdrill_start_layer_set = true;
|
||||
|
||||
backdrill_end_layer = secondaryDrill.end;
|
||||
backdrill_end_layer_set = true;
|
||||
|
||||
selection_first_layer = v->TopLayer();
|
||||
selection_last_layer = v->BottomLayer();
|
||||
|
||||
@@ -337,6 +387,20 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataToWindow()
|
||||
|
||||
if( static_cast<int>( getViaConfiguration( v ) ) != m_protectionFeatures->GetSelection() )
|
||||
m_protectionFeatures->SetSelection( m_protectionFeatures->Append( INDETERMINATE_STATE ) );
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secondaryDrill = v->Padstack().SecondaryDrill();
|
||||
|
||||
if( primary_post_machining_set && primary_post_machining_value != v->Padstack().FrontPostMachining().mode )
|
||||
primary_post_machining_mixed = true;
|
||||
|
||||
if( secondary_post_machining_set && secondary_post_machining_value != v->Padstack().BackPostMachining().mode )
|
||||
secondary_post_machining_mixed = true;
|
||||
|
||||
if( backdrill_start_layer_set && backdrill_start_layer != secondaryDrill.start )
|
||||
backdrill_start_layer_mixed = true;
|
||||
|
||||
if( backdrill_end_layer_set && backdrill_end_layer != secondaryDrill.end )
|
||||
backdrill_end_layer_mixed = true;
|
||||
}
|
||||
|
||||
if( v->IsLocked() )
|
||||
@@ -385,6 +449,96 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataToWindow()
|
||||
}
|
||||
|
||||
m_ViaEndLayer->SetLayerSelection( selection_last_layer );
|
||||
|
||||
if( backdrill_start_layer_mixed )
|
||||
{
|
||||
m_backdrillStartIndeterminate = true;
|
||||
m_backDrillFrontLayer->SetUndefinedLayerName( INDETERMINATE_STATE );
|
||||
m_backDrillFrontLayer->Resync();
|
||||
m_backDrillFrontLayer->SetLayerSelection( UNDEFINED_LAYER );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_backdrillStartIndeterminate = false;
|
||||
|
||||
if( !backdrill_start_layer_set )
|
||||
backdrill_start_layer = UNDEFINED_LAYER;
|
||||
|
||||
m_backDrillFrontLayer->SetUndefinedLayerName( _( "None" ) );
|
||||
m_backDrillFrontLayer->Resync();
|
||||
m_backDrillFrontLayer->SetLayerSelection( backdrill_start_layer );
|
||||
}
|
||||
|
||||
if( backdrill_end_layer_mixed )
|
||||
{
|
||||
m_backdrillEndIndeterminate = true;
|
||||
m_ViaStartLayer11->SetUndefinedLayerName( INDETERMINATE_STATE );
|
||||
m_ViaStartLayer11->Resync();
|
||||
m_ViaStartLayer11->SetLayerSelection( UNDEFINED_LAYER );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_backdrillEndIndeterminate = false;
|
||||
|
||||
if( !backdrill_end_layer_set )
|
||||
backdrill_end_layer = UNDEFINED_LAYER;
|
||||
|
||||
m_ViaStartLayer11->SetUndefinedLayerName( _( "None" ) );
|
||||
m_ViaStartLayer11->Resync();
|
||||
m_ViaStartLayer11->SetLayerSelection( backdrill_end_layer );
|
||||
}
|
||||
|
||||
// Set Backdrill Choice
|
||||
if( backdrill_start_layer_mixed || backdrill_end_layer_mixed )
|
||||
{
|
||||
m_backDrillChoice->SetSelection( wxNOT_FOUND );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( backdrill_start_layer != UNDEFINED_LAYER )
|
||||
m_backDrillChoice->SetSelection( 2 ); // Top
|
||||
else if( backdrill_end_layer != UNDEFINED_LAYER )
|
||||
m_backDrillChoice->SetSelection( 1 ); // Bottom
|
||||
else
|
||||
m_backDrillChoice->SetSelection( 0 ); // None
|
||||
}
|
||||
|
||||
// Post Machining
|
||||
if( primary_post_machining_mixed )
|
||||
{
|
||||
m_topPostMachine->SetSelection( wxNOT_FOUND );
|
||||
}
|
||||
else if( primary_post_machining_set && primary_post_machining_value.has_value() )
|
||||
{
|
||||
switch( primary_post_machining_value.value() )
|
||||
{
|
||||
case PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE: m_topPostMachine->SetSelection( 2 ); break;
|
||||
case PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK: m_topPostMachine->SetSelection( 1 ); break;
|
||||
default: m_topPostMachine->SetSelection( 0 ); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_topPostMachine->SetSelection( 0 );
|
||||
}
|
||||
|
||||
if( secondary_post_machining_mixed )
|
||||
{
|
||||
m_bottomPostMachine->SetSelection( wxNOT_FOUND );
|
||||
}
|
||||
else if( secondary_post_machining_set && secondary_post_machining_value.has_value() )
|
||||
{
|
||||
switch( secondary_post_machining_value.value() )
|
||||
{
|
||||
case PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE: m_bottomPostMachine->SetSelection( 2 ); break;
|
||||
case PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK: m_bottomPostMachine->SetSelection( 1 ); break;
|
||||
default: m_bottomPostMachine->SetSelection( 0 ); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_bottomPostMachine->SetSelection( 0 );
|
||||
}
|
||||
}
|
||||
|
||||
m_netSelector->SetNetInfo( &m_frame->GetBoard()->GetNetInfo() );
|
||||
@@ -500,6 +654,11 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataToWindow()
|
||||
else
|
||||
SetInitialFocus( m_ViaDiameterCtrl );
|
||||
|
||||
wxCommandEvent dummyEvent;
|
||||
onBackdrillChange( dummyEvent );
|
||||
onTopPostMachineChange( dummyEvent );
|
||||
onBottomPostMachineChange( dummyEvent );
|
||||
|
||||
// Now all widgets have the size fixed, call FinishDialogSettings
|
||||
finishDialogSettings();
|
||||
|
||||
@@ -660,8 +819,99 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataFromWindow()
|
||||
if( m_ViaEndLayer->GetLayerSelection() != UNDEFINED_LAYER )
|
||||
endLayer = static_cast<PCB_LAYER_ID>( m_ViaEndLayer->GetLayerSelection() );
|
||||
|
||||
std::optional<int> secondaryDrill;
|
||||
|
||||
// Calculate backdrill size (10% larger than drill)
|
||||
if( viaDrill.has_value() )
|
||||
{
|
||||
secondaryDrill = viaDrill.value() * 1.1;
|
||||
}
|
||||
else if( !m_viaDrill.IsIndeterminate() )
|
||||
{
|
||||
secondaryDrill = m_viaDrill.GetIntValue() * 1.1;
|
||||
}
|
||||
|
||||
std::optional<PCB_LAYER_ID> secondaryStartLayer;
|
||||
std::optional<PCB_LAYER_ID> secondaryEndLayer;
|
||||
|
||||
int backdrillChoice = m_backDrillChoice->GetSelection();
|
||||
|
||||
if( backdrillChoice == 1 ) // Bottom
|
||||
{
|
||||
secondaryStartLayer = B_Cu;
|
||||
if( m_ViaStartLayer11->GetLayerSelection() != UNDEFINED_LAYER )
|
||||
secondaryEndLayer = static_cast<PCB_LAYER_ID>( m_ViaStartLayer11->GetLayerSelection() );
|
||||
}
|
||||
else if( backdrillChoice == 2 ) // Top
|
||||
{
|
||||
secondaryStartLayer = F_Cu;
|
||||
if( m_backDrillFrontLayer->GetLayerSelection() != UNDEFINED_LAYER )
|
||||
secondaryEndLayer = static_cast<PCB_LAYER_ID>( m_backDrillFrontLayer->GetLayerSelection() );
|
||||
}
|
||||
// Choice 0 is None, so optional values remain empty
|
||||
|
||||
// Post Machining
|
||||
std::optional<PADSTACK::POST_MACHINING_PROPS> frontPostMachining;
|
||||
std::optional<PADSTACK::POST_MACHINING_PROPS> backPostMachining;
|
||||
|
||||
if( m_topPostMachine->GetSelection() != wxNOT_FOUND )
|
||||
{
|
||||
PADSTACK::POST_MACHINING_PROPS props;
|
||||
switch( m_topPostMachine->GetSelection() )
|
||||
{
|
||||
case 1: props.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK; break;
|
||||
case 2: props.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE; break;
|
||||
default: props.mode = PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED; break;
|
||||
}
|
||||
|
||||
if( !m_topPostMachineSize1Binder.IsIndeterminate() )
|
||||
props.size = m_topPostMachineSize1Binder.GetIntValue();
|
||||
|
||||
if( !m_topPostMachineSize2Binder.IsIndeterminate() )
|
||||
{
|
||||
if( props.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
props.angle = m_topPostMachineSize2Binder.GetIntValue();
|
||||
else
|
||||
props.depth = m_topPostMachineSize2Binder.GetIntValue();
|
||||
}
|
||||
frontPostMachining = props;
|
||||
}
|
||||
|
||||
if( m_bottomPostMachine->GetSelection() != wxNOT_FOUND )
|
||||
{
|
||||
PADSTACK::POST_MACHINING_PROPS props;
|
||||
switch( m_bottomPostMachine->GetSelection() )
|
||||
{
|
||||
case 1: props.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK; break;
|
||||
case 2: props.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE; break;
|
||||
default: props.mode = PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED; break;
|
||||
}
|
||||
|
||||
if( !m_bottomPostMachineSize1Binder.IsIndeterminate() )
|
||||
props.size = m_bottomPostMachineSize1Binder.GetIntValue();
|
||||
|
||||
if( !m_bottomPostMachineSize2Binder.IsIndeterminate() )
|
||||
{
|
||||
if( props.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
props.angle = m_bottomPostMachineSize2Binder.GetIntValue();
|
||||
else
|
||||
props.depth = m_bottomPostMachineSize2Binder.GetIntValue();
|
||||
}
|
||||
backPostMachining = props;
|
||||
}
|
||||
|
||||
// Tertiary drill not supported in new UI for now
|
||||
std::optional<int> tertiaryDrill;
|
||||
std::optional<PCB_LAYER_ID> tertiaryStartLayer;
|
||||
std::optional<PCB_LAYER_ID> tertiaryEndLayer;
|
||||
|
||||
int copperLayerCount = m_frame->GetBoard() ? m_frame->GetBoard()->GetCopperLayerCount() : 0;
|
||||
|
||||
if( std::optional<PCB_VIA::VIA_PARAMETER_ERROR> error =
|
||||
PCB_VIA::ValidateViaParameters( viaDiameter, viaDrill, startLayer, endLayer ) )
|
||||
PCB_VIA::ValidateViaParameters( viaDiameter, viaDrill, startLayer, endLayer,
|
||||
secondaryDrill, secondaryStartLayer,
|
||||
secondaryEndLayer, tertiaryDrill, tertiaryStartLayer,
|
||||
tertiaryEndLayer, copperLayerCount ) )
|
||||
{
|
||||
DisplayError( GetParent(), error->m_Message );
|
||||
|
||||
@@ -675,15 +925,10 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataFromWindow()
|
||||
m_ViaDiameterCtrl->SelectAll();
|
||||
m_ViaDiameterCtrl->SetFocus();
|
||||
}
|
||||
|
||||
// Other fields might not have direct focus targets in new UI or I'd need to map them
|
||||
return false;
|
||||
}
|
||||
|
||||
if( viaDiameter.has_value() )
|
||||
{
|
||||
int diameter = viaDiameter.value();
|
||||
m_viaStack->SetSize( { diameter, diameter }, m_editLayer );
|
||||
}
|
||||
}
|
||||
|
||||
if( m_tracks )
|
||||
@@ -753,6 +998,7 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataFromWindow()
|
||||
{
|
||||
wxASSERT( m_vias );
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( track );
|
||||
bool updatePadstack = m_padstackDirty;
|
||||
|
||||
if( !m_viaX.IsIndeterminate() )
|
||||
via->SetPosition( VECTOR2I( m_viaX.GetIntValue(), via->GetPosition().y ) );
|
||||
@@ -764,7 +1010,132 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataFromWindow()
|
||||
via->SetIsFree( !m_viaNotFree->GetValue() );
|
||||
|
||||
if( !m_viaDiameter.IsIndeterminate() )
|
||||
via->SetPadstack( *m_viaStack );
|
||||
{
|
||||
int newDiameter = m_viaDiameter.GetIntValue();
|
||||
const VECTOR2I& currentSize = via->Padstack().Size( m_editLayer );
|
||||
|
||||
if( currentSize.x != newDiameter || currentSize.y != newDiameter )
|
||||
{
|
||||
m_viaStack->SetSize( { newDiameter, newDiameter }, m_editLayer );
|
||||
updatePadstack = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Backdrill
|
||||
int backdrillChoice = m_backDrillChoice->GetSelection();
|
||||
if( backdrillChoice != wxNOT_FOUND )
|
||||
{
|
||||
PADSTACK::DRILL_PROPS secondaryDrill;
|
||||
secondaryDrill.shape = PAD_DRILL_SHAPE::CIRCLE;
|
||||
|
||||
// Calculate size (10% larger)
|
||||
int drillSize = via->GetDrillValue();
|
||||
if( !m_viaDrill.IsIndeterminate() )
|
||||
drillSize = m_viaDrill.GetIntValue();
|
||||
|
||||
secondaryDrill.size = VECTOR2I( drillSize * 1.1, drillSize * 1.1 );
|
||||
|
||||
if( backdrillChoice == 0 ) // None
|
||||
{
|
||||
secondaryDrill.start = UNDEFINED_LAYER;
|
||||
secondaryDrill.end = UNDEFINED_LAYER;
|
||||
secondaryDrill.size = { 0, 0 };
|
||||
secondaryDrill.shape = PAD_DRILL_SHAPE::UNDEFINED;
|
||||
}
|
||||
else if( backdrillChoice == 1 ) // Bottom
|
||||
{
|
||||
secondaryDrill.start = B_Cu;
|
||||
if( m_ViaStartLayer11->GetLayerSelection() != UNDEFINED_LAYER )
|
||||
secondaryDrill.end = static_cast<PCB_LAYER_ID>( m_ViaStartLayer11->GetLayerSelection() );
|
||||
else
|
||||
secondaryDrill.end = UNDEFINED_LAYER; // Or keep existing?
|
||||
}
|
||||
else if( backdrillChoice == 2 ) // Top
|
||||
{
|
||||
secondaryDrill.start = F_Cu;
|
||||
if( m_backDrillFrontLayer->GetLayerSelection() != UNDEFINED_LAYER )
|
||||
secondaryDrill.end = static_cast<PCB_LAYER_ID>( m_backDrillFrontLayer->GetLayerSelection() );
|
||||
else
|
||||
secondaryDrill.end = UNDEFINED_LAYER;
|
||||
}
|
||||
|
||||
if( via->Padstack().SecondaryDrill() != secondaryDrill )
|
||||
{
|
||||
m_viaStack->SecondaryDrill() = secondaryDrill;
|
||||
updatePadstack = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Post Machining
|
||||
if( m_topPostMachine->GetSelection() != wxNOT_FOUND )
|
||||
{
|
||||
PADSTACK::POST_MACHINING_PROPS props;
|
||||
switch( m_topPostMachine->GetSelection() )
|
||||
{
|
||||
case 1: props.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK; break;
|
||||
case 2: props.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE; break;
|
||||
default: props.mode = PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED; break;
|
||||
}
|
||||
|
||||
if( !m_topPostMachineSize1Binder.IsIndeterminate() )
|
||||
props.size = m_topPostMachineSize1Binder.GetIntValue();
|
||||
else
|
||||
props.size = via->Padstack().FrontPostMachining().size;
|
||||
|
||||
if( !m_topPostMachineSize2Binder.IsIndeterminate() )
|
||||
{
|
||||
if( props.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
props.angle = m_topPostMachineSize2Binder.GetIntValue();
|
||||
else
|
||||
props.depth = m_topPostMachineSize2Binder.GetIntValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
props.angle = via->Padstack().FrontPostMachining().angle;
|
||||
props.depth = via->Padstack().FrontPostMachining().depth;
|
||||
}
|
||||
|
||||
if( via->Padstack().FrontPostMachining() != props )
|
||||
{
|
||||
m_viaStack->FrontPostMachining() = props;
|
||||
updatePadstack = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( m_bottomPostMachine->GetSelection() != wxNOT_FOUND )
|
||||
{
|
||||
PADSTACK::POST_MACHINING_PROPS props;
|
||||
switch( m_bottomPostMachine->GetSelection() )
|
||||
{
|
||||
case 1: props.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK; break;
|
||||
case 2: props.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE; break;
|
||||
default: props.mode = PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED; break;
|
||||
}
|
||||
|
||||
if( !m_bottomPostMachineSize1Binder.IsIndeterminate() )
|
||||
props.size = m_bottomPostMachineSize1Binder.GetIntValue();
|
||||
else
|
||||
props.size = via->Padstack().BackPostMachining().size;
|
||||
|
||||
if( !m_bottomPostMachineSize2Binder.IsIndeterminate() )
|
||||
{
|
||||
if( props.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
props.angle = m_bottomPostMachineSize2Binder.GetIntValue();
|
||||
else
|
||||
props.depth = m_bottomPostMachineSize2Binder.GetIntValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
props.angle = via->Padstack().BackPostMachining().angle;
|
||||
props.depth = via->Padstack().BackPostMachining().depth;
|
||||
}
|
||||
|
||||
if( via->Padstack().BackPostMachining() != props )
|
||||
{
|
||||
m_viaStack->BackPostMachining() = props;
|
||||
updatePadstack = true;
|
||||
}
|
||||
}
|
||||
|
||||
switch( m_ViaTypeChoice->GetSelection() )
|
||||
{
|
||||
@@ -780,17 +1151,31 @@ bool DIALOG_TRACK_VIA_PROPERTIES::TransferDataFromWindow()
|
||||
|
||||
if( startLayer != UNDEFINED_LAYER )
|
||||
{
|
||||
m_viaStack->Drill().start = startLayer;
|
||||
if( via->Padstack().Drill().start != startLayer )
|
||||
{
|
||||
m_viaStack->Drill().start = startLayer;
|
||||
updatePadstack = true;
|
||||
}
|
||||
|
||||
via->SetTopLayer( startLayer );
|
||||
}
|
||||
|
||||
if( endLayer != UNDEFINED_LAYER )
|
||||
{
|
||||
m_viaStack->Drill().end = endLayer;
|
||||
if( via->Padstack().Drill().end != endLayer )
|
||||
{
|
||||
m_viaStack->Drill().end = endLayer;
|
||||
updatePadstack = true;
|
||||
}
|
||||
|
||||
via->SetBottomLayer( endLayer );
|
||||
}
|
||||
|
||||
via->SanitizeLayers();
|
||||
if( updatePadstack )
|
||||
{
|
||||
via->SetPadstack( *m_viaStack );
|
||||
via->SanitizeLayers();
|
||||
}
|
||||
|
||||
switch( m_annularRingsCtrl->GetSelection() )
|
||||
{
|
||||
@@ -981,6 +1366,8 @@ void DIALOG_TRACK_VIA_PROPERTIES::onPadstackModeChanged( wxCommandEvent& aEvent
|
||||
case 2: m_viaStack->SetMode( PADSTACK::MODE::CUSTOM ); break;
|
||||
}
|
||||
|
||||
m_padstackDirty = true;
|
||||
|
||||
afterPadstackModeChanged();
|
||||
}
|
||||
|
||||
@@ -1168,3 +1555,78 @@ void DIALOG_TRACK_VIA_PROPERTIES::onTeardropsUpdateUi( wxUpdateUIEvent& event )
|
||||
event.Enable( !m_frame->GetBoard()->LegacyTeardrops() );
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_TRACK_VIA_PROPERTIES::onBackdrillChange( wxCommandEvent& aEvent )
|
||||
{
|
||||
int selection = m_backDrillChoice->GetSelection();
|
||||
// 0: None, 1: Bottom, 2: Top, 3: Both
|
||||
|
||||
bool enableTop = ( selection == 2 || selection == 3 );
|
||||
bool enableBottom = ( selection == 1 || selection == 3 );
|
||||
|
||||
m_backDrillFrontLayer->Enable( enableTop );
|
||||
m_backDrillFrontLayerLabel->Enable( enableTop );
|
||||
|
||||
m_ViaStartLayer11->Enable( enableBottom ); // Back layer selector
|
||||
m_backDrillBackLayer->Enable( enableBottom ); // Back layer label
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_TRACK_VIA_PROPERTIES::onTopPostMachineChange( wxCommandEvent& aEvent )
|
||||
{
|
||||
int selection = m_topPostMachine->GetSelection();
|
||||
// 0: None, 1: Countersink, 2: Counterbore
|
||||
|
||||
bool enable = ( selection != 0 );
|
||||
m_topPostMachineSize1Binder.Enable( enable );
|
||||
m_topPostMachineSize2Binder.Enable( enable );
|
||||
m_topPostMachineSize1Label->Enable( enable );
|
||||
m_topPostMachineSize2Label->Enable( enable );
|
||||
|
||||
if( selection == 1 ) // Countersink
|
||||
{
|
||||
m_topPostMachineSize2Label->SetLabel( _( "Angle:" ) );
|
||||
m_topPostMachineSize2Units->SetLabel( _( "deg" ) );
|
||||
|
||||
if( m_topPostMachineSize2Binder.IsIndeterminate() || m_topPostMachineSize2Binder.GetDoubleValue() == 0 )
|
||||
{
|
||||
m_topPostMachineSize2Binder.SetValue( "82" );
|
||||
}
|
||||
}
|
||||
else if( selection == 2 ) // Counterbore
|
||||
{
|
||||
m_topPostMachineSize2Label->SetLabel( _( "Depth:" ) );
|
||||
m_topPostMachineSize2Units->SetLabel( EDA_UNIT_UTILS::GetLabel( m_frame->GetUserUnits() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DIALOG_TRACK_VIA_PROPERTIES::onBottomPostMachineChange( wxCommandEvent& aEvent )
|
||||
{
|
||||
int selection = m_bottomPostMachine->GetSelection();
|
||||
// 0: None, 1: Countersink, 2: Counterbore
|
||||
|
||||
bool enable = ( selection != 0 );
|
||||
m_bottomPostMachineSize1Binder.Enable( enable );
|
||||
m_bottomPostMachineSize2Binder.Enable( enable );
|
||||
m_bottomPostMachineSize1Label->Enable( enable );
|
||||
m_bottomPostMachineSize2Label->Enable( enable );
|
||||
|
||||
if( selection == 1 ) // Countersink
|
||||
{
|
||||
m_bottomPostMachineSize2Label->SetLabel( _( "Angle:" ) );
|
||||
m_bottomPostMachineSize2Units->SetLabel( _( "deg" ) );
|
||||
|
||||
if( m_bottomPostMachineSize2Binder.IsIndeterminate() || m_bottomPostMachineSize2Binder.GetDoubleValue() == 0 )
|
||||
{
|
||||
m_bottomPostMachineSize2Binder.SetValue( "82" );
|
||||
}
|
||||
}
|
||||
else if( selection == 2 ) // Counterbore
|
||||
{
|
||||
m_bottomPostMachineSize2Label->SetLabel( _( "Depth:" ) );
|
||||
m_bottomPostMachineSize2Units->SetLabel( EDA_UNIT_UTILS::GetLabel( m_frame->GetUserUnits() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -53,6 +53,9 @@ private:
|
||||
void onTrackEdit( wxCommandEvent& aEvent ) override;
|
||||
void onPadstackModeChanged( wxCommandEvent& aEvent ) override;
|
||||
void onEditLayerChanged( wxCommandEvent& aEvent ) override;
|
||||
void onBackdrillChange( wxCommandEvent& aEvent ) override;
|
||||
void onTopPostMachineChange( wxCommandEvent& aEvent ) override;
|
||||
void onBottomPostMachineChange( wxCommandEvent& aEvent ) override;
|
||||
|
||||
void onUnitsChanged( wxCommandEvent& aEvent );
|
||||
void onTeardropsUpdateUi( wxUpdateUIEvent& event ) override;
|
||||
@@ -78,6 +81,11 @@ private:
|
||||
UNIT_BINDER m_viaX, m_viaY;
|
||||
UNIT_BINDER m_viaDiameter, m_viaDrill;
|
||||
|
||||
UNIT_BINDER m_topPostMachineSize1Binder;
|
||||
UNIT_BINDER m_topPostMachineSize2Binder;
|
||||
UNIT_BINDER m_bottomPostMachineSize1Binder;
|
||||
UNIT_BINDER m_bottomPostMachineSize2Binder;
|
||||
|
||||
UNIT_BINDER m_teardropHDPercent;
|
||||
UNIT_BINDER m_teardropLenPercent;
|
||||
UNIT_BINDER m_teardropMaxLen;
|
||||
@@ -93,4 +101,7 @@ private:
|
||||
/// The currently-shown copper layer of the edited via(s)
|
||||
PCB_LAYER_ID m_editLayer;
|
||||
std::map<int, PCB_LAYER_ID> m_editLayerCtrlMap;
|
||||
bool m_backdrillStartIndeterminate;
|
||||
bool m_backdrillEndIndeterminate;
|
||||
bool m_padstackDirty;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
|
||||
// C++ code generated with wxFormBuilder (version 4.2.1-115-g11c2dec8-dirty)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
@@ -17,7 +17,7 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::DIALOG_TRACK_VIA_PROPERTIES_BASE( wxWindow* pa
|
||||
|
||||
m_MainSizer = new wxBoxSizer( wxVERTICAL );
|
||||
|
||||
m_sbCommonSizer = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Common") ), wxVERTICAL );
|
||||
m_sbCommonSizer = new wxStaticBoxSizer( wxVERTICAL, this, _("Common") );
|
||||
|
||||
wxBoxSizer* bSizerNetWidgets;
|
||||
bSizerNetWidgets = new wxBoxSizer( wxHORIZONTAL );
|
||||
@@ -61,7 +61,7 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::DIALOG_TRACK_VIA_PROPERTIES_BASE( wxWindow* pa
|
||||
|
||||
m_MainSizer->Add( m_sbCommonSizer, 0, wxEXPAND|wxALL, 10 );
|
||||
|
||||
m_sbTrackSizer = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Tracks") ), wxHORIZONTAL );
|
||||
m_sbTrackSizer = new wxStaticBoxSizer( wxHORIZONTAL, this, _("Tracks") );
|
||||
|
||||
wxBoxSizer* bSizerTracksLeftCol;
|
||||
bSizerTracksLeftCol = new wxBoxSizer( wxVERTICAL );
|
||||
@@ -228,7 +228,7 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::DIALOG_TRACK_VIA_PROPERTIES_BASE( wxWindow* pa
|
||||
|
||||
m_MainSizer->Add( m_sbTrackSizer, 0, wxEXPAND|wxRIGHT|wxLEFT, 10 );
|
||||
|
||||
m_sbViaSizer = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Vias") ), wxVERTICAL );
|
||||
m_sbViaSizer = new wxStaticBoxSizer( wxVERTICAL, this, _("Vias") );
|
||||
|
||||
wxBoxSizer* bSizerViaCols;
|
||||
bSizerViaCols = new wxBoxSizer( wxHORIZONTAL );
|
||||
@@ -406,8 +406,181 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::DIALOG_TRACK_VIA_PROPERTIES_BASE( wxWindow* pa
|
||||
|
||||
m_sbViaSizer->Add( bSizerViaCols, 0, wxALL|wxEXPAND, 5 );
|
||||
|
||||
wxBoxSizer* bBackdrillLabel;
|
||||
bBackdrillLabel = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
m_backDrillLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Backdrill"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_backDrillLabel->Wrap( -1 );
|
||||
bBackdrillLabel->Add( m_backDrillLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_staticline2 = new wxStaticLine( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
|
||||
m_sbViaSizer->Add( m_staticline2, 0, wxEXPAND|wxBOTTOM, 8 );
|
||||
bBackdrillLabel->Add( m_staticline2, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 8 );
|
||||
|
||||
|
||||
m_sbViaSizer->Add( bBackdrillLabel, 0, wxEXPAND, 5 );
|
||||
|
||||
wxBoxSizer* bSizerBackdrill;
|
||||
bSizerBackdrill = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
wxFlexGridSizer* fgSizer6;
|
||||
fgSizer6 = new wxFlexGridSizer( 0, 2, 0, 0 );
|
||||
fgSizer6->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer6->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
wxString m_backDrillChoiceChoices[] = { _("None"), _("Bottom"), _("Top"), _("Both") };
|
||||
int m_backDrillChoiceNChoices = sizeof( m_backDrillChoiceChoices ) / sizeof( wxString );
|
||||
m_backDrillChoice = new wxChoice( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_backDrillChoiceNChoices, m_backDrillChoiceChoices, 0 );
|
||||
m_backDrillChoice->SetSelection( 0 );
|
||||
fgSizer6->Add( m_backDrillChoice, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizerBackdrill->Add( fgSizer6, 1, wxEXPAND, 5 );
|
||||
|
||||
wxFlexGridSizer* fgSizer7;
|
||||
fgSizer7 = new wxFlexGridSizer( 0, 2, 0, 0 );
|
||||
fgSizer7->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer7->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
m_backDrillFrontLayerLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Backdrill front ending layer:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_backDrillFrontLayerLabel->Wrap( -1 );
|
||||
fgSizer7->Add( m_backDrillFrontLayerLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_backDrillFrontLayer = new PCB_LAYER_BOX_SELECTOR( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
|
||||
fgSizer7->Add( m_backDrillFrontLayer, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizerBackdrill->Add( fgSizer7, 2, wxEXPAND, 5 );
|
||||
|
||||
wxFlexGridSizer* fgSizer8;
|
||||
fgSizer8 = new wxFlexGridSizer( 0, 2, 0, 0 );
|
||||
fgSizer8->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer8->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
m_backDrillBackLayer = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Backdrill bottom ending layer:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_backDrillBackLayer->Wrap( -1 );
|
||||
fgSizer8->Add( m_backDrillBackLayer, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_ViaStartLayer11 = new PCB_LAYER_BOX_SELECTOR( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
|
||||
fgSizer8->Add( m_ViaStartLayer11, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizerBackdrill->Add( fgSizer8, 2, wxEXPAND, 5 );
|
||||
|
||||
|
||||
m_sbViaSizer->Add( bSizerBackdrill, 0, wxALL|wxEXPAND, 5 );
|
||||
|
||||
wxBoxSizer* bPostMachineLabel;
|
||||
bPostMachineLabel = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
m_postMachineSectionLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Hole post-machining"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_postMachineSectionLabel->Wrap( -1 );
|
||||
bPostMachineLabel->Add( m_postMachineSectionLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_staticline21 = new wxStaticLine( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
|
||||
bPostMachineLabel->Add( m_staticline21, 1, wxALIGN_CENTER_VERTICAL|wxRIGHT, 8 );
|
||||
|
||||
|
||||
m_sbViaSizer->Add( bPostMachineLabel, 0, wxEXPAND, 5 );
|
||||
|
||||
wxBoxSizer* bSizerPostMachine;
|
||||
bSizerPostMachine = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
wxFlexGridSizer* fgSizer9;
|
||||
fgSizer9 = new wxFlexGridSizer( 0, 3, 0, 0 );
|
||||
fgSizer9->AddGrowableCol( 1 );
|
||||
fgSizer9->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer9->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
m_topPostMachineLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Top:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachineLabel->Wrap( -1 );
|
||||
fgSizer9->Add( m_topPostMachineLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
wxString m_topPostMachineChoices[] = { _("None"), _("Countersink"), _("Counterbore") };
|
||||
int m_topPostMachineNChoices = sizeof( m_topPostMachineChoices ) / sizeof( wxString );
|
||||
m_topPostMachine = new wxChoice( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_topPostMachineNChoices, m_topPostMachineChoices, 0 );
|
||||
m_topPostMachine->SetSelection( 0 );
|
||||
fgSizer9->Add( m_topPostMachine, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
fgSizer9->Add( 0, 0, 1, wxEXPAND, 5 );
|
||||
|
||||
m_topPostMachineSize1Label = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Size:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachineSize1Label->Wrap( -1 );
|
||||
fgSizer9->Add( m_topPostMachineSize1Label, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_topPostMachineSize1 = new wxTextCtrl( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer9->Add( m_topPostMachineSize1, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_topPostMachineSize1Units = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachineSize1Units->Wrap( -1 );
|
||||
fgSizer9->Add( m_topPostMachineSize1Units, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_topPostMachineSize2Label = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Angle:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachineSize2Label->Wrap( -1 );
|
||||
fgSizer9->Add( m_topPostMachineSize2Label, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_topPostMachineSize2 = new wxTextCtrl( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer9->Add( m_topPostMachineSize2, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_topPostMachineSize2Units = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("deg"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_topPostMachineSize2Units->Wrap( -1 );
|
||||
fgSizer9->Add( m_topPostMachineSize2Units, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
|
||||
bSizerPostMachine->Add( fgSizer9, 1, wxEXPAND, 5 );
|
||||
|
||||
|
||||
bSizerPostMachine->Add( 0, 0, 0, wxEXPAND|wxLEFT|wxRIGHT, 15 );
|
||||
|
||||
wxFlexGridSizer* fgSizer10;
|
||||
fgSizer10 = new wxFlexGridSizer( 0, 3, 0, 0 );
|
||||
fgSizer10->AddGrowableCol( 1 );
|
||||
fgSizer10->SetFlexibleDirection( wxBOTH );
|
||||
fgSizer10->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
|
||||
|
||||
m_bottomPostMachineLabel = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Bottom:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachineLabel->Wrap( -1 );
|
||||
fgSizer10->Add( m_bottomPostMachineLabel, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
wxString m_bottomPostMachineChoices[] = { _("None"), _("Countersink"), _("Counterbore") };
|
||||
int m_bottomPostMachineNChoices = sizeof( m_bottomPostMachineChoices ) / sizeof( wxString );
|
||||
m_bottomPostMachine = new wxChoice( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, m_bottomPostMachineNChoices, m_bottomPostMachineChoices, 0 );
|
||||
m_bottomPostMachine->SetSelection( 0 );
|
||||
fgSizer10->Add( m_bottomPostMachine, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
|
||||
fgSizer10->Add( 0, 0, 1, wxEXPAND, 5 );
|
||||
|
||||
m_bottomPostMachineSize1Label = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Size:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachineSize1Label->Wrap( -1 );
|
||||
fgSizer10->Add( m_bottomPostMachineSize1Label, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_bottomPostMachineSize1 = new wxTextCtrl( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer10->Add( m_bottomPostMachineSize1, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_bottomPostMachineSize1Units = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachineSize1Units->Wrap( -1 );
|
||||
fgSizer10->Add( m_bottomPostMachineSize1Units, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_bottomPostMachineSize2Label = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("Angle:"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachineSize2Label->Wrap( -1 );
|
||||
fgSizer10->Add( m_bottomPostMachineSize2Label, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
m_bottomPostMachineSize2 = new wxTextCtrl( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
|
||||
fgSizer10->Add( m_bottomPostMachineSize2, 0, wxALIGN_CENTER_VERTICAL|wxALL|wxEXPAND, 5 );
|
||||
|
||||
m_bottomPostMachineSize2Units = new wxStaticText( m_sbViaSizer->GetStaticBox(), wxID_ANY, _("deg"), wxDefaultPosition, wxDefaultSize, 0 );
|
||||
m_bottomPostMachineSize2Units->Wrap( -1 );
|
||||
fgSizer10->Add( m_bottomPostMachineSize2Units, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
|
||||
|
||||
|
||||
bSizerPostMachine->Add( fgSizer10, 1, wxEXPAND, 5 );
|
||||
|
||||
|
||||
m_sbViaSizer->Add( bSizerPostMachine, 0, wxEXPAND, 5 );
|
||||
|
||||
m_staticline4 = new wxStaticLine( m_sbViaSizer->GetStaticBox(), wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
|
||||
m_sbViaSizer->Add( m_staticline4, 0, wxBOTTOM|wxEXPAND|wxTOP, 8 );
|
||||
|
||||
m_legacyTeardropsWarning = new wxBoxSizer( wxHORIZONTAL );
|
||||
|
||||
@@ -665,6 +838,11 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::DIALOG_TRACK_VIA_PROPERTIES_BASE( wxWindow* pa
|
||||
m_ViaTypeChoice->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_ViaStartLayer->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_ViaEndLayer->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_backDrillChoice->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onBackdrillChange ), NULL, this );
|
||||
m_backDrillFrontLayer->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_ViaStartLayer11->Connect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_topPostMachine->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTopPostMachineChange ), NULL, this );
|
||||
m_bottomPostMachine->Connect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onBottomPostMachineChange ), NULL, this );
|
||||
m_cbTeardrops->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
|
||||
m_cbTeardropsUseNextTrack->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
|
||||
m_stHDRatio->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
|
||||
@@ -706,6 +884,11 @@ DIALOG_TRACK_VIA_PROPERTIES_BASE::~DIALOG_TRACK_VIA_PROPERTIES_BASE()
|
||||
m_ViaTypeChoice->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_ViaStartLayer->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_ViaEndLayer->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_backDrillChoice->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onBackdrillChange ), NULL, this );
|
||||
m_backDrillFrontLayer->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_ViaStartLayer11->Disconnect( wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onViaEdit ), NULL, this );
|
||||
m_topPostMachine->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTopPostMachineChange ), NULL, this );
|
||||
m_bottomPostMachine->Disconnect( wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onBottomPostMachineChange ), NULL, this );
|
||||
m_cbTeardrops->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
|
||||
m_cbTeardropsUseNextTrack->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
|
||||
m_stHDRatio->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( DIALOG_TRACK_VIA_PROPERTIES_BASE::onTeardropsUpdateUi ), NULL, this );
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
|
||||
// C++ code generated with wxFormBuilder (version 4.2.1-115-g11c2dec8-dirty)
|
||||
// http://www.wxformbuilder.org/
|
||||
//
|
||||
// PLEASE DO *NOT* EDIT THIS FILE!
|
||||
@@ -113,7 +113,32 @@ class DIALOG_TRACK_VIA_PROPERTIES_BASE : public DIALOG_SHIM
|
||||
wxChoice* m_annularRingsCtrl;
|
||||
wxStaticText* m_protectionPresetsLabel;
|
||||
wxChoice* m_protectionFeatures;
|
||||
wxStaticText* m_backDrillLabel;
|
||||
wxStaticLine* m_staticline2;
|
||||
wxChoice* m_backDrillChoice;
|
||||
wxStaticText* m_backDrillFrontLayerLabel;
|
||||
PCB_LAYER_BOX_SELECTOR* m_backDrillFrontLayer;
|
||||
wxStaticText* m_backDrillBackLayer;
|
||||
PCB_LAYER_BOX_SELECTOR* m_ViaStartLayer11;
|
||||
wxStaticText* m_postMachineSectionLabel;
|
||||
wxStaticLine* m_staticline21;
|
||||
wxStaticText* m_topPostMachineLabel;
|
||||
wxChoice* m_topPostMachine;
|
||||
wxStaticText* m_topPostMachineSize1Label;
|
||||
wxTextCtrl* m_topPostMachineSize1;
|
||||
wxStaticText* m_topPostMachineSize1Units;
|
||||
wxStaticText* m_topPostMachineSize2Label;
|
||||
wxTextCtrl* m_topPostMachineSize2;
|
||||
wxStaticText* m_topPostMachineSize2Units;
|
||||
wxStaticText* m_bottomPostMachineLabel;
|
||||
wxChoice* m_bottomPostMachine;
|
||||
wxStaticText* m_bottomPostMachineSize1Label;
|
||||
wxTextCtrl* m_bottomPostMachineSize1;
|
||||
wxStaticText* m_bottomPostMachineSize1Units;
|
||||
wxStaticText* m_bottomPostMachineSize2Label;
|
||||
wxTextCtrl* m_bottomPostMachineSize2;
|
||||
wxStaticText* m_bottomPostMachineSize2Units;
|
||||
wxStaticLine* m_staticline4;
|
||||
wxBoxSizer* m_legacyTeardropsWarning;
|
||||
wxStaticBitmap* m_legacyTeardropsIcon;
|
||||
wxStaticText* m_staticText85;
|
||||
@@ -149,6 +174,9 @@ class DIALOG_TRACK_VIA_PROPERTIES_BASE : public DIALOG_SHIM
|
||||
virtual void onPadstackModeChanged( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onEditLayerChanged( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onViaEdit( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onBackdrillChange( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onTopPostMachineChange( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onBottomPostMachineChange( wxCommandEvent& event ) { event.Skip(); }
|
||||
virtual void onTeardropsUpdateUi( wxUpdateUIEvent& event ) { event.Skip(); }
|
||||
|
||||
|
||||
|
||||
@@ -59,7 +59,9 @@ bool DIALOG_TRACK_VIA_SIZE::TransferDataFromWindow()
|
||||
std::optional<int> viaDrill = m_viaDrill.GetIntValue();
|
||||
|
||||
if( std::optional<PCB_VIA::VIA_PARAMETER_ERROR> error =
|
||||
PCB_VIA::ValidateViaParameters( viaDiameter, viaDrill ) )
|
||||
PCB_VIA::ValidateViaParameters( viaDiameter, viaDrill, std::nullopt,
|
||||
std::nullopt, std::nullopt, std::nullopt,
|
||||
std::nullopt, 0 ) )
|
||||
{
|
||||
DisplayError( GetParent(), error->m_Message );
|
||||
|
||||
|
||||
@@ -392,7 +392,9 @@ bool PANEL_SETUP_TRACKS_AND_VIAS::Validate()
|
||||
viaDrillSize = m_viaSizesGrid->GetUnitValue( row, VIA_DRILL_COL );
|
||||
|
||||
if( std::optional<PCB_VIA::VIA_PARAMETER_ERROR> error =
|
||||
PCB_VIA::ValidateViaParameters( viaDiameter, viaDrillSize ) )
|
||||
PCB_VIA::ValidateViaParameters( viaDiameter, viaDrillSize, std::nullopt,
|
||||
std::nullopt, std::nullopt, std::nullopt,
|
||||
std::nullopt, m_Pcb ? m_Pcb->GetCopperLayerCount() : 0 ) )
|
||||
{
|
||||
msg = error->m_Message;
|
||||
|
||||
|
||||
@@ -306,6 +306,10 @@ DRC_ITEM DRC_ITEM::tuningProfileImplicitRules( DRCE_TUNING_PROFILE_IMPLICIT_RULE
|
||||
_HKI( "Tuning profile track geometries" ),
|
||||
wxT( "tuning_profile_track_geometries" ) );
|
||||
|
||||
DRC_ITEM DRC_ITEM::trackOnPostMachinedLayer( DRCE_TRACK_ON_POST_MACHINED_LAYER,
|
||||
_HKI( "Track connected to post-machined or backdrilled layer" ),
|
||||
wxT( "track_on_post_machined_layer" ) );
|
||||
|
||||
std::vector<std::reference_wrapper<RC_ITEM>> DRC_ITEM::allItemTypes( {
|
||||
DRC_ITEM::heading_electrical,
|
||||
DRC_ITEM::shortingItems,
|
||||
@@ -334,6 +338,7 @@ std::vector<std::reference_wrapper<RC_ITEM>> DRC_ITEM::allItemTypes( {
|
||||
DRC_ITEM::copperSliver,
|
||||
DRC_ITEM::solderMaskBridge,
|
||||
DRC_ITEM::connectionWidth,
|
||||
DRC_ITEM::trackOnPostMachinedLayer,
|
||||
DRC_ITEM::tuningProfileImplicitRules,
|
||||
|
||||
DRC_ITEM::heading_schematic_parity,
|
||||
@@ -454,6 +459,7 @@ std::shared_ptr<DRC_ITEM> DRC_ITEM::Create( int aErrorCode )
|
||||
case DRCE_MIRRORED_TEXT_ON_FRONT_LAYER: return std::make_shared<DRC_ITEM>( mirroredTextOnFrontLayer );
|
||||
case DRCE_NONMIRRORED_TEXT_ON_BACK_LAYER: return std::make_shared<DRC_ITEM>( nonMirroredTextOnBackLayer );
|
||||
case DRCE_MISSING_TUNING_PROFILE: return std::make_shared<DRC_ITEM>( missingTuningProfile );
|
||||
case DRCE_TRACK_ON_POST_MACHINED_LAYER: return std::make_shared<DRC_ITEM>( trackOnPostMachinedLayer );
|
||||
default:
|
||||
wxFAIL_MSG( wxT( "Unknown DRC error code" ) );
|
||||
return nullptr;
|
||||
|
||||
@@ -113,7 +113,9 @@ enum PCB_DRC_CODE
|
||||
DRCE_MISSING_TUNING_PROFILE, // Tuning profile used in net class is not defined
|
||||
DRCE_TUNING_PROFILE_IMPLICIT_RULES, // Pseudo-code for setting severities
|
||||
|
||||
DRCE_LAST = DRCE_TUNING_PROFILE_IMPLICIT_RULES
|
||||
DRCE_TRACK_ON_POST_MACHINED_LAYER, // Track connected to pad/via on post-machined/backdrilled layer
|
||||
|
||||
DRCE_LAST = DRCE_TRACK_ON_POST_MACHINED_LAYER
|
||||
};
|
||||
|
||||
|
||||
@@ -248,6 +250,7 @@ private:
|
||||
static DRC_ITEM nonMirroredTextOnBackLayer;
|
||||
static DRC_ITEM missingTuningProfile;
|
||||
static DRC_ITEM tuningProfileImplicitRules;
|
||||
static DRC_ITEM trackOnPostMachinedLayer;
|
||||
|
||||
private:
|
||||
DRC_RULE* m_violatingRule = nullptr;
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
#include <connectivity/connectivity_data.h>
|
||||
#include <connectivity/connectivity_algo.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_track.h>
|
||||
#include <zone.h>
|
||||
#include <drc/drc_item.h>
|
||||
#include <drc/drc_test_provider.h>
|
||||
@@ -112,6 +114,71 @@ bool DRC_TEST_PROVIDER_CONNECTIVITY::Run()
|
||||
}
|
||||
}
|
||||
|
||||
// Test for tracks connecting to post-machined or backdrilled layers
|
||||
if( !m_drcEngine->IsErrorLimitExceeded( DRCE_TRACK_ON_POST_MACHINED_LAYER ) )
|
||||
{
|
||||
for( PCB_TRACK* track : board->Tracks() )
|
||||
{
|
||||
if( m_drcEngine->IsErrorLimitExceeded( DRCE_TRACK_ON_POST_MACHINED_LAYER ) )
|
||||
break;
|
||||
|
||||
// Only check traces and arcs (not vias)
|
||||
if( track->Type() != PCB_TRACE_T && track->Type() != PCB_ARC_T )
|
||||
continue;
|
||||
|
||||
PCB_LAYER_ID layer = track->GetLayer();
|
||||
|
||||
// Get items connected to this track
|
||||
const std::list<CN_ITEM*>& items = connectivity->GetConnectivityAlgo()->ItemEntry( track ).GetItems();
|
||||
|
||||
if( items.empty() )
|
||||
continue;
|
||||
|
||||
CN_ITEM* citem = items.front();
|
||||
|
||||
if( !citem->Valid() )
|
||||
continue;
|
||||
|
||||
for( CN_ITEM* connected : citem->ConnectedItems() )
|
||||
{
|
||||
BOARD_CONNECTED_ITEM* item = connected->Parent();
|
||||
|
||||
if( item->GetFlags() & IS_DELETED )
|
||||
continue;
|
||||
|
||||
bool isPostMachined = false;
|
||||
|
||||
if( item->Type() == PCB_PAD_T )
|
||||
{
|
||||
PAD* pad = static_cast<PAD*>( item );
|
||||
isPostMachined = pad->IsBackdrilledOrPostMachined( layer );
|
||||
}
|
||||
else if( item->Type() == PCB_VIA_T )
|
||||
{
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( item );
|
||||
isPostMachined = via->IsBackdrilledOrPostMachined( layer );
|
||||
}
|
||||
|
||||
if( isPostMachined )
|
||||
{
|
||||
std::shared_ptr<DRC_ITEM> drcItem = DRC_ITEM::Create( DRCE_TRACK_ON_POST_MACHINED_LAYER );
|
||||
drcItem->SetItems( track, item );
|
||||
|
||||
VECTOR2I pos = ( track->GetStart() + track->GetEnd() ) / 2;
|
||||
|
||||
// Use the endpoint that's closer to the pad/via
|
||||
if( item->HitTest( track->GetStart() ) )
|
||||
pos = track->GetStart();
|
||||
else if( item->HitTest( track->GetEnd() ) )
|
||||
pos = track->GetEnd();
|
||||
|
||||
reportViolation( drcItem, pos, layer );
|
||||
break; // Only report once per track
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* test starved zones */
|
||||
for( const auto& [ zone, zoneIslands ] : board->m_ZoneIsolatedIslandsMap )
|
||||
{
|
||||
|
||||
@@ -362,31 +362,55 @@ bool GENDRILL_WRITER_BASE::genDrillMapFile( const wxString& aFullFileName, PLOT_
|
||||
diameter_in_inches( tool.m_Diameter ) );
|
||||
|
||||
msg = From_UTF8( line );
|
||||
const char* extra_info;
|
||||
wxString extraInfo;
|
||||
|
||||
// Add more info for holes with specific constraints (castelleted, pressfit)
|
||||
// note: only PTH pads have this option
|
||||
if( tool.m_HoleAttribute == HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED )
|
||||
extra_info = ", castellated";
|
||||
extraInfo += wxT( ", castellated" );
|
||||
else if( tool.m_HoleAttribute == HOLE_ATTRIBUTE::HOLE_PAD_PRESSFIT )
|
||||
extra_info = ", press-fit";
|
||||
else
|
||||
extra_info = "";
|
||||
extraInfo += wxT( ", press-fit" );
|
||||
|
||||
if( tool.m_IsBackdrill )
|
||||
{
|
||||
if( tool.m_MinStubLength.has_value() )
|
||||
{
|
||||
double minStub = pcbIUScale.IUTomm( *tool.m_MinStubLength );
|
||||
|
||||
if( tool.m_MaxStubLength.has_value()
|
||||
&& tool.m_MaxStubLength != tool.m_MinStubLength )
|
||||
{
|
||||
double maxStub = pcbIUScale.IUTomm( *tool.m_MaxStubLength );
|
||||
extraInfo += wxString::Format( wxT( ", backdrill stub %.3f-%.3fmm" ),
|
||||
minStub, maxStub );
|
||||
}
|
||||
else
|
||||
{
|
||||
extraInfo += wxString::Format( wxT( ", backdrill stub %.3fmm" ), minStub );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
extraInfo += wxT( ", backdrill" );
|
||||
}
|
||||
}
|
||||
|
||||
if( tool.m_HasPostMachining )
|
||||
extraInfo += wxT( ", post-machined" );
|
||||
|
||||
wxString counts;
|
||||
|
||||
// Now list how many holes and ovals are associated with each drill.
|
||||
if( ( tool.m_TotalCount == 1 ) && ( tool.m_OvalCount == 0 ) )
|
||||
snprintf( line, sizeof(line), "(1 hole%s)", extra_info );
|
||||
else if( tool.m_TotalCount == 1 ) // && ( toolm_OvalCount == 1 )
|
||||
snprintf( line, sizeof(line), "(1 slot%s)", extra_info );
|
||||
counts.Printf( wxT( "(1 hole%s)" ), extraInfo );
|
||||
else if( tool.m_TotalCount == 1 )
|
||||
counts.Printf( wxT( "(1 slot%s)" ), extraInfo );
|
||||
else if( tool.m_OvalCount == 0 )
|
||||
snprintf( line, sizeof(line), "(%d holes%s)", tool.m_TotalCount, extra_info );
|
||||
counts.Printf( wxT( "(%d holes%s)" ), tool.m_TotalCount, extraInfo );
|
||||
else if( tool.m_OvalCount == 1 )
|
||||
snprintf( line, sizeof(line), "(%d holes + 1 slot%s)", tool.m_TotalCount - 1, extra_info );
|
||||
else // if ( toolm_OvalCount > 1 )
|
||||
snprintf( line, sizeof(line), "(%d holes + %d slots%s)", tool.m_TotalCount - tool.m_OvalCount,
|
||||
tool.m_OvalCount, extra_info );
|
||||
counts.Printf( wxT( "(%d holes + 1 slot%s)" ), tool.m_TotalCount - 1, extraInfo );
|
||||
else
|
||||
counts.Printf( wxT( "(%d holes + %d slots%s)" ), tool.m_TotalCount - tool.m_OvalCount,
|
||||
tool.m_OvalCount, extraInfo );
|
||||
|
||||
msg += From_UTF8( line );
|
||||
msg += counts;
|
||||
|
||||
if( tool.m_Hole_NotPlated )
|
||||
msg += wxT( " (not plated)" );
|
||||
@@ -424,7 +448,7 @@ bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName )
|
||||
unsigned totalHoleCount;
|
||||
wxFileName brdFilename( m_pcb->GetFileName() );
|
||||
|
||||
std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
|
||||
std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
|
||||
|
||||
out.Print( 0, "Drill report for %s\n", TO_UTF8( brdFilename.GetFullName() ) );
|
||||
out.Print( 0, "Created on %s\n\n", TO_UTF8( GetISO8601CurrentDateTime() ) );
|
||||
@@ -456,29 +480,42 @@ bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName )
|
||||
// in this loop are plated only:
|
||||
for( unsigned pair_ndx = 0; pair_ndx < hole_sets.size(); ++pair_ndx )
|
||||
{
|
||||
DRILL_LAYER_PAIR pair = hole_sets[pair_ndx];
|
||||
const DRILL_SPAN& span = hole_sets[pair_ndx];
|
||||
|
||||
buildHolesList( pair, buildNPTHlist );
|
||||
buildHolesList( span, buildNPTHlist );
|
||||
|
||||
if( pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
if( span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) && !span.m_IsBackdrill )
|
||||
{
|
||||
out.Print( 0, "Drill file '%s' contains\n",
|
||||
TO_UTF8( getDrillFileName( pair, false, m_merge_PTH_NPTH ) ) );
|
||||
TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
|
||||
|
||||
out.Print( 0, " plated through holes:\n" );
|
||||
out.Print( 0, separator );
|
||||
totalHoleCount = printToolSummary( out, false );
|
||||
out.Print( 0, " Total plated holes count %u\n", totalHoleCount );
|
||||
}
|
||||
else // blind/buried
|
||||
else if( span.m_IsBackdrill )
|
||||
{
|
||||
out.Print( 0, "Drill file '%s' contains\n",
|
||||
TO_UTF8( getDrillFileName( pair, false, m_merge_PTH_NPTH ) ) );
|
||||
TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
|
||||
|
||||
out.Print( 0, " backdrill span: '%s' to '%s':\n",
|
||||
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillStartLayer() ) ) ),
|
||||
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.DrillEndLayer() ) ) ) );
|
||||
|
||||
out.Print( 0, separator );
|
||||
totalHoleCount = printToolSummary( out, false );
|
||||
out.Print( 0, " Total backdrilled holes count %u\n", totalHoleCount );
|
||||
}
|
||||
else
|
||||
{
|
||||
out.Print( 0, "Drill file '%s' contains\n",
|
||||
TO_UTF8( getDrillFileName( span, false, m_merge_PTH_NPTH ) ) );
|
||||
|
||||
out.Print( 0, " holes connecting layer pair: '%s and %s' (%s vias):\n",
|
||||
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( pair.first ) ) ),
|
||||
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( pair.second ) ) ),
|
||||
pair.first == F_Cu || pair.second == B_Cu ? "blind" : "buried" );
|
||||
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().first ) ) ),
|
||||
TO_UTF8( m_pcb->GetLayerName( ToLAYER_ID( span.Pair().second ) ) ),
|
||||
span.Pair().first == F_Cu || span.Pair().second == B_Cu ? "blind" : "buried" );
|
||||
|
||||
out.Print( 0, separator );
|
||||
totalHoleCount = printToolSummary( out, false );
|
||||
@@ -493,15 +530,16 @@ bool GENDRILL_WRITER_BASE::GenDrillReportFile( const wxString& aFullFileName )
|
||||
if( !m_merge_PTH_NPTH )
|
||||
buildNPTHlist = true;
|
||||
|
||||
buildHolesList( DRILL_LAYER_PAIR( F_Cu, B_Cu ), buildNPTHlist );
|
||||
DRILL_SPAN npthSpan( F_Cu, B_Cu, false, buildNPTHlist );
|
||||
|
||||
buildHolesList( npthSpan, buildNPTHlist );
|
||||
|
||||
// nothing wrong with an empty NPTH file in report.
|
||||
if( m_merge_PTH_NPTH )
|
||||
out.Print( 0, "Not plated through holes are merged with plated holes\n" );
|
||||
else
|
||||
out.Print( 0, "Drill file '%s' contains\n",
|
||||
TO_UTF8( getDrillFileName( DRILL_LAYER_PAIR( F_Cu, B_Cu ),
|
||||
true, m_merge_PTH_NPTH ) ) );
|
||||
TO_UTF8( getDrillFileName( npthSpan, true, m_merge_PTH_NPTH ) ) );
|
||||
|
||||
out.Print( 0, " unplated through holes:\n" );
|
||||
out.Print( 0, separator );
|
||||
|
||||
@@ -82,29 +82,27 @@ bool EXCELLON_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory,
|
||||
wxFileName fn;
|
||||
wxString msg;
|
||||
|
||||
std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
|
||||
std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
|
||||
|
||||
// append a pair representing the NPTH set of holes, for separate drill files.
|
||||
if( !m_merge_PTH_NPTH )
|
||||
hole_sets.emplace_back( F_Cu, B_Cu );
|
||||
hole_sets.emplace_back( F_Cu, B_Cu, false, true );
|
||||
|
||||
for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin();
|
||||
for( std::vector<DRILL_SPAN>::const_iterator it = hole_sets.begin();
|
||||
it != hole_sets.end(); ++it )
|
||||
{
|
||||
DRILL_LAYER_PAIR pair = *it;
|
||||
// For separate drill files, the last layer pair is the NPTH drill file.
|
||||
bool doing_npth = m_merge_PTH_NPTH ? false : ( it == hole_sets.end() - 1 );
|
||||
const DRILL_SPAN& span = *it;
|
||||
bool doing_npth = m_merge_PTH_NPTH ? false : span.m_IsNonPlatedFile;
|
||||
|
||||
buildHolesList( pair, doing_npth );
|
||||
buildHolesList( span, doing_npth );
|
||||
|
||||
// The file is created if it has holes, or if it is the non plated drill file to be
|
||||
// sure the NPTH file is up to date in separate files mode.
|
||||
// Also a PTH drill/map file is always created, to be sure at least one plated hole
|
||||
// drill file is created (do not create any PTH drill file can be seen as not working
|
||||
// drill generator).
|
||||
if( getHolesCount() > 0 || doing_npth || pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
if( getHolesCount() > 0 || doing_npth || span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
{
|
||||
fn = getDrillFileName( pair, doing_npth, m_merge_PTH_NPTH );
|
||||
fn = getDrillFileName( span, doing_npth, m_merge_PTH_NPTH );
|
||||
fn.SetPath( aPlotDirectory );
|
||||
|
||||
if( aGenDrill )
|
||||
@@ -135,19 +133,24 @@ bool EXCELLON_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory,
|
||||
|
||||
TYPE_FILE file_type = TYPE_FILE::PTH_FILE;
|
||||
|
||||
// Only external layer pair can have non plated hole
|
||||
// internal layers have only plated via holes
|
||||
if( pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
if( span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) && !span.m_IsBackdrill )
|
||||
{
|
||||
if( m_merge_PTH_NPTH )
|
||||
file_type = TYPE_FILE::MIXED_FILE;
|
||||
else if( doing_npth )
|
||||
file_type = TYPE_FILE::NPTH_FILE;
|
||||
}
|
||||
else if( span.m_IsBackdrill )
|
||||
{
|
||||
file_type = TYPE_FILE::NPTH_FILE;
|
||||
}
|
||||
|
||||
bool wroteDrillFile = false;
|
||||
|
||||
try
|
||||
{
|
||||
createDrillFile( file, pair, file_type );
|
||||
createDrillFile( file, span, file_type );
|
||||
wroteDrillFile = true;
|
||||
}
|
||||
catch( ... ) // Capture fmt::print exception on write issues
|
||||
{
|
||||
@@ -156,6 +159,15 @@ bool EXCELLON_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory,
|
||||
aReporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
success = false;
|
||||
}
|
||||
|
||||
if( wroteDrillFile && span.m_IsBackdrill && getHolesCount() > 0 )
|
||||
{
|
||||
if( !writeBackdrillLayerPairFile( aPlotDirectory, aReporter, span ) )
|
||||
{
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -187,6 +199,10 @@ void EXCELLON_WRITER::writeHoleAttribute( HOLE_ATTRIBUTE aAttribute )
|
||||
fmt::print( m_file, "{}", "; #@! TA.AperFunction,Plated,Buried,ViaDrill\n" );
|
||||
break;
|
||||
|
||||
case HOLE_ATTRIBUTE::HOLE_VIA_BACKDRILL:
|
||||
fmt::print( m_file, "{}", "; #@! TA.AperFunction,NonPlated,BackDrill\n" );
|
||||
break;
|
||||
|
||||
case HOLE_ATTRIBUTE::HOLE_PAD:
|
||||
//case HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED:
|
||||
fmt::print( m_file, "{}", "; #@! TA.AperFunction,Plated,PTH,ComponentDrill\n" );
|
||||
@@ -212,8 +228,8 @@ void EXCELLON_WRITER::writeHoleAttribute( HOLE_ATTRIBUTE aAttribute )
|
||||
}
|
||||
|
||||
|
||||
int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair,
|
||||
TYPE_FILE aHolesType )
|
||||
int EXCELLON_WRITER::createDrillFile( FILE* aFile, const DRILL_SPAN& aSpan,
|
||||
TYPE_FILE aHolesType, bool aTagBackdrillHit )
|
||||
{
|
||||
// if units are mm, the resolution is 0.001 mm (3 digits in mantissa)
|
||||
// if units are inches, the resolution is 0.1 mil (4 digits in mantissa)
|
||||
@@ -226,7 +242,7 @@ int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair,
|
||||
double xt, yt;
|
||||
char line[1024];
|
||||
|
||||
writeEXCELLONHeader( aLayerPair, aHolesType );
|
||||
writeEXCELLONHeader( aSpan, aHolesType );
|
||||
|
||||
holes_count = 0;
|
||||
|
||||
@@ -239,6 +255,44 @@ int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair,
|
||||
writeHoleAttribute( tool_descr.m_HoleAttribute );
|
||||
#endif
|
||||
fmt::print( m_file, "T{}C{:.{}f}\n", ii + 1, tool_descr.m_Diameter * m_conversionUnits, m_mantissaLenght );
|
||||
|
||||
if( !m_minimalHeader )
|
||||
{
|
||||
if( tool_descr.m_IsBackdrill )
|
||||
{
|
||||
auto formatStub = [&]( int aStubLength )
|
||||
{
|
||||
double stubMM = pcbIUScale.IUTomm( aStubLength );
|
||||
double stubInches = stubMM / 25.4;
|
||||
return wxString::Format( wxT( "%.3fmm (%.4f\")" ), stubMM, stubInches );
|
||||
};
|
||||
|
||||
wxString comment = wxT( "; Backdrill" );
|
||||
|
||||
if( tool_descr.m_MinStubLength.has_value() )
|
||||
{
|
||||
comment += wxT( " stub " );
|
||||
comment += formatStub( *tool_descr.m_MinStubLength );
|
||||
|
||||
if( tool_descr.m_MaxStubLength.has_value()
|
||||
&& tool_descr.m_MaxStubLength != tool_descr.m_MinStubLength )
|
||||
{
|
||||
comment += wxT( " to " );
|
||||
comment += formatStub( *tool_descr.m_MaxStubLength );
|
||||
}
|
||||
}
|
||||
|
||||
if( tool_descr.m_HasPostMachining )
|
||||
comment += wxT( ", post-machining" );
|
||||
|
||||
comment += wxT( "\n" );
|
||||
fmt::print( m_file, "{}", TO_UTF8( comment ) );
|
||||
}
|
||||
else if( tool_descr.m_HasPostMachining )
|
||||
{
|
||||
fmt::print( m_file, "{}", "; Post-machining\n" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt::print( m_file, "{}", "%\n" ); // End of header info
|
||||
@@ -270,6 +324,7 @@ int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair,
|
||||
|
||||
xt = x0 * m_conversionUnits;
|
||||
yt = y0 * m_conversionUnits;
|
||||
writeHoleComments( hole_descr, aTagBackdrillHit );
|
||||
writeCoordinates( line, sizeof( line ), xt, yt );
|
||||
|
||||
fmt::print( m_file, "{}", line );
|
||||
@@ -328,6 +383,7 @@ int EXCELLON_WRITER::createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair,
|
||||
|
||||
xt = x0 * m_conversionUnits;
|
||||
yt = y0 * m_conversionUnits;
|
||||
writeHoleComments( hole_descr, aTagBackdrillHit );
|
||||
|
||||
if( m_useRouteModeForOval )
|
||||
fmt::print( m_file, "{}", "G00" ); // Select the routing mode
|
||||
@@ -498,7 +554,7 @@ void EXCELLON_WRITER::writeCoordinates( char* aLine, size_t aLineSize, double aC
|
||||
}
|
||||
|
||||
|
||||
void EXCELLON_WRITER::writeEXCELLONHeader( DRILL_LAYER_PAIR aLayerPair, TYPE_FILE aHolesType )
|
||||
void EXCELLON_WRITER::writeEXCELLONHeader( const DRILL_SPAN& aSpan, TYPE_FILE aHolesType )
|
||||
{
|
||||
fmt::print( m_file, "{}", "M48\n" ); // The beginning of a header
|
||||
|
||||
@@ -555,7 +611,7 @@ void EXCELLON_WRITER::writeEXCELLONHeader( DRILL_LAYER_PAIR aLayerPair, TYPE_FIL
|
||||
|
||||
// Add the standard X2 FileFunction for drill files
|
||||
// TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH]
|
||||
msg = BuildFileFunctionAttributeString( aLayerPair, aHolesType , true ) + wxT( "\n" );
|
||||
msg = BuildFileFunctionAttributeString( aSpan, aHolesType , true ) + wxT( "\n" );
|
||||
fmt::print( m_file, "{}", TO_UTF8( msg ) );
|
||||
|
||||
fmt::print( m_file, "{}", "FMAT,2\n" ); // Use Format 2 commands (version used since 1979)
|
||||
@@ -591,3 +647,140 @@ void EXCELLON_WRITER::writeEXCELLONEndOfFile()
|
||||
fmt::print( m_file, "{}", "M30\n" );
|
||||
fclose( m_file );
|
||||
}
|
||||
|
||||
|
||||
wxFileName EXCELLON_WRITER::getBackdrillLayerPairFileName( const DRILL_SPAN& aSpan ) const
|
||||
{
|
||||
wxFileName fn = m_pcb->GetFileName();
|
||||
wxString extend;
|
||||
|
||||
extend << wxT( "-" )
|
||||
<< wxString::FromUTF8( layerPairName( aSpan.Pair() ).c_str() )
|
||||
<< wxT( "-backdrill" );
|
||||
|
||||
fn.SetName( fn.GetName() + extend );
|
||||
fn.SetExt( m_drillFileExtension );
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
bool EXCELLON_WRITER::writeBackdrillLayerPairFile( const wxString& aPlotDirectory,
|
||||
REPORTER* aReporter, const DRILL_SPAN& aSpan )
|
||||
{
|
||||
wxFileName fn = getBackdrillLayerPairFileName( aSpan );
|
||||
fn.SetPath( aPlotDirectory );
|
||||
|
||||
wxString fullFilename = fn.GetFullPath();
|
||||
FILE* file = wxFopen( fullFilename, wxT( "w" ) );
|
||||
|
||||
if( file == nullptr )
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
wxString msg;
|
||||
msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else if( aReporter )
|
||||
{
|
||||
wxString msg;
|
||||
msg.Printf( _( "Created file '%s'" ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
createDrillFile( file, aSpan, TYPE_FILE::NPTH_FILE, true );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
fclose( file );
|
||||
|
||||
if( aReporter )
|
||||
{
|
||||
wxString msg;
|
||||
msg.Printf( _( "Failed to write file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void EXCELLON_WRITER::writeHoleComments( const HOLE_INFO& aHole, bool aTagBackdrillHit )
|
||||
{
|
||||
if( aTagBackdrillHit && aHole.m_IsBackdrill )
|
||||
fmt::print( m_file, "{}", "; backdrill\n" );
|
||||
|
||||
writePostMachiningComment( aHole.m_FrontPostMachining, aHole.m_FrontPostMachiningSize,
|
||||
aHole.m_FrontPostMachiningDepth, aHole.m_FrontPostMachiningAngle,
|
||||
wxT( "front" ) );
|
||||
|
||||
writePostMachiningComment( aHole.m_BackPostMachining, aHole.m_BackPostMachiningSize,
|
||||
aHole.m_BackPostMachiningDepth, aHole.m_BackPostMachiningAngle,
|
||||
wxT( "back" ) );
|
||||
}
|
||||
|
||||
|
||||
void EXCELLON_WRITER::writePostMachiningComment( PAD_DRILL_POST_MACHINING_MODE aMode,
|
||||
int aSizeIU, int aDepthIU, int aAngleDeciDegree,
|
||||
const wxString& aSideLabel )
|
||||
{
|
||||
if( aMode != PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE
|
||||
&& aMode != PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wxString comment;
|
||||
comment << wxT( "; Post-machining " ) << aSideLabel << wxT( " " )
|
||||
<< ( aMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ? wxT( "countersink" )
|
||||
: wxT( "counterbore" ) );
|
||||
|
||||
wxString sizeStr = formatLinearValue( aSizeIU );
|
||||
|
||||
if( !sizeStr.IsEmpty() )
|
||||
comment << wxT( " dia " ) << sizeStr;
|
||||
|
||||
wxString depthStr = formatLinearValue( aDepthIU );
|
||||
|
||||
if( !depthStr.IsEmpty() )
|
||||
comment << wxT( " depth " ) << depthStr;
|
||||
|
||||
if( aMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && aAngleDeciDegree > 0 )
|
||||
{
|
||||
double angle = aAngleDeciDegree / 10.0;
|
||||
wxString angleStr;
|
||||
|
||||
if( ( aAngleDeciDegree % 10 ) == 0 )
|
||||
angleStr.Printf( wxT( "%.0fdeg" ), angle );
|
||||
else
|
||||
angleStr.Printf( wxT( "%.1fdeg" ), angle );
|
||||
|
||||
comment << wxT( " angle " ) << angleStr;
|
||||
}
|
||||
|
||||
comment << wxT( "\n" );
|
||||
fmt::print( m_file, "{}", TO_UTF8( comment ) );
|
||||
}
|
||||
|
||||
|
||||
wxString EXCELLON_WRITER::formatLinearValue( int aValueIU ) const
|
||||
{
|
||||
if( aValueIU <= 0 )
|
||||
return wxString();
|
||||
|
||||
double converted = aValueIU * m_conversionUnits;
|
||||
wxString value;
|
||||
value.Printf( wxT( "%.*f" ), m_mantissaLenght, converted );
|
||||
value << ( m_unitsMetric ? wxT( "mm" ) : wxT( "in" ) );
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
#ifndef _GENDRILL_EXCELLON_WRITER_
|
||||
#define _GENDRILL_EXCELLON_WRITER_
|
||||
|
||||
#include <gendrill_file_writer_base.h>
|
||||
#include "gendrill_file_writer_base.h"
|
||||
|
||||
#include <wx/filename.h>
|
||||
|
||||
class BOARD;
|
||||
class PLOTTER;
|
||||
@@ -117,7 +119,8 @@ private:
|
||||
* @param aHolesType is the holes type (PTH, NPTH, mixed).
|
||||
* @return the hole count.
|
||||
*/
|
||||
int createDrillFile( FILE* aFile, DRILL_LAYER_PAIR aLayerPair, TYPE_FILE aHolesType );
|
||||
int createDrillFile( FILE* aFile, const DRILL_SPAN& aSpan, TYPE_FILE aHolesType,
|
||||
bool aTagBackdrillHit = false );
|
||||
|
||||
|
||||
/**
|
||||
@@ -133,10 +136,10 @@ private:
|
||||
* FMAT,2
|
||||
* INCH,TZ
|
||||
*
|
||||
* @param aLayerPair is the layer pair for the current holes.
|
||||
* @param aSpan is the drilling span for the current holes.
|
||||
* @param aHolesType is the holes type in file (PTH, NPTH, mixed).
|
||||
*/
|
||||
void writeEXCELLONHeader( DRILL_LAYER_PAIR aLayerPair, TYPE_FILE aHolesType );
|
||||
void writeEXCELLONHeader( const DRILL_SPAN& aSpan, TYPE_FILE aHolesType );
|
||||
|
||||
void writeEXCELLONEndOfFile();
|
||||
|
||||
@@ -152,6 +155,15 @@ private:
|
||||
*/
|
||||
void writeHoleAttribute( HOLE_ATTRIBUTE aAttribute );
|
||||
|
||||
wxFileName getBackdrillLayerPairFileName( const DRILL_SPAN& aSpan ) const;
|
||||
bool writeBackdrillLayerPairFile( const wxString& aPlotDirectory,
|
||||
REPORTER* aReporter, const DRILL_SPAN& aSpan );
|
||||
void writeHoleComments( const HOLE_INFO& aHole, bool aTagBackdrillHit );
|
||||
void writePostMachiningComment( PAD_DRILL_POST_MACHINING_MODE aMode, int aSizeIU,
|
||||
int aDepthIU, int aAngleDeciDegree,
|
||||
const wxString& aSideLabel );
|
||||
wxString formatLinearValue( int aValueIU ) const;
|
||||
|
||||
FILE* m_file; // The output file
|
||||
bool m_minimalHeader; // True to use minimal header
|
||||
bool m_mirror;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
|
||||
#include <board.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <footprint.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_track.h>
|
||||
@@ -31,6 +32,8 @@
|
||||
#include <reporter.h>
|
||||
#include <richio.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <gendrill_file_writer_base.h>
|
||||
|
||||
|
||||
@@ -64,17 +67,25 @@ static bool cmpHoleSorting( const HOLE_INFO& a, const HOLE_INFO& b )
|
||||
}
|
||||
|
||||
|
||||
void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list )
|
||||
void GENDRILL_WRITER_BASE::buildHolesList( const DRILL_SPAN& aSpan, bool aGenerateNPTH_list )
|
||||
{
|
||||
HOLE_INFO new_hole;
|
||||
|
||||
m_holeListBuffer.clear();
|
||||
m_toolListBuffer.clear();
|
||||
|
||||
wxASSERT( aLayerPair.first < aLayerPair.second ); // fix the caller
|
||||
wxASSERT( aSpan.TopLayer() < aSpan.BottomLayer() ); // fix the caller
|
||||
|
||||
// build hole list for vias
|
||||
if( !aGenerateNPTH_list ) // vias are always plated !
|
||||
auto computeStubLength = [&]( PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer )
|
||||
{
|
||||
if( aStartLayer == UNDEFINED_LAYER || aEndLayer == UNDEFINED_LAYER )
|
||||
return std::optional<int>();
|
||||
|
||||
BOARD_STACKUP& stackup = m_pcb->GetDesignSettings().GetStackupDescriptor();
|
||||
return std::optional<int>( stackup.GetLayerDistance( aStartLayer, aEndLayer ) );
|
||||
};
|
||||
|
||||
if( !aGenerateNPTH_list )
|
||||
{
|
||||
for( PCB_TRACK* track : m_pcb->Tracks() )
|
||||
{
|
||||
@@ -82,54 +93,141 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, bool aGe
|
||||
continue;
|
||||
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( track );
|
||||
int hole_sz = via->GetDrillValue();
|
||||
|
||||
if( hole_sz == 0 ) // Should not occur.
|
||||
if( aSpan.m_IsBackdrill )
|
||||
{
|
||||
const PADSTACK::DRILL_PROPS& secondary = via->Padstack().SecondaryDrill();
|
||||
|
||||
if( secondary.start == UNDEFINED_LAYER || secondary.end == UNDEFINED_LAYER )
|
||||
continue;
|
||||
|
||||
DRILL_LAYER_PAIR secondaryPair( std::min( secondary.start, secondary.end ),
|
||||
std::max( secondary.start, secondary.end ) );
|
||||
|
||||
if( secondaryPair != aSpan.Pair() )
|
||||
continue;
|
||||
|
||||
if( secondary.start != aSpan.DrillStartLayer()
|
||||
|| secondary.end != aSpan.DrillEndLayer() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if( secondary.size.x <= 0 && secondary.size.y <= 0 )
|
||||
continue;
|
||||
|
||||
new_hole = HOLE_INFO();
|
||||
new_hole.m_ItemParent = via;
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_VIA_BACKDRILL;
|
||||
new_hole.m_Tool_Reference = -1;
|
||||
new_hole.m_Hole_Orient = ANGLE_0;
|
||||
new_hole.m_Hole_NotPlated = true;
|
||||
new_hole.m_Hole_Shape = 0;
|
||||
new_hole.m_Hole_Pos = via->GetStart();
|
||||
new_hole.m_Hole_Top_Layer = aSpan.TopLayer();
|
||||
new_hole.m_Hole_Bottom_Layer = aSpan.BottomLayer();
|
||||
|
||||
int diameter = secondary.size.x;
|
||||
|
||||
if( secondary.size.y > 0 )
|
||||
diameter = ( diameter > 0 ) ? std::min( diameter, secondary.size.y )
|
||||
: secondary.size.y;
|
||||
|
||||
new_hole.m_Hole_Diameter = diameter;
|
||||
new_hole.m_Hole_Size = secondary.size;
|
||||
|
||||
if( secondary.shape != PAD_DRILL_SHAPE::CIRCLE
|
||||
&& secondary.size.x != secondary.size.y )
|
||||
{
|
||||
new_hole.m_Hole_Shape = 1;
|
||||
}
|
||||
|
||||
new_hole.m_Hole_Filled = secondary.is_filled.value_or( false );
|
||||
new_hole.m_Hole_Capped = secondary.is_capped.value_or( false );
|
||||
new_hole.m_Hole_Top_Covered = via->Padstack().IsCovered( new_hole.m_Hole_Top_Layer )
|
||||
.value_or( false );
|
||||
new_hole.m_Hole_Bot_Covered = via->Padstack().IsCovered( new_hole.m_Hole_Bottom_Layer )
|
||||
.value_or( false );
|
||||
new_hole.m_Hole_Top_Plugged = via->Padstack().IsPlugged( new_hole.m_Hole_Top_Layer )
|
||||
.value_or( false );
|
||||
new_hole.m_Hole_Bot_Plugged = via->Padstack().IsPlugged( new_hole.m_Hole_Bottom_Layer )
|
||||
.value_or( false );
|
||||
new_hole.m_Hole_Top_Tented = via->Padstack().IsTented( new_hole.m_Hole_Top_Layer )
|
||||
.value_or( false );
|
||||
new_hole.m_Hole_Bot_Tented = via->Padstack().IsTented( new_hole.m_Hole_Bottom_Layer )
|
||||
.value_or( false );
|
||||
new_hole.m_IsBackdrill = true;
|
||||
new_hole.m_FrontPostMachining = PAD_DRILL_POST_MACHINING_MODE::UNKNOWN;
|
||||
new_hole.m_FrontPostMachiningSize = 0;
|
||||
new_hole.m_FrontPostMachiningDepth = 0;
|
||||
new_hole.m_FrontPostMachiningAngle = 0;
|
||||
new_hole.m_BackPostMachining = PAD_DRILL_POST_MACHINING_MODE::UNKNOWN;
|
||||
new_hole.m_BackPostMachiningSize = 0;
|
||||
new_hole.m_BackPostMachiningDepth = 0;
|
||||
new_hole.m_BackPostMachiningAngle = 0;
|
||||
new_hole.m_DrillStart = secondary.start;
|
||||
new_hole.m_DrillEnd = secondary.end;
|
||||
new_hole.m_StubLength = computeStubLength( secondary.start, secondary.end );
|
||||
|
||||
m_holeListBuffer.push_back( new_hole );
|
||||
continue;
|
||||
}
|
||||
|
||||
int hole_sz = via->GetDrillValue();
|
||||
|
||||
if( hole_sz == 0 )
|
||||
continue;
|
||||
|
||||
PCB_LAYER_ID top_layer;
|
||||
PCB_LAYER_ID bottom_layer;
|
||||
via->LayerPair( &top_layer, &bottom_layer );
|
||||
|
||||
if( DRILL_LAYER_PAIR( top_layer, bottom_layer ) != aSpan.Pair() )
|
||||
continue;
|
||||
|
||||
new_hole = HOLE_INFO();
|
||||
new_hole.m_ItemParent = via;
|
||||
|
||||
if( aLayerPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
if( aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_VIA_THROUGH;
|
||||
else
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_VIA_BURIED;
|
||||
|
||||
new_hole.m_Tool_Reference = -1; // Flag value for Not initialized
|
||||
new_hole.m_Hole_Orient = ANGLE_0;
|
||||
new_hole.m_Hole_Diameter = hole_sz;
|
||||
new_hole.m_Tool_Reference = -1;
|
||||
new_hole.m_Hole_Orient = ANGLE_0;
|
||||
new_hole.m_Hole_Diameter = hole_sz;
|
||||
new_hole.m_Hole_NotPlated = false;
|
||||
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
|
||||
|
||||
new_hole.m_Hole_Shape = 0; // hole shape: round
|
||||
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
|
||||
new_hole.m_Hole_Shape = 0;
|
||||
new_hole.m_Hole_Pos = via->GetStart();
|
||||
|
||||
via->LayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer );
|
||||
|
||||
new_hole.m_Hole_Top_Layer = top_layer;
|
||||
new_hole.m_Hole_Bottom_Layer = bottom_layer;
|
||||
new_hole.m_Hole_Filled = via->Padstack().IsFilled().value_or( false );
|
||||
new_hole.m_Hole_Capped = via->Padstack().IsCapped().value_or( false );
|
||||
|
||||
new_hole.m_Hole_Top_Covered = via->Padstack().IsCovered( new_hole.m_Hole_Top_Layer ).value_or( false );
|
||||
new_hole.m_Hole_Bot_Covered = via->Padstack().IsCovered( new_hole.m_Hole_Bottom_Layer ).value_or( false );
|
||||
|
||||
new_hole.m_Hole_Top_Plugged = via->Padstack().IsPlugged( new_hole.m_Hole_Top_Layer ).value_or( false );
|
||||
new_hole.m_Hole_Bot_Plugged = via->Padstack().IsPlugged( new_hole.m_Hole_Bottom_Layer ).value_or( false );
|
||||
|
||||
new_hole.m_Hole_Top_Tented = via->Padstack().IsTented( new_hole.m_Hole_Top_Layer ).value_or( false );
|
||||
new_hole.m_Hole_Bot_Tented = via->Padstack().IsTented( new_hole.m_Hole_Bottom_Layer ).value_or( false );
|
||||
|
||||
// LayerPair() returns params with m_Hole_Bottom_Layer > m_Hole_Top_Layer
|
||||
// Remember: top layer = 0 and bottom layer = 31 for through hole vias
|
||||
// Any captured via should be from aLayerPair.first to aLayerPair.second exactly.
|
||||
if( new_hole.m_Hole_Top_Layer != aLayerPair.first || new_hole.m_Hole_Bottom_Layer != aLayerPair.second )
|
||||
continue;
|
||||
new_hole.m_Hole_Top_Covered = via->Padstack().IsCovered( top_layer ).value_or( false );
|
||||
new_hole.m_Hole_Bot_Covered = via->Padstack().IsCovered( bottom_layer ).value_or( false );
|
||||
new_hole.m_Hole_Top_Plugged = via->Padstack().IsPlugged( top_layer ).value_or( false );
|
||||
new_hole.m_Hole_Bot_Plugged = via->Padstack().IsPlugged( bottom_layer ).value_or( false );
|
||||
new_hole.m_Hole_Top_Tented = via->Padstack().IsTented( top_layer ).value_or( false );
|
||||
new_hole.m_Hole_Bot_Tented = via->Padstack().IsTented( bottom_layer ).value_or( false );
|
||||
new_hole.m_IsBackdrill = false;
|
||||
new_hole.m_FrontPostMachining = via->Padstack().FrontPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
|
||||
new_hole.m_FrontPostMachiningSize = via->Padstack().FrontPostMachining().size;
|
||||
new_hole.m_FrontPostMachiningDepth = via->Padstack().FrontPostMachining().depth;
|
||||
new_hole.m_FrontPostMachiningAngle = via->Padstack().FrontPostMachining().angle;
|
||||
new_hole.m_BackPostMachining = via->Padstack().BackPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
|
||||
new_hole.m_BackPostMachiningSize = via->Padstack().BackPostMachining().size;
|
||||
new_hole.m_BackPostMachiningDepth = via->Padstack().BackPostMachining().depth;
|
||||
new_hole.m_BackPostMachiningAngle = via->Padstack().BackPostMachining().angle;
|
||||
new_hole.m_DrillStart = via->Padstack().Drill().start;
|
||||
new_hole.m_DrillEnd = bottom_layer;
|
||||
|
||||
m_holeListBuffer.push_back( new_hole );
|
||||
}
|
||||
}
|
||||
|
||||
if( aLayerPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
if( !aSpan.m_IsBackdrill && aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
{
|
||||
// add holes for thru hole pads
|
||||
for( FOOTPRINT* footprint : m_pcb->Footprints() )
|
||||
{
|
||||
for( PAD* pad : footprint->Pads() )
|
||||
@@ -146,35 +244,38 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, bool aGe
|
||||
if( pad->GetDrillSize().x == 0 )
|
||||
continue;
|
||||
|
||||
new_hole.m_ItemParent = pad;
|
||||
new_hole = HOLE_INFO();
|
||||
new_hole.m_ItemParent = pad;
|
||||
new_hole.m_Hole_NotPlated = ( pad->GetAttribute() == PAD_ATTRIB::NPTH );
|
||||
|
||||
if( new_hole.m_Hole_NotPlated )
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_MECHANICAL;
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_MECHANICAL;
|
||||
else
|
||||
{
|
||||
if( pad->GetProperty() == PAD_PROP::CASTELLATED )
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED;
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_PAD_CASTELLATED;
|
||||
else if( pad->GetProperty() == PAD_PROP::PRESSFIT )
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_PAD_PRESSFIT;
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_PAD_PRESSFIT;
|
||||
else
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_PAD;
|
||||
new_hole.m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_PAD;
|
||||
}
|
||||
|
||||
new_hole.m_Tool_Reference = -1; // Flag is: Not initialized
|
||||
new_hole.m_Hole_Orient = pad->GetOrientation();
|
||||
new_hole.m_Hole_Shape = 0; // hole shape: round
|
||||
new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
|
||||
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
|
||||
new_hole.m_Tool_Reference = -1;
|
||||
new_hole.m_Hole_Orient = pad->GetOrientation();
|
||||
new_hole.m_Hole_Shape = 0;
|
||||
new_hole.m_Hole_Diameter = std::min( pad->GetDrillSize().x, pad->GetDrillSize().y );
|
||||
new_hole.m_Hole_Size.x = new_hole.m_Hole_Size.y = new_hole.m_Hole_Diameter;
|
||||
|
||||
// Convert oblong holes that are actually circular into drill hits
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE && pad->GetDrillSizeX() != pad->GetDrillSizeY() )
|
||||
new_hole.m_Hole_Shape = 1; // oval flag set
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE
|
||||
&& pad->GetDrillSizeX() != pad->GetDrillSizeY() )
|
||||
{
|
||||
new_hole.m_Hole_Shape = 1;
|
||||
}
|
||||
|
||||
new_hole.m_Hole_Size = pad->GetDrillSize();
|
||||
new_hole.m_Hole_Pos = pad->GetPosition(); // hole position
|
||||
new_hole.m_Hole_Size = pad->GetDrillSize();
|
||||
new_hole.m_Hole_Pos = pad->GetPosition();
|
||||
new_hole.m_Hole_Bottom_Layer = B_Cu;
|
||||
new_hole.m_Hole_Top_Layer = F_Cu; // pad holes are through holes
|
||||
new_hole.m_Hole_Top_Layer = F_Cu;
|
||||
m_holeListBuffer.push_back( new_hole );
|
||||
}
|
||||
}
|
||||
@@ -221,39 +322,80 @@ void GENDRILL_WRITER_BASE::buildHolesList( DRILL_LAYER_PAIR aLayerPair, bool aGe
|
||||
|
||||
if( m_holeListBuffer[ii].m_Hole_Shape )
|
||||
m_toolListBuffer.back().m_OvalCount++;
|
||||
|
||||
if( m_holeListBuffer[ii].m_IsBackdrill )
|
||||
{
|
||||
m_toolListBuffer.back().m_IsBackdrill = true;
|
||||
|
||||
if( m_holeListBuffer[ii].m_StubLength.has_value() )
|
||||
{
|
||||
int stub = *m_holeListBuffer[ii].m_StubLength;
|
||||
|
||||
if( !m_toolListBuffer.back().m_MinStubLength.has_value()
|
||||
|| stub < *m_toolListBuffer.back().m_MinStubLength )
|
||||
{
|
||||
m_toolListBuffer.back().m_MinStubLength = stub;
|
||||
}
|
||||
|
||||
if( !m_toolListBuffer.back().m_MaxStubLength.has_value()
|
||||
|| stub > *m_toolListBuffer.back().m_MaxStubLength )
|
||||
{
|
||||
m_toolListBuffer.back().m_MaxStubLength = stub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( m_holeListBuffer[ii].m_FrontPostMachining == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE
|
||||
|| m_holeListBuffer[ii].m_FrontPostMachining == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK
|
||||
|| m_holeListBuffer[ii].m_BackPostMachining == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE
|
||||
|| m_holeListBuffer[ii].m_BackPostMachining == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK
|
||||
|| m_holeListBuffer[ii].m_IsBackdrill )
|
||||
m_toolListBuffer.back().m_HasPostMachining = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<DRILL_LAYER_PAIR> GENDRILL_WRITER_BASE::getUniqueLayerPairs() const
|
||||
std::vector<DRILL_SPAN> GENDRILL_WRITER_BASE::getUniqueLayerPairs() const
|
||||
{
|
||||
wxASSERT( m_pcb );
|
||||
|
||||
PCB_TYPE_COLLECTOR vias;
|
||||
PCB_TYPE_COLLECTOR vias;
|
||||
|
||||
vias.Collect( m_pcb, { PCB_VIA_T } );
|
||||
|
||||
std::set<DRILL_LAYER_PAIR> unique;
|
||||
DRILL_LAYER_PAIR layer_pair;
|
||||
std::set<DRILL_SPAN> unique;
|
||||
|
||||
for( int i = 0; i < vias.GetCount(); ++i )
|
||||
{
|
||||
PCB_VIA* v = static_cast<PCB_VIA*>( vias[i] );
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( vias[i] );
|
||||
PCB_LAYER_ID top_layer;
|
||||
PCB_LAYER_ID bottom_layer;
|
||||
|
||||
v->LayerPair( &layer_pair.first, &layer_pair.second );
|
||||
via->LayerPair( &top_layer, &bottom_layer );
|
||||
|
||||
// only make note of blind buried.
|
||||
// thru hole is placed unconditionally as first in fetched list.
|
||||
if( layer_pair != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
unique.insert( layer_pair );
|
||||
if( DRILL_LAYER_PAIR( top_layer, bottom_layer ) != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
unique.emplace( top_layer, bottom_layer, false, false );
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secondary = via->Padstack().SecondaryDrill();
|
||||
|
||||
if( secondary.start == UNDEFINED_LAYER || secondary.end == UNDEFINED_LAYER )
|
||||
continue;
|
||||
|
||||
if( secondary.size.x <= 0 && secondary.size.y <= 0 )
|
||||
continue;
|
||||
|
||||
unique.emplace( secondary.start, secondary.end, true, false );
|
||||
}
|
||||
|
||||
std::vector<DRILL_LAYER_PAIR> ret;
|
||||
std::vector<DRILL_SPAN> ret;
|
||||
|
||||
ret.emplace_back( F_Cu, B_Cu ); // always first in returned list
|
||||
ret.emplace_back( F_Cu, B_Cu, false, false );
|
||||
|
||||
for( const DRILL_LAYER_PAIR& pair : unique )
|
||||
ret.push_back( pair );
|
||||
for( const DRILL_SPAN& span : unique )
|
||||
{
|
||||
if( span.m_IsBackdrill || span.Pair() != DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
ret.push_back( span );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -288,18 +430,39 @@ const std::string GENDRILL_WRITER_BASE::layerPairName( DRILL_LAYER_PAIR aPair )
|
||||
}
|
||||
|
||||
|
||||
const wxString GENDRILL_WRITER_BASE::getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
|
||||
const wxString GENDRILL_WRITER_BASE::getDrillFileName( const DRILL_SPAN& aSpan, bool aNPTH,
|
||||
bool aMerge_PTH_NPTH ) const
|
||||
{
|
||||
wxASSERT( m_pcb );
|
||||
|
||||
wxString extend;
|
||||
|
||||
if( aNPTH )
|
||||
auto layerIndex = [&]( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
int conventional_layer_num = 1;
|
||||
|
||||
for( PCB_LAYER_ID layer : LSET::AllCuMask( m_pcb->GetCopperLayerCount() ).UIOrder() )
|
||||
{
|
||||
if( layer == aLayer )
|
||||
return conventional_layer_num;
|
||||
|
||||
conventional_layer_num++;
|
||||
}
|
||||
|
||||
return conventional_layer_num;
|
||||
};
|
||||
|
||||
if( aSpan.m_IsBackdrill )
|
||||
{
|
||||
extend.Printf( wxT( "_Backdrills_Drill_%d_%d" ),
|
||||
layerIndex( aSpan.DrillStartLayer() ),
|
||||
layerIndex( aSpan.DrillEndLayer() ) );
|
||||
}
|
||||
else if( aNPTH )
|
||||
{
|
||||
extend = wxT( "-NPTH" );
|
||||
}
|
||||
else if( aPair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
else if( aSpan.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
{
|
||||
if( !aMerge_PTH_NPTH )
|
||||
extend = wxT( "-PTH" );
|
||||
@@ -308,7 +471,7 @@ const wxString GENDRILL_WRITER_BASE::getDrillFileName( DRILL_LAYER_PAIR aPair, b
|
||||
else
|
||||
{
|
||||
extend += '-';
|
||||
extend += layerPairName( aPair );
|
||||
extend += layerPairName( aSpan.Pair() );
|
||||
}
|
||||
|
||||
wxFileName fn = m_pcb->GetFileName();
|
||||
@@ -322,7 +485,7 @@ const wxString GENDRILL_WRITER_BASE::getDrillFileName( DRILL_LAYER_PAIR aPair, b
|
||||
}
|
||||
|
||||
|
||||
const wxString GENDRILL_WRITER_BASE::getProtectionFileName( DRILL_LAYER_PAIR aPair,
|
||||
const wxString GENDRILL_WRITER_BASE::getProtectionFileName( const DRILL_SPAN& aSpan,
|
||||
IPC4761_FEATURES aFeature ) const
|
||||
{
|
||||
wxASSERT( m_pcb );
|
||||
@@ -333,35 +496,35 @@ const wxString GENDRILL_WRITER_BASE::getProtectionFileName( DRILL_LAYER_PAIR aPa
|
||||
{
|
||||
case IPC4761_FEATURES::FILLED:
|
||||
extend << wxT( "-filling-" );
|
||||
extend << layerPairName( aPair );
|
||||
extend << layerPairName( aSpan.Pair() );
|
||||
break;
|
||||
case IPC4761_FEATURES::CAPPED:
|
||||
extend << wxT( "-capping-" );
|
||||
extend << layerPairName( aPair );
|
||||
extend << layerPairName( aSpan.Pair() );
|
||||
break;
|
||||
case IPC4761_FEATURES::COVERED_BACK:
|
||||
extend << wxT( "-covering-" );
|
||||
extend << layerName( aPair.second );
|
||||
extend << layerName( aSpan.Pair().second );
|
||||
break;
|
||||
case IPC4761_FEATURES::COVERED_FRONT:
|
||||
extend << wxT( "-covering-" );
|
||||
extend << layerName( aPair.first );
|
||||
extend << layerName( aSpan.Pair().first );
|
||||
break;
|
||||
case IPC4761_FEATURES::PLUGGED_BACK:
|
||||
extend << wxT( "-plugging-" );
|
||||
extend << layerName( aPair.second );
|
||||
extend << layerName( aSpan.Pair().second );
|
||||
break;
|
||||
case IPC4761_FEATURES::PLUGGED_FRONT:
|
||||
extend << wxT( "-plugging-" );
|
||||
extend << layerName( aPair.first );
|
||||
extend << layerName( aSpan.Pair().first );
|
||||
break;
|
||||
case IPC4761_FEATURES::TENTED_BACK:
|
||||
extend << wxT( "-tenting-" );
|
||||
extend << layerName( aPair.second );
|
||||
extend << layerName( aSpan.Pair().second );
|
||||
break;
|
||||
case IPC4761_FEATURES::TENTED_FRONT:
|
||||
extend << wxT( "-tenting-" );
|
||||
extend << layerName( aPair.first );
|
||||
extend << layerName( aSpan.Pair().first );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -381,27 +544,21 @@ bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory, RE
|
||||
wxFileName fn;
|
||||
wxString msg;
|
||||
|
||||
std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
|
||||
std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
|
||||
|
||||
// append a pair representing the NPTH set of holes, for separate drill files.
|
||||
if( !m_merge_PTH_NPTH )
|
||||
hole_sets.emplace_back( F_Cu, B_Cu );
|
||||
hole_sets.emplace_back( F_Cu, B_Cu, false, true );
|
||||
|
||||
for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin(); it != hole_sets.end(); ++it )
|
||||
for( std::vector<DRILL_SPAN>::const_iterator it = hole_sets.begin(); it != hole_sets.end(); ++it )
|
||||
{
|
||||
DRILL_LAYER_PAIR pair = *it;
|
||||
// For separate drill files, the last layer pair is the NPTH drill file.
|
||||
bool doing_npth = m_merge_PTH_NPTH ? false : ( it == hole_sets.end() - 1 );
|
||||
const DRILL_SPAN& span = *it;
|
||||
bool doing_npth = span.m_IsNonPlatedFile;
|
||||
|
||||
buildHolesList( pair, doing_npth );
|
||||
buildHolesList( span, doing_npth );
|
||||
|
||||
// The file is created if it has holes, or if it is the non plated drill file
|
||||
// to be sure the NPTH file is up to date in separate files mode.
|
||||
// Also a PTH drill file is always created, to be sure at least one plated hole drill file
|
||||
// is created (do not create any PTH drill file can be seen as not working drill generator).
|
||||
if( getHolesCount() > 0 || doing_npth || pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
if( getHolesCount() > 0 || doing_npth || span.Pair() == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
{
|
||||
fn = GENDRILL_WRITER_BASE::getDrillFileName( pair, doing_npth, m_merge_PTH_NPTH );
|
||||
fn = GENDRILL_WRITER_BASE::getDrillFileName( span, doing_npth, m_merge_PTH_NPTH );
|
||||
fn.SetPath( aPlotDirectory );
|
||||
|
||||
fn.SetExt( wxEmptyString ); // Will be added by GenDrillMap
|
||||
@@ -435,7 +592,7 @@ bool GENDRILL_WRITER_BASE::CreateMapFilesSet( const wxString& aPlotDirectory, RE
|
||||
}
|
||||
|
||||
|
||||
const wxString GENDRILL_WRITER_BASE::BuildFileFunctionAttributeString( DRILL_LAYER_PAIR aLayerPair,
|
||||
const wxString GENDRILL_WRITER_BASE::BuildFileFunctionAttributeString( const DRILL_SPAN& aSpan,
|
||||
TYPE_FILE aHoleType,
|
||||
bool aCompatNCdrill ) const
|
||||
{
|
||||
@@ -450,15 +607,15 @@ const wxString GENDRILL_WRITER_BASE::BuildFileFunctionAttributeString( DRILL_LAY
|
||||
|
||||
text << wxT( "TF.FileFunction," );
|
||||
|
||||
if( aHoleType == NPTH_FILE )
|
||||
if( aSpan.m_IsBackdrill || aHoleType == NPTH_FILE )
|
||||
text << wxT( "NonPlated," );
|
||||
else if( aHoleType == MIXED_FILE ) // only for Excellon format
|
||||
text << wxT( "MixedPlating," );
|
||||
else
|
||||
text << wxT( "Plated," );
|
||||
|
||||
int layer1 = aLayerPair.first;
|
||||
int layer2 = aLayerPair.second;
|
||||
int layer1 = aSpan.Pair().first;
|
||||
int layer2 = aSpan.Pair().second;
|
||||
// In Gerber files, layers num are 1 to copper layer count instead of F_Cu to B_Cu
|
||||
// (0 to copper layer count-1)
|
||||
// Note also for a n copper layers board, gerber layers num are 1 ... n
|
||||
@@ -481,7 +638,9 @@ const wxString GENDRILL_WRITER_BASE::BuildFileFunctionAttributeString( DRILL_LAY
|
||||
int toplayer = 1;
|
||||
int bottomlayer = m_pcb->GetCopperLayerCount();
|
||||
|
||||
if( aHoleType == NPTH_FILE )
|
||||
if( aSpan.m_IsBackdrill )
|
||||
text << wxT( ",Blind" );
|
||||
else if( aHoleType == NPTH_FILE )
|
||||
text << wxT( ",NPTH" );
|
||||
else if( aHoleType == MIXED_FILE ) // only for Excellon format
|
||||
; // write nothing
|
||||
|
||||
@@ -35,11 +35,13 @@
|
||||
// Set to 1 to add these comments and 0 to not use these comments
|
||||
#define USE_ATTRIB_FOR_HOLES 1
|
||||
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <layer_ids.h>
|
||||
#include <plotters/plotter.h>
|
||||
#include <padstack.h>
|
||||
|
||||
|
||||
class BOARD;
|
||||
@@ -55,6 +57,7 @@ enum class HOLE_ATTRIBUTE
|
||||
HOLE_UNKNOWN, // uninitialized type
|
||||
HOLE_VIA_THROUGH, // a via hole (always plated) from top to bottom
|
||||
HOLE_VIA_BURIED, // a via hole (always plated) not through hole
|
||||
HOLE_VIA_BACKDRILL, // a via hole created by a backdrill operation
|
||||
HOLE_PAD, // a plated or not plated pad hole
|
||||
HOLE_PAD_CASTELLATED, // a plated castelleted pad hole
|
||||
HOLE_PAD_PRESSFIT, // a plated press-fit pad hole
|
||||
@@ -84,6 +87,10 @@ public:
|
||||
int m_OvalCount; // oblong count
|
||||
bool m_Hole_NotPlated; // Is the hole plated or not plated
|
||||
HOLE_ATTRIBUTE m_HoleAttribute; // Attribute (used in Excellon drill file)
|
||||
bool m_IsBackdrill; // True when drilling a backdrill span
|
||||
bool m_HasPostMachining; // True if any hole for this tool has post-machining
|
||||
std::optional<int> m_MinStubLength; // Minimum stub length for this tool (IU)
|
||||
std::optional<int> m_MaxStubLength; // Maximum stub length for this tool (IU)
|
||||
|
||||
public:
|
||||
DRILL_TOOL( int aDiameter, bool a_NotPlated )
|
||||
@@ -93,6 +100,8 @@ public:
|
||||
m_Diameter = aDiameter;
|
||||
m_Hole_NotPlated = a_NotPlated;
|
||||
m_HoleAttribute = HOLE_ATTRIBUTE::HOLE_UNKNOWN;
|
||||
m_IsBackdrill = false;
|
||||
m_HasPostMachining = false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -126,6 +135,17 @@ public:
|
||||
m_Hole_Bot_Plugged = false;
|
||||
m_Hole_Top_Tented = false;
|
||||
m_Hole_Bot_Tented = false;
|
||||
m_IsBackdrill = false;
|
||||
m_FrontPostMachining = PAD_DRILL_POST_MACHINING_MODE::UNKNOWN;
|
||||
m_FrontPostMachiningSize = 0;
|
||||
m_FrontPostMachiningDepth = 0;
|
||||
m_FrontPostMachiningAngle = 0;
|
||||
m_BackPostMachining = PAD_DRILL_POST_MACHINING_MODE::UNKNOWN;
|
||||
m_BackPostMachiningSize = 0;
|
||||
m_BackPostMachiningDepth = 0;
|
||||
m_BackPostMachiningAngle = 0;
|
||||
m_DrillStart = UNDEFINED_LAYER;
|
||||
m_DrillEnd = UNDEFINED_LAYER;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -145,15 +165,102 @@ public:
|
||||
// section.
|
||||
HOLE_ATTRIBUTE m_HoleAttribute; // Attribute, used in Excellon drill file and to sort holes
|
||||
// by type.
|
||||
bool m_Hole_Filled; // True if the hole is filled
|
||||
bool m_Hole_Capped; // True if the hole is capped
|
||||
bool m_Hole_Top_Covered; // True if the hole is covered on the top layer
|
||||
bool m_Hole_Bot_Covered; // True if the hole is covered on the bottom layer
|
||||
bool m_Hole_Top_Plugged; // True if the hole is plugged on the top layer
|
||||
bool m_Hole_Bot_Plugged; // True if the hole is plugged on the bottom layer
|
||||
bool m_Hole_Top_Tented; // True if the hole is tented on the top layer
|
||||
bool m_Hole_Bot_Tented; // True if the hole is tented on the bottom layer
|
||||
bool m_IsBackdrill; // True if the hole is a backdrill
|
||||
PAD_DRILL_POST_MACHINING_MODE m_FrontPostMachining; // Post-machining mode
|
||||
int m_FrontPostMachiningSize; // Post-machining size
|
||||
int m_FrontPostMachiningDepth; // Post-machining depth
|
||||
int m_FrontPostMachiningAngle; // Post-machining angle
|
||||
PAD_DRILL_POST_MACHINING_MODE m_BackPostMachining; // Post-machining mode
|
||||
int m_BackPostMachiningSize; // Post-machining size
|
||||
int m_BackPostMachiningDepth; // Post-machining depth
|
||||
int m_BackPostMachiningAngle; // Post-machining angle
|
||||
PCB_LAYER_ID m_DrillStart; // Start layer for backdrills
|
||||
PCB_LAYER_ID m_DrillEnd; // End layer for backdrills
|
||||
std::optional<int> m_StubLength; // Stub length for backdrills
|
||||
|
||||
bool m_Hole_Filled; // hole should be filled
|
||||
bool m_Hole_Capped; // hole should be capped
|
||||
bool m_Hole_Top_Covered; // hole should be covered on top
|
||||
bool m_Hole_Bot_Covered; // hole should be covered on bottom
|
||||
bool m_Hole_Top_Plugged; // hole should be plugged on top
|
||||
bool m_Hole_Bot_Plugged; // hole should be plugged on bottom
|
||||
bool m_Hole_Top_Tented; // hole should be tented on top
|
||||
bool m_Hole_Bot_Tented; // hole should be tented on bottom
|
||||
|
||||
};
|
||||
|
||||
|
||||
typedef std::pair<PCB_LAYER_ID, PCB_LAYER_ID> DRILL_LAYER_PAIR;
|
||||
|
||||
|
||||
struct DRILL_SPAN
|
||||
{
|
||||
DRILL_SPAN()
|
||||
{
|
||||
m_StartLayer = F_Cu;
|
||||
m_EndLayer = B_Cu;
|
||||
m_IsBackdrill = false;
|
||||
m_IsNonPlatedFile = false;
|
||||
}
|
||||
|
||||
DRILL_SPAN( PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer, bool aIsBackdrill,
|
||||
bool aIsNonPlated )
|
||||
{
|
||||
m_StartLayer = aStartLayer;
|
||||
m_EndLayer = aEndLayer;
|
||||
m_IsBackdrill = aIsBackdrill;
|
||||
m_IsNonPlatedFile = aIsNonPlated;
|
||||
}
|
||||
|
||||
PCB_LAYER_ID TopLayer() const
|
||||
{
|
||||
return m_StartLayer < m_EndLayer ? m_StartLayer : m_EndLayer;
|
||||
}
|
||||
|
||||
PCB_LAYER_ID BottomLayer() const
|
||||
{
|
||||
return m_StartLayer < m_EndLayer ? m_EndLayer : m_StartLayer;
|
||||
}
|
||||
|
||||
PCB_LAYER_ID DrillStartLayer() const
|
||||
{
|
||||
return m_StartLayer;
|
||||
}
|
||||
|
||||
PCB_LAYER_ID DrillEndLayer() const
|
||||
{
|
||||
return m_EndLayer;
|
||||
}
|
||||
|
||||
DRILL_LAYER_PAIR Pair() const
|
||||
{
|
||||
return DRILL_LAYER_PAIR( TopLayer(), BottomLayer() );
|
||||
}
|
||||
|
||||
bool operator<( const DRILL_SPAN& aOther ) const
|
||||
{
|
||||
if( TopLayer() != aOther.TopLayer() )
|
||||
return TopLayer() < aOther.TopLayer();
|
||||
|
||||
if( BottomLayer() != aOther.BottomLayer() )
|
||||
return BottomLayer() < aOther.BottomLayer();
|
||||
|
||||
if( m_IsBackdrill != aOther.m_IsBackdrill )
|
||||
return m_IsBackdrill && !aOther.m_IsBackdrill;
|
||||
|
||||
if( m_IsNonPlatedFile != aOther.m_IsNonPlatedFile )
|
||||
return m_IsNonPlatedFile && !aOther.m_IsNonPlatedFile;
|
||||
|
||||
if( m_StartLayer != aOther.m_StartLayer )
|
||||
return m_StartLayer < aOther.m_StartLayer;
|
||||
|
||||
return m_EndLayer < aOther.m_EndLayer;
|
||||
}
|
||||
|
||||
PCB_LAYER_ID m_StartLayer;
|
||||
PCB_LAYER_ID m_EndLayer;
|
||||
bool m_IsBackdrill;
|
||||
bool m_IsNonPlatedFile;
|
||||
};
|
||||
|
||||
|
||||
@@ -182,8 +289,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
typedef std::pair<PCB_LAYER_ID, PCB_LAYER_ID> DRILL_LAYER_PAIR;
|
||||
|
||||
/**
|
||||
* Create drill maps and drill reports and drill files.
|
||||
*
|
||||
@@ -335,7 +440,7 @@ protected:
|
||||
* true to create NPTH only list (with no plated holes)
|
||||
* false to created plated holes list (with no NPTH )
|
||||
*/
|
||||
void buildHolesList( DRILL_LAYER_PAIR aLayerPair, bool aGenerateNPTH_list );
|
||||
void buildHolesList( const DRILL_SPAN& aSpan, bool aGenerateNPTH_list );
|
||||
|
||||
int getHolesCount() const { return m_holeListBuffer.size(); }
|
||||
|
||||
@@ -351,7 +456,7 @@ protected:
|
||||
bool plotDrillMarks( PLOTTER* aPlotter );
|
||||
|
||||
/// Get unique layer pairs by examining the micro and blind_buried vias.
|
||||
std::vector<DRILL_LAYER_PAIR> getUniqueLayerPairs() const;
|
||||
std::vector<DRILL_SPAN> getUniqueLayerPairs() const;
|
||||
|
||||
/**
|
||||
* Print m_toolListBuffer[] tools to aOut and returns total hole count.
|
||||
@@ -383,7 +488,7 @@ protected:
|
||||
* it is the board name with the layer pair names added, and for separate
|
||||
* (PTH and NPTH) files, "-NPH" or "-NPTH" added
|
||||
*/
|
||||
virtual const wxString getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
|
||||
virtual const wxString getDrillFileName( const DRILL_SPAN& aSpan, bool aNPTH,
|
||||
bool aMerge_PTH_NPTH ) const;
|
||||
|
||||
|
||||
@@ -393,7 +498,7 @@ protected:
|
||||
* @return a filename which identifies the specific protection feature.
|
||||
* It is the board file name followed by the feature name and the layer(s) associated with it.
|
||||
*/
|
||||
virtual const wxString getProtectionFileName( DRILL_LAYER_PAIR aPair,
|
||||
virtual const wxString getProtectionFileName( const DRILL_SPAN& aSpan,
|
||||
IPC4761_FEATURES aFeature ) const;
|
||||
|
||||
|
||||
@@ -408,7 +513,7 @@ protected:
|
||||
* There is no X1 version, as the Gerber drill files uses only X2 format
|
||||
* There is a compatible NC drill version.
|
||||
*/
|
||||
const wxString BuildFileFunctionAttributeString( DRILL_LAYER_PAIR aLayerPair,
|
||||
const wxString BuildFileFunctionAttributeString( const DRILL_SPAN& aSpan,
|
||||
TYPE_FILE aHoleType,
|
||||
bool aCompatNCdrill = false ) const;
|
||||
|
||||
|
||||
@@ -67,100 +67,103 @@ bool GERBER_WRITER::CreateDrillandMapFilesSet( const wxString& aPlotDirectory, b
|
||||
wxFileName fn;
|
||||
wxString msg;
|
||||
|
||||
std::vector<DRILL_LAYER_PAIR> hole_sets = getUniqueLayerPairs();
|
||||
std::vector<DRILL_SPAN> hole_sets = getUniqueLayerPairs();
|
||||
|
||||
// append a pair representing the NPTH set of holes, for separate drill files.
|
||||
// (Gerber drill files are separate files for PTH and NPTH)
|
||||
hole_sets.emplace_back( F_Cu, B_Cu );
|
||||
hole_sets.emplace_back( F_Cu, B_Cu, false, true );
|
||||
|
||||
for( std::vector<DRILL_LAYER_PAIR>::const_iterator it = hole_sets.begin();
|
||||
for( std::vector<DRILL_SPAN>::const_iterator it = hole_sets.begin();
|
||||
it != hole_sets.end(); ++it )
|
||||
{
|
||||
DRILL_LAYER_PAIR pair = *it;
|
||||
// For separate drill files, the last layer pair is the NPTH drill file.
|
||||
bool doing_npth = ( it == hole_sets.end() - 1 );
|
||||
const DRILL_SPAN& span = *it;
|
||||
bool doing_npth = span.m_IsNonPlatedFile;
|
||||
|
||||
buildHolesList( pair, doing_npth );
|
||||
buildHolesList( span, doing_npth );
|
||||
|
||||
// The file is created if it has holes, or if it is the non plated drill file
|
||||
// to be sure the NPTH file is up to date in separate files mode.
|
||||
// Also a PTH drill/map file is always created, to be sure at least one plated hole drill
|
||||
// file is created (do not create any PTH drill file can be seen as not working drill
|
||||
// generator).
|
||||
if( getHolesCount() > 0 || doing_npth || pair == DRILL_LAYER_PAIR( F_Cu, B_Cu ) )
|
||||
if( getHolesCount() == 0 )
|
||||
continue;
|
||||
|
||||
fn = getDrillFileName( span, doing_npth, m_merge_PTH_NPTH );
|
||||
fn.SetPath( aPlotDirectory );
|
||||
|
||||
if( aGenDrill )
|
||||
{
|
||||
fn = getDrillFileName( pair, doing_npth, false );
|
||||
fn.SetPath( aPlotDirectory );
|
||||
wxString fullFilename = fn.GetFullPath();
|
||||
bool isNonPlated = doing_npth || span.m_IsBackdrill;
|
||||
bool wroteDrillFile = false;
|
||||
|
||||
if( aGenDrill )
|
||||
int result = createDrillFile( fullFilename, isNonPlated, span );
|
||||
|
||||
if( result < 0 )
|
||||
{
|
||||
wxString fullFilename = fn.GetFullPath();
|
||||
|
||||
int result = createDrillFile( fullFilename, doing_npth, pair );
|
||||
|
||||
if( result < 0 )
|
||||
if( aReporter )
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
success = false;
|
||||
}
|
||||
|
||||
break;
|
||||
msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
}
|
||||
else
|
||||
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
wroteDrillFile = true;
|
||||
|
||||
if( aReporter )
|
||||
{
|
||||
msg.Printf( _( "Created file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
}
|
||||
|
||||
if( wroteDrillFile && span.m_IsBackdrill )
|
||||
{
|
||||
if( !writeBackdrillLayerPairFile( aPlotDirectory, aReporter, span ) )
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
msg.Printf( _( "Created file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
}
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( getHolesCount() > 0 && !doing_npth )
|
||||
if( doing_npth )
|
||||
continue;
|
||||
|
||||
for( IPC4761_FEATURES feature :
|
||||
{ IPC4761_FEATURES::FILLED, IPC4761_FEATURES::CAPPED,
|
||||
IPC4761_FEATURES::COVERED_BACK, IPC4761_FEATURES::COVERED_FRONT,
|
||||
IPC4761_FEATURES::PLUGGED_BACK, IPC4761_FEATURES::PLUGGED_FRONT,
|
||||
IPC4761_FEATURES::TENTED_BACK, IPC4761_FEATURES::TENTED_FRONT } )
|
||||
{
|
||||
for( IPC4761_FEATURES feature :
|
||||
{ IPC4761_FEATURES::FILLED, IPC4761_FEATURES::CAPPED,
|
||||
IPC4761_FEATURES::COVERED_BACK, IPC4761_FEATURES::COVERED_FRONT,
|
||||
IPC4761_FEATURES::PLUGGED_BACK, IPC4761_FEATURES::PLUGGED_FRONT,
|
||||
IPC4761_FEATURES::TENTED_BACK, IPC4761_FEATURES::TENTED_FRONT } )
|
||||
if( !aGenTenting )
|
||||
{
|
||||
if( !aGenTenting )
|
||||
{
|
||||
if( feature == IPC4761_FEATURES::TENTED_BACK
|
||||
if( feature == IPC4761_FEATURES::TENTED_BACK
|
||||
|| feature == IPC4761_FEATURES::TENTED_FRONT )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if( !hasViaType( feature ) )
|
||||
{
|
||||
continue;
|
||||
|
||||
fn = getProtectionFileName( pair, feature );
|
||||
fn.SetPath( aPlotDirectory );
|
||||
|
||||
wxString fullFilename = fn.GetFullPath();
|
||||
|
||||
if( createProtectionFile( fullFilename, feature, pair ) < 0 )
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
if( !hasViaType( feature ) )
|
||||
continue;
|
||||
|
||||
fn = getProtectionFileName( span, feature );
|
||||
fn.SetPath( aPlotDirectory );
|
||||
|
||||
wxString fullFilename = fn.GetFullPath();
|
||||
|
||||
if( createProtectionFile( fullFilename, feature, span.Pair() ) < 0 )
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
msg.Printf( _( "Created file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
}
|
||||
msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
msg.Printf( _( "Created file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -318,7 +321,7 @@ int GERBER_WRITER::createProtectionFile( const wxString& aFullFilename, IPC4761_
|
||||
}
|
||||
|
||||
int GERBER_WRITER::createDrillFile( wxString& aFullFilename, bool aIsNpth,
|
||||
DRILL_LAYER_PAIR aLayerPair )
|
||||
const DRILL_SPAN& aSpan )
|
||||
{
|
||||
int holes_count;
|
||||
|
||||
@@ -341,7 +344,7 @@ int GERBER_WRITER::createDrillFile( wxString& aFullFilename, bool aIsNpth,
|
||||
|
||||
// Add the standard X2 FileFunction for drill files
|
||||
// %TF.FileFunction,Plated[NonPlated],layer1num,layer2num,PTH[NPTH][Blind][Buried],Drill[Rout][Mixed]*%
|
||||
wxString text = BuildFileFunctionAttributeString( aLayerPair,
|
||||
wxString text = BuildFileFunctionAttributeString( aSpan,
|
||||
aIsNpth ? TYPE_FILE::NPTH_FILE
|
||||
: TYPE_FILE::PTH_FILE );
|
||||
plotter.AddLineToHeader( text );
|
||||
@@ -373,7 +376,10 @@ int GERBER_WRITER::createDrillFile( wxString& aFullFilename, bool aIsNpth,
|
||||
|
||||
if( dyn_cast<const PCB_VIA*>( hole_descr.m_ItemParent ) )
|
||||
{
|
||||
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_VIADRILL );
|
||||
if( hole_descr.m_IsBackdrill )
|
||||
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_BACKDRILL );
|
||||
else
|
||||
gbr_metadata.SetApertureAttrib( GBR_APERTURE_METADATA::GBR_APERTURE_ATTRIB_VIADRILL );
|
||||
|
||||
if( !last_item_is_via )
|
||||
{
|
||||
@@ -454,6 +460,49 @@ int GERBER_WRITER::createDrillFile( wxString& aFullFilename, bool aIsNpth,
|
||||
}
|
||||
|
||||
|
||||
wxFileName GERBER_WRITER::getBackdrillLayerPairFileName( const DRILL_SPAN& aSpan ) const
|
||||
{
|
||||
wxFileName fn = m_pcb->GetFileName();
|
||||
wxString pairName = wxString::FromUTF8( layerPairName( aSpan.Pair() ).c_str() );
|
||||
|
||||
fn.SetName( fn.GetName() + wxT( "-" ) + pairName + wxT( "-backdrill-drl" ) );
|
||||
fn.SetExt( m_drillFileExtension );
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
bool GERBER_WRITER::writeBackdrillLayerPairFile( const wxString& aPlotDirectory,
|
||||
REPORTER* aReporter, const DRILL_SPAN& aSpan )
|
||||
{
|
||||
wxFileName fn = getBackdrillLayerPairFileName( aSpan );
|
||||
fn.SetPath( aPlotDirectory );
|
||||
|
||||
wxString fullFilename = fn.GetFullPath();
|
||||
|
||||
if( createDrillFile( fullFilename, true, aSpan ) < 0 )
|
||||
{
|
||||
if( aReporter )
|
||||
{
|
||||
wxString msg;
|
||||
msg.Printf( _( "Failed to create file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ERROR );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if( aReporter )
|
||||
{
|
||||
wxString msg;
|
||||
msg.Printf( _( "Created file '%s'." ), fullFilename );
|
||||
aReporter->Report( msg, RPT_SEVERITY_ACTION );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#if !FLASH_OVAL_HOLE
|
||||
void convertOblong2Segment( const VECTOR2I& aSize, const EDA_ANGLE& aOrient, VECTOR2I& aStart,
|
||||
VECTOR2I& aEnd )
|
||||
@@ -491,12 +540,12 @@ void GERBER_WRITER::SetFormat( int aRightDigits )
|
||||
}
|
||||
|
||||
|
||||
const wxString GERBER_WRITER::getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
|
||||
const wxString GERBER_WRITER::getDrillFileName( const DRILL_SPAN& aSpan, bool aNPTH,
|
||||
bool aMerge_PTH_NPTH ) const
|
||||
{
|
||||
// Gerber files extension is always .gbr.
|
||||
// Therefore, to mark drill files, add "-drl" to the filename.
|
||||
wxFileName fname( GENDRILL_WRITER_BASE::getDrillFileName( aPair, aNPTH, aMerge_PTH_NPTH ) );
|
||||
wxFileName fname( GENDRILL_WRITER_BASE::getDrillFileName( aSpan, aNPTH, aMerge_PTH_NPTH ) );
|
||||
fname.SetName( fname.GetName() + wxT( "-drl" ) );
|
||||
|
||||
return fname.GetFullPath();
|
||||
|
||||
@@ -30,7 +30,9 @@
|
||||
#ifndef _GENDRILL_GERBER_WRITER_
|
||||
#define _GENDRILL_GERBER_WRITER_
|
||||
|
||||
#include <gendrill_file_writer_base.h>
|
||||
#include "gendrill_file_writer_base.h"
|
||||
|
||||
#include <wx/filename.h>
|
||||
|
||||
class BOARD;
|
||||
|
||||
@@ -91,7 +93,7 @@ private:
|
||||
* for blind buried vias, they are not always top and bottom layers/
|
||||
* @return hole count or -1 if the file cannot be created.
|
||||
*/
|
||||
int createDrillFile( wxString& aFullFilename, bool aIsNpth, DRILL_LAYER_PAIR aLayerPair );
|
||||
int createDrillFile( wxString& aFullFilename, bool aIsNpth, const DRILL_SPAN& aSpan );
|
||||
|
||||
/**
|
||||
* Create a Gerber X2 file for via protection features.
|
||||
@@ -112,7 +114,7 @@ private:
|
||||
* layer pair names added, and for separate (PTH and NPTH) files, "-NPH" or "-NPTH"
|
||||
* added.
|
||||
*/
|
||||
virtual const wxString getDrillFileName( DRILL_LAYER_PAIR aPair, bool aNPTH,
|
||||
virtual const wxString getDrillFileName( const DRILL_SPAN& aSpan, bool aNPTH,
|
||||
bool aMerge_PTH_NPTH ) const override;
|
||||
|
||||
/**
|
||||
@@ -121,6 +123,10 @@ private:
|
||||
* @return true if at least one via having this feature is found in m_holeListBuffer
|
||||
*/
|
||||
bool hasViaType( IPC4761_FEATURES aFeature );
|
||||
|
||||
wxFileName getBackdrillLayerPairFileName( const DRILL_SPAN& aSpan ) const;
|
||||
bool writeBackdrillLayerPairFile( const wxString& aPlotDirectory, REPORTER* aReporter,
|
||||
const DRILL_SPAN& aSpan );
|
||||
};
|
||||
|
||||
#endif // #ifndef _GENDRILL_GERBER_WRITER_
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <advanced_config.h>
|
||||
#include <board.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <convert_basic_shapes_to_polygon.h>
|
||||
#include <footprint.h>
|
||||
#include <pcb_textbox.h>
|
||||
#include <pcb_table.h>
|
||||
@@ -157,6 +158,46 @@ EXPORTER_STEP::~EXPORTER_STEP()
|
||||
}
|
||||
|
||||
|
||||
bool EXPORTER_STEP::isLayerInBackdrillSpan( PCB_LAYER_ID aLayer, PCB_LAYER_ID aStartLayer,
|
||||
PCB_LAYER_ID aEndLayer ) const
|
||||
{
|
||||
if( !IsCopperLayer( aLayer ) )
|
||||
return false;
|
||||
|
||||
// Quick check for exact match
|
||||
if( aLayer == aStartLayer || aLayer == aEndLayer )
|
||||
return true;
|
||||
|
||||
// Convert layers to a sortable index for comparison
|
||||
// F_Cu = -1, In1_Cu through In30_Cu = 0-29, B_Cu = MAX_CU_LAYERS (32)
|
||||
auto layerToIndex = []( PCB_LAYER_ID layer ) -> int
|
||||
{
|
||||
if( layer == F_Cu )
|
||||
return -1;
|
||||
|
||||
if( layer == B_Cu )
|
||||
return MAX_CU_LAYERS;
|
||||
|
||||
if( IsInnerCopperLayer( layer ) )
|
||||
return layer - In1_Cu;
|
||||
|
||||
return -2; // Invalid copper layer
|
||||
};
|
||||
|
||||
int startIdx = layerToIndex( aStartLayer );
|
||||
int endIdx = layerToIndex( aEndLayer );
|
||||
int layerIdx = layerToIndex( aLayer );
|
||||
|
||||
if( layerIdx == -2 )
|
||||
return false;
|
||||
|
||||
int minIdx = std::min( startIdx, endIdx );
|
||||
int maxIdx = std::max( startIdx, endIdx );
|
||||
|
||||
return ( layerIdx >= minIdx && layerIdx <= maxIdx );
|
||||
}
|
||||
|
||||
|
||||
bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, const VECTOR2D& aOrigin,
|
||||
SHAPE_POLY_SET* aClipPolygon )
|
||||
{
|
||||
@@ -194,6 +235,166 @@ bool EXPORTER_STEP::buildFootprint3DShapes( FOOTPRINT* aFootprint, const VECTOR2
|
||||
// m_poly_holes[F_SilkS].Append( holePoly );
|
||||
// m_poly_holes[B_SilkS].Append( holePoly );
|
||||
//}
|
||||
|
||||
// Handle backdrills - secondary and tertiary drills defined in the padstack
|
||||
const PADSTACK& padstack = pad->Padstack();
|
||||
const PADSTACK::DRILL_PROPS& secondaryDrill = padstack.SecondaryDrill();
|
||||
const PADSTACK::DRILL_PROPS& tertiaryDrill = padstack.TertiaryDrill();
|
||||
|
||||
// Process secondary drill (typically bottom backdrill)
|
||||
if( secondaryDrill.size.x > 0 )
|
||||
{
|
||||
SHAPE_SEGMENT backdrillShape( pad->GetPosition(), pad->GetPosition(),
|
||||
secondaryDrill.size.x );
|
||||
m_pcbModel->AddBackdrill( backdrillShape, secondaryDrill.start,
|
||||
secondaryDrill.end, aOrigin );
|
||||
|
||||
// Add backdrill holes to affected copper layers for 2D polygon subtraction
|
||||
SHAPE_POLY_SET backdrillPoly;
|
||||
backdrillShape.TransformToPolygon( backdrillPoly, pad->GetMaxError(), ERROR_INSIDE );
|
||||
|
||||
for( PCB_LAYER_ID layer : pad->GetLayerSet() )
|
||||
{
|
||||
if( isLayerInBackdrillSpan( layer, secondaryDrill.start, secondaryDrill.end ) )
|
||||
m_poly_holes[layer].Append( backdrillPoly );
|
||||
}
|
||||
|
||||
// Add knockouts for silkscreen and soldermask on the backdrill side
|
||||
if( isLayerInBackdrillSpan( F_Cu, secondaryDrill.start, secondaryDrill.end ) )
|
||||
{
|
||||
m_poly_holes[F_SilkS].Append( backdrillPoly );
|
||||
m_poly_holes[F_Mask].Append( backdrillPoly );
|
||||
}
|
||||
if( isLayerInBackdrillSpan( B_Cu, secondaryDrill.start, secondaryDrill.end ) )
|
||||
{
|
||||
m_poly_holes[B_SilkS].Append( backdrillPoly );
|
||||
m_poly_holes[B_Mask].Append( backdrillPoly );
|
||||
}
|
||||
}
|
||||
|
||||
// Process tertiary drill (typically top backdrill)
|
||||
if( tertiaryDrill.size.x > 0 )
|
||||
{
|
||||
SHAPE_SEGMENT backdrillShape( pad->GetPosition(), pad->GetPosition(),
|
||||
tertiaryDrill.size.x );
|
||||
m_pcbModel->AddBackdrill( backdrillShape, tertiaryDrill.start,
|
||||
tertiaryDrill.end, aOrigin );
|
||||
|
||||
// Add backdrill holes to affected copper layers for 2D polygon subtraction
|
||||
SHAPE_POLY_SET backdrillPoly;
|
||||
backdrillShape.TransformToPolygon( backdrillPoly, pad->GetMaxError(), ERROR_INSIDE );
|
||||
|
||||
for( PCB_LAYER_ID layer : pad->GetLayerSet() )
|
||||
{
|
||||
if( isLayerInBackdrillSpan( layer, tertiaryDrill.start, tertiaryDrill.end ) )
|
||||
m_poly_holes[layer].Append( backdrillPoly );
|
||||
}
|
||||
|
||||
// Add knockouts for silkscreen and soldermask on the backdrill side
|
||||
if( isLayerInBackdrillSpan( F_Cu, tertiaryDrill.start, tertiaryDrill.end ) )
|
||||
{
|
||||
m_poly_holes[F_SilkS].Append( backdrillPoly );
|
||||
m_poly_holes[F_Mask].Append( backdrillPoly );
|
||||
}
|
||||
if( isLayerInBackdrillSpan( B_Cu, tertiaryDrill.start, tertiaryDrill.end ) )
|
||||
{
|
||||
m_poly_holes[B_SilkS].Append( backdrillPoly );
|
||||
m_poly_holes[B_Mask].Append( backdrillPoly );
|
||||
}
|
||||
}
|
||||
|
||||
// Process post-machining (counterbore/countersink) on front and back
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPM = padstack.FrontPostMachining();
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPM = padstack.BackPostMachining();
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "PAD post-machining check: frontPM.mode.has_value=%d frontPM.size=%d frontPM.depth=%d frontPM.angle=%d" ),
|
||||
frontPM.mode.has_value() ? 1 : 0, frontPM.size, frontPM.depth, frontPM.angle );
|
||||
wxLogTrace( traceKiCad2Step, wxT( "PAD post-machining check: backPM.mode.has_value=%d backPM.size=%d backPM.depth=%d backPM.angle=%d" ),
|
||||
backPM.mode.has_value() ? 1 : 0, backPM.size, backPM.depth, backPM.angle );
|
||||
|
||||
// For counterbore, depth must be > 0. For countersink, depth can be 0 (calculated from diameter/angle)
|
||||
bool frontPMValid = frontPM.mode.has_value() && frontPM.size > 0 &&
|
||||
( ( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE && frontPM.depth > 0 ) ||
|
||||
( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && frontPM.angle > 0 ) );
|
||||
|
||||
if( frontPMValid )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "PAD front post-machining: mode=%d (COUNTERBORE=2, COUNTERSINK=3)" ),
|
||||
static_cast<int>( *frontPM.mode ) );
|
||||
|
||||
int pmAngle = ( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) ? frontPM.angle : 0;
|
||||
|
||||
if( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
m_pcbModel->AddCounterbore( pad->GetPosition(), frontPM.size,
|
||||
frontPM.depth, true, aOrigin );
|
||||
}
|
||||
else if( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
m_pcbModel->AddCountersink( pad->GetPosition(), frontPM.size,
|
||||
frontPM.depth, frontPM.angle, true, aOrigin );
|
||||
}
|
||||
|
||||
// Add knockouts to all copper layers the feature crosses
|
||||
auto knockouts = m_pcbModel->GetCopperLayerKnockouts( frontPM.size, frontPM.depth,
|
||||
pmAngle, true );
|
||||
for( const auto& [layer, diameter] : knockouts )
|
||||
{
|
||||
SHAPE_POLY_SET pmPoly;
|
||||
TransformCircleToPolygon( pmPoly, pad->GetPosition(), diameter / 2,
|
||||
pad->GetMaxError(), ERROR_INSIDE );
|
||||
m_poly_holes[layer].Append( pmPoly );
|
||||
}
|
||||
|
||||
// Add knockout for silkscreen and soldermask on front side (full diameter)
|
||||
SHAPE_POLY_SET pmPoly;
|
||||
TransformCircleToPolygon( pmPoly, pad->GetPosition(), frontPM.size / 2,
|
||||
pad->GetMaxError(), ERROR_INSIDE );
|
||||
m_poly_holes[F_SilkS].Append( pmPoly );
|
||||
m_poly_holes[F_Mask].Append( pmPoly );
|
||||
}
|
||||
|
||||
// For counterbore, depth must be > 0. For countersink, depth can be 0 (calculated from diameter/angle)
|
||||
bool backPMValid = backPM.mode.has_value() && backPM.size > 0 &&
|
||||
( ( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE && backPM.depth > 0 ) ||
|
||||
( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && backPM.angle > 0 ) );
|
||||
|
||||
if( backPMValid )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "PAD back post-machining: mode=%d (COUNTERBORE=2, COUNTERSINK=3)" ),
|
||||
static_cast<int>( *backPM.mode ) );
|
||||
|
||||
int pmAngle = ( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) ? backPM.angle : 0;
|
||||
|
||||
if( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
m_pcbModel->AddCounterbore( pad->GetPosition(), backPM.size,
|
||||
backPM.depth, false, aOrigin );
|
||||
}
|
||||
else if( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
m_pcbModel->AddCountersink( pad->GetPosition(), backPM.size,
|
||||
backPM.depth, backPM.angle, false, aOrigin );
|
||||
}
|
||||
|
||||
// Add knockouts to all copper layers the feature crosses
|
||||
auto knockouts = m_pcbModel->GetCopperLayerKnockouts( backPM.size, backPM.depth,
|
||||
pmAngle, false );
|
||||
for( const auto& [layer, diameter] : knockouts )
|
||||
{
|
||||
SHAPE_POLY_SET pmPoly;
|
||||
TransformCircleToPolygon( pmPoly, pad->GetPosition(), diameter / 2,
|
||||
pad->GetMaxError(), ERROR_INSIDE );
|
||||
m_poly_holes[layer].Append( pmPoly );
|
||||
}
|
||||
|
||||
// Add knockout for silkscreen and soldermask on back side (full diameter)
|
||||
SHAPE_POLY_SET pmPoly;
|
||||
TransformCircleToPolygon( pmPoly, pad->GetPosition(), backPM.size / 2,
|
||||
pad->GetMaxError(), ERROR_INSIDE );
|
||||
m_poly_holes[B_SilkS].Append( pmPoly );
|
||||
m_poly_holes[B_Mask].Append( pmPoly );
|
||||
}
|
||||
}
|
||||
|
||||
if( !m_params.m_NetFilter.IsEmpty() && !pad->GetNetname().Matches( m_params.m_NetFilter ) )
|
||||
@@ -461,6 +662,166 @@ bool EXPORTER_STEP::buildTrack3DShape( PCB_TRACK* aTrack, const VECTOR2D& aOrigi
|
||||
m_pcbModel->AddHole( *holeShape, m_platingThickness, top_layer, bot_layer, true, aOrigin,
|
||||
!m_params.m_FillAllVias, m_params.m_CutViasInBody );
|
||||
|
||||
// Handle via backdrills - secondary and tertiary drills defined in the padstack
|
||||
const PADSTACK& padstack = via->Padstack();
|
||||
const PADSTACK::DRILL_PROPS& secondaryDrill = padstack.SecondaryDrill();
|
||||
const PADSTACK::DRILL_PROPS& tertiaryDrill = padstack.TertiaryDrill();
|
||||
|
||||
// Process secondary drill (typically bottom backdrill)
|
||||
if( secondaryDrill.size.x > 0 )
|
||||
{
|
||||
SHAPE_SEGMENT backdrillShape( via->GetPosition(), via->GetPosition(),
|
||||
secondaryDrill.size.x );
|
||||
m_pcbModel->AddBackdrill( backdrillShape, secondaryDrill.start,
|
||||
secondaryDrill.end, aOrigin );
|
||||
|
||||
// Add backdrill holes to affected copper layers for 2D polygon subtraction
|
||||
SHAPE_POLY_SET backdrillPoly;
|
||||
backdrillShape.TransformToPolygon( backdrillPoly, via->GetMaxError(), ERROR_INSIDE );
|
||||
|
||||
for( PCB_LAYER_ID layer : via->GetLayerSet() )
|
||||
{
|
||||
if( isLayerInBackdrillSpan( layer, secondaryDrill.start, secondaryDrill.end ) )
|
||||
m_poly_holes[layer].Append( backdrillPoly );
|
||||
}
|
||||
|
||||
// Add knockouts for silkscreen and soldermask on the backdrill side
|
||||
if( isLayerInBackdrillSpan( F_Cu, secondaryDrill.start, secondaryDrill.end ) )
|
||||
{
|
||||
m_poly_holes[F_SilkS].Append( backdrillPoly );
|
||||
m_poly_holes[F_Mask].Append( backdrillPoly );
|
||||
}
|
||||
if( isLayerInBackdrillSpan( B_Cu, secondaryDrill.start, secondaryDrill.end ) )
|
||||
{
|
||||
m_poly_holes[B_SilkS].Append( backdrillPoly );
|
||||
m_poly_holes[B_Mask].Append( backdrillPoly );
|
||||
}
|
||||
}
|
||||
|
||||
// Process tertiary drill (typically top backdrill)
|
||||
if( tertiaryDrill.size.x > 0 )
|
||||
{
|
||||
SHAPE_SEGMENT backdrillShape( via->GetPosition(), via->GetPosition(),
|
||||
tertiaryDrill.size.x );
|
||||
m_pcbModel->AddBackdrill( backdrillShape, tertiaryDrill.start,
|
||||
tertiaryDrill.end, aOrigin );
|
||||
|
||||
// Add backdrill holes to affected copper layers for 2D polygon subtraction
|
||||
SHAPE_POLY_SET backdrillPoly;
|
||||
backdrillShape.TransformToPolygon( backdrillPoly, via->GetMaxError(), ERROR_INSIDE );
|
||||
|
||||
for( PCB_LAYER_ID layer : via->GetLayerSet() )
|
||||
{
|
||||
if( isLayerInBackdrillSpan( layer, tertiaryDrill.start, tertiaryDrill.end ) )
|
||||
m_poly_holes[layer].Append( backdrillPoly );
|
||||
}
|
||||
|
||||
// Add knockouts for silkscreen and soldermask on the backdrill side
|
||||
if( isLayerInBackdrillSpan( F_Cu, tertiaryDrill.start, tertiaryDrill.end ) )
|
||||
{
|
||||
m_poly_holes[F_SilkS].Append( backdrillPoly );
|
||||
m_poly_holes[F_Mask].Append( backdrillPoly );
|
||||
}
|
||||
if( isLayerInBackdrillSpan( B_Cu, tertiaryDrill.start, tertiaryDrill.end ) )
|
||||
{
|
||||
m_poly_holes[B_SilkS].Append( backdrillPoly );
|
||||
m_poly_holes[B_Mask].Append( backdrillPoly );
|
||||
}
|
||||
}
|
||||
|
||||
// Process post-machining (counterbore/countersink) on front and back
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPM = padstack.FrontPostMachining();
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPM = padstack.BackPostMachining();
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "VIA post-machining check: frontPM.mode.has_value=%d frontPM.size=%d frontPM.depth=%d frontPM.angle=%d" ),
|
||||
frontPM.mode.has_value() ? 1 : 0, frontPM.size, frontPM.depth, frontPM.angle );
|
||||
wxLogTrace( traceKiCad2Step, wxT( "VIA post-machining check: backPM.mode.has_value=%d backPM.size=%d backPM.depth=%d backPM.angle=%d" ),
|
||||
backPM.mode.has_value() ? 1 : 0, backPM.size, backPM.depth, backPM.angle );
|
||||
|
||||
// For counterbore, depth must be > 0. For countersink, depth can be 0 (calculated from diameter/angle)
|
||||
bool frontPMValid = frontPM.mode.has_value() && frontPM.size > 0 &&
|
||||
( ( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE && frontPM.depth > 0 ) ||
|
||||
( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && frontPM.angle > 0 ) );
|
||||
|
||||
if( frontPMValid )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "VIA front post-machining: mode=%d (COUNTERBORE=2, COUNTERSINK=3)" ),
|
||||
static_cast<int>( *frontPM.mode ) );
|
||||
|
||||
int pmAngle = ( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) ? frontPM.angle : 0;
|
||||
|
||||
if( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
m_pcbModel->AddCounterbore( via->GetPosition(), frontPM.size,
|
||||
frontPM.depth, true, aOrigin );
|
||||
}
|
||||
else if( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
m_pcbModel->AddCountersink( via->GetPosition(), frontPM.size,
|
||||
frontPM.depth, frontPM.angle, true, aOrigin );
|
||||
}
|
||||
|
||||
// Add knockouts to all copper layers the feature crosses
|
||||
auto knockouts = m_pcbModel->GetCopperLayerKnockouts( frontPM.size, frontPM.depth,
|
||||
pmAngle, true );
|
||||
for( const auto& [layer, diameter] : knockouts )
|
||||
{
|
||||
SHAPE_POLY_SET pmPoly;
|
||||
TransformCircleToPolygon( pmPoly, via->GetPosition(), diameter / 2,
|
||||
via->GetMaxError(), ERROR_INSIDE );
|
||||
m_poly_holes[layer].Append( pmPoly );
|
||||
}
|
||||
|
||||
// Add knockout for silkscreen and soldermask on front side (full diameter)
|
||||
SHAPE_POLY_SET pmPoly;
|
||||
TransformCircleToPolygon( pmPoly, via->GetPosition(), frontPM.size / 2,
|
||||
via->GetMaxError(), ERROR_INSIDE );
|
||||
m_poly_holes[F_SilkS].Append( pmPoly );
|
||||
m_poly_holes[F_Mask].Append( pmPoly );
|
||||
}
|
||||
|
||||
// For counterbore, depth must be > 0. For countersink, depth can be 0 (calculated from diameter/angle)
|
||||
bool backPMValid = backPM.mode.has_value() && backPM.size > 0 &&
|
||||
( ( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE && backPM.depth > 0 ) ||
|
||||
( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && backPM.angle > 0 ) );
|
||||
|
||||
if( backPMValid )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "VIA back post-machining: mode=%d (COUNTERBORE=2, COUNTERSINK=3)" ),
|
||||
static_cast<int>( *backPM.mode ) );
|
||||
|
||||
int pmAngle = ( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) ? backPM.angle : 0;
|
||||
|
||||
if( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE )
|
||||
{
|
||||
m_pcbModel->AddCounterbore( via->GetPosition(), backPM.size,
|
||||
backPM.depth, false, aOrigin );
|
||||
}
|
||||
else if( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
{
|
||||
m_pcbModel->AddCountersink( via->GetPosition(), backPM.size,
|
||||
backPM.depth, backPM.angle, false, aOrigin );
|
||||
}
|
||||
|
||||
// Add knockouts to all copper layers the feature crosses
|
||||
auto knockouts = m_pcbModel->GetCopperLayerKnockouts( backPM.size, backPM.depth,
|
||||
pmAngle, false );
|
||||
for( const auto& [layer, diameter] : knockouts )
|
||||
{
|
||||
SHAPE_POLY_SET pmPoly;
|
||||
TransformCircleToPolygon( pmPoly, via->GetPosition(), diameter / 2,
|
||||
via->GetMaxError(), ERROR_INSIDE );
|
||||
m_poly_holes[layer].Append( pmPoly );
|
||||
}
|
||||
|
||||
// Add knockout for silkscreen and soldermask on back side (full diameter)
|
||||
SHAPE_POLY_SET pmPoly;
|
||||
TransformCircleToPolygon( pmPoly, via->GetPosition(), backPM.size / 2,
|
||||
via->GetMaxError(), ERROR_INSIDE );
|
||||
m_poly_holes[B_SilkS].Append( pmPoly );
|
||||
m_poly_holes[B_Mask].Append( pmPoly );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,16 @@ private:
|
||||
bool buildGraphic3DShape( BOARD_ITEM* aItem, const VECTOR2D& aOrigin );
|
||||
void initOutputVariant();
|
||||
|
||||
/**
|
||||
* Check if a copper layer is within a backdrill layer span (inclusive).
|
||||
* @param aLayer The layer to check
|
||||
* @param aStartLayer The backdrill start layer
|
||||
* @param aEndLayer The backdrill end layer
|
||||
* @return true if the layer is within the span (inclusive)
|
||||
*/
|
||||
bool isLayerInBackdrillSpan( PCB_LAYER_ID aLayer, PCB_LAYER_ID aStartLayer,
|
||||
PCB_LAYER_ID aEndLayer ) const;
|
||||
|
||||
EXPORTER_STEP_PARAMS m_params;
|
||||
std::unique_ptr<FILENAME_RESOLVER> m_resolver;
|
||||
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <decompress.hpp>
|
||||
|
||||
#include <thread_pool.h>
|
||||
#include <trace_helpers.h>
|
||||
#include <board.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <footprint.h>
|
||||
@@ -109,6 +110,8 @@
|
||||
#include <BRepBuilderAPI_MakeWire.hxx>
|
||||
#include <BRepBuilderAPI_MakeFace.hxx>
|
||||
#include <BRepExtrema_DistShapeShape.hxx>
|
||||
#include <BRepPrimAPI_MakeCone.hxx>
|
||||
#include <BRepPrimAPI_MakeCylinder.hxx>
|
||||
#include <BRepPrimAPI_MakePrism.hxx>
|
||||
#include <BRepTools.hxx>
|
||||
#include <BRepLib_MakeWire.hxx>
|
||||
@@ -1060,6 +1063,458 @@ bool STEP_PCB_MODEL::AddBarrel( const SHAPE_SEGMENT& aShape, PCB_LAYER_ID aLayer
|
||||
}
|
||||
|
||||
|
||||
bool STEP_PCB_MODEL::AddBackdrill( const SHAPE_SEGMENT& aShape, PCB_LAYER_ID aLayerStart,
|
||||
PCB_LAYER_ID aLayerEnd, const VECTOR2D& aOrigin )
|
||||
{
|
||||
// A backdrill removes board material and copper plating between two layers.
|
||||
// The backdrill typically starts from an outer layer and drills into an inner layer.
|
||||
// For example, a top backdrill starts at F_Cu and ends at an inner layer.
|
||||
// A bottom backdrill starts at B_Cu and ends at an inner layer.
|
||||
|
||||
double margin = 0.001; // a small margin on the Z axis to ensure the hole
|
||||
// is bigger than the board section being removed
|
||||
|
||||
// Extra margin to extend past outer copper layers to ensure complete annular ring removal
|
||||
double copperMargin = 0.5; // 0.5mm extra to cut through any copper/pad thickness
|
||||
|
||||
double start_pos, start_thickness;
|
||||
double end_pos, end_thickness;
|
||||
getLayerZPlacement( aLayerStart, start_pos, start_thickness );
|
||||
getLayerZPlacement( aLayerEnd, end_pos, end_thickness );
|
||||
|
||||
// Calculate the Z extent of the backdrill
|
||||
double top = std::max( { start_pos, start_pos + start_thickness,
|
||||
end_pos, end_pos + end_thickness } );
|
||||
double bottom = std::min( { start_pos, start_pos + start_thickness,
|
||||
end_pos, end_pos + end_thickness } );
|
||||
|
||||
// Extend past outer copper layers if the backdrill reaches them
|
||||
if( aLayerStart == F_Cu || aLayerEnd == F_Cu )
|
||||
top += copperMargin;
|
||||
if( aLayerStart == B_Cu || aLayerEnd == B_Cu )
|
||||
bottom -= copperMargin;
|
||||
|
||||
double holeZsize = ( top - bottom ) + ( margin * 2 );
|
||||
double holeZpos = bottom - margin;
|
||||
|
||||
double backdrillDiameter = aShape.GetWidth();
|
||||
|
||||
TopoDS_Shape backdrillHole;
|
||||
|
||||
// Create the backdrill hole shape - this cuts the board body
|
||||
if( MakeShapeAsThickSegment( backdrillHole, aShape.GetSeg().A, aShape.GetSeg().B,
|
||||
backdrillDiameter, holeZsize, holeZpos, aOrigin ) )
|
||||
{
|
||||
m_boardCutouts.push_back( backdrillHole );
|
||||
|
||||
// This removes annular rings and barrel copper between the backdrill layers.
|
||||
m_copperCutouts.push_back( backdrillHole );
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool STEP_PCB_MODEL::AddCounterbore( const VECTOR2I& aPosition, int aDiameter, int aDepth,
|
||||
bool aFrontSide, const VECTOR2D& aOrigin )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCounterbore: pos=(%d,%d) diameter=%d depth=%d frontSide=%d origin=(%f,%f)" ),
|
||||
aPosition.x, aPosition.y, aDiameter, aDepth, aFrontSide ? 1 : 0, aOrigin.x, aOrigin.y );
|
||||
|
||||
// A counterbore is a cylindrical recess from the top or bottom of the board
|
||||
if( aDiameter <= 0 || aDepth <= 0 )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCounterbore: REJECTED - invalid diameter=%d or depth=%d" ),
|
||||
aDiameter, aDepth );
|
||||
return false;
|
||||
}
|
||||
|
||||
double margin = 0.001; // small margin to ensure clean cuts
|
||||
|
||||
// Extra margin to extend past outer copper layers to ensure complete annular ring removal
|
||||
double copperMargin = 0.5; // 0.5mm extra to cut through any copper/pad thickness
|
||||
|
||||
// Get board body position (between copper layers)
|
||||
double boardZpos, boardThickness;
|
||||
getBoardBodyZPlacement( boardZpos, boardThickness );
|
||||
|
||||
// Get copper layer positions - these extend beyond the board body
|
||||
double f_pos, f_thickness, b_pos, b_thickness;
|
||||
getLayerZPlacement( F_Cu, f_pos, f_thickness );
|
||||
getLayerZPlacement( B_Cu, b_pos, b_thickness );
|
||||
|
||||
// Calculate actual outer surfaces including copper
|
||||
// F_Cu: f_pos is inner surface, f_pos + f_thickness is outer surface (copper extends upward)
|
||||
// B_Cu: b_pos is inner surface, b_pos + b_thickness is outer surface (thickness is negative, copper extends downward)
|
||||
double topOuterSurface = std::max( f_pos, f_pos + f_thickness );
|
||||
double bottomOuterSurface = std::min( b_pos, b_pos + b_thickness );
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCounterbore: boardZpos=%f boardThickness=%f f_pos=%f f_thickness=%f topOuter=%f bottomOuter=%f" ),
|
||||
boardZpos, boardThickness, f_pos, f_thickness, topOuterSurface, bottomOuterSurface );
|
||||
|
||||
// Convert dimensions to mm
|
||||
double diameter_mm = pcbIUScale.IUTomm( aDiameter );
|
||||
double depth_mm = pcbIUScale.IUTomm( aDepth );
|
||||
double radius_mm = diameter_mm / 2.0;
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCounterbore: diameter_mm=%f depth_mm=%f radius_mm=%f" ),
|
||||
diameter_mm, depth_mm, radius_mm );
|
||||
|
||||
// Calculate cylinder position based on which side
|
||||
// The cylinder must extend past the outer surface to ensure complete copper removal
|
||||
double cylinderZpos;
|
||||
double cylinderHeight;
|
||||
|
||||
if( aFrontSide )
|
||||
{
|
||||
// Counterbore from top - cylinder extends from above outer copper surface down to depth
|
||||
// Add copperMargin above the surface to ensure complete annular ring removal
|
||||
cylinderZpos = topOuterSurface - depth_mm - margin;
|
||||
cylinderHeight = depth_mm + copperMargin + 2 * margin;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counterbore from bottom - cylinder extends from below outer copper surface up to depth
|
||||
// Add copperMargin below the surface to ensure complete annular ring removal
|
||||
cylinderZpos = bottomOuterSurface - copperMargin - margin;
|
||||
cylinderHeight = depth_mm + copperMargin + 2 * margin;
|
||||
}
|
||||
|
||||
// Convert position to mm
|
||||
double posX_mm = pcbIUScale.IUTomm( aPosition.x - aOrigin.x );
|
||||
double posY_mm = -pcbIUScale.IUTomm( aPosition.y - aOrigin.y );
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCounterbore: posX_mm=%f posY_mm=%f cylinderZpos=%f cylinderHeight=%f" ),
|
||||
posX_mm, posY_mm, cylinderZpos, cylinderHeight );
|
||||
|
||||
try
|
||||
{
|
||||
// Create coordinate system for the cylinder
|
||||
// The cylinder axis is along Z, positioned at the counterbore center
|
||||
gp_Ax2 axis( gp_Pnt( posX_mm, posY_mm, cylinderZpos ), gp::DZ() );
|
||||
|
||||
TopoDS_Shape cylinder = BRepPrimAPI_MakeCylinder( axis, radius_mm, cylinderHeight );
|
||||
|
||||
if( cylinder.IsNull() )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCounterbore: FAILED - cylinder shape is null" ) );
|
||||
m_reporter->Report( _( "Failed to create counterbore cylinder shape" ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add to both board and copper cutouts
|
||||
m_boardCutouts.push_back( cylinder );
|
||||
m_copperCutouts.push_back( cylinder );
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCounterbore: SUCCESS - added cylinder. boardCutouts=%zu copperCutouts=%zu" ),
|
||||
m_boardCutouts.size(), m_copperCutouts.size() );
|
||||
}
|
||||
catch( const Standard_Failure& e )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCounterbore: EXCEPTION - %s" ), e.GetMessageString() );
|
||||
m_reporter->Report( wxString::Format( _( "OCC exception creating counterbore: %s" ),
|
||||
e.GetMessageString() ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool STEP_PCB_MODEL::AddCountersink( const VECTOR2I& aPosition, int aDiameter, int aDepth,
|
||||
int aAngle, bool aFrontSide, const VECTOR2D& aOrigin )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: pos=(%d,%d) diameter=%d depth=%d angle=%d frontSide=%d origin=(%f,%f)" ),
|
||||
aPosition.x, aPosition.y, aDiameter, aDepth, aAngle, aFrontSide ? 1 : 0, aOrigin.x, aOrigin.y );
|
||||
|
||||
// A countersink is a conical recess from the top or bottom of the board
|
||||
// The angle parameter is the total cone angle in decidegrees
|
||||
// (angle between opposite sides of the cone)
|
||||
if( aDiameter <= 0 || aAngle <= 0 )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: REJECTED - invalid diameter=%d or angle=%d" ),
|
||||
aDiameter, aAngle );
|
||||
return false;
|
||||
}
|
||||
|
||||
double margin = 0.001; // small margin to ensure clean cuts
|
||||
|
||||
// Extra margin to extend past outer copper layers to ensure complete annular ring removal
|
||||
double copperMargin = 0.5; // 0.5mm extra to cut through any copper/pad thickness
|
||||
|
||||
// Get board body position (between copper layers)
|
||||
double boardZpos, boardThickness;
|
||||
getBoardBodyZPlacement( boardZpos, boardThickness );
|
||||
|
||||
// Get copper layer positions - these extend beyond the board body
|
||||
double f_pos, f_thickness, b_pos, b_thickness;
|
||||
getLayerZPlacement( F_Cu, f_pos, f_thickness );
|
||||
getLayerZPlacement( B_Cu, b_pos, b_thickness );
|
||||
|
||||
// Calculate actual outer surfaces including copper
|
||||
double topOuterSurface = std::max( f_pos, f_pos + f_thickness );
|
||||
double bottomOuterSurface = std::min( b_pos, b_pos + b_thickness );
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: boardZpos=%f boardThickness=%f f_pos=%f f_thickness=%f topOuter=%f bottomOuter=%f" ),
|
||||
boardZpos, boardThickness, f_pos, f_thickness, topOuterSurface, bottomOuterSurface );
|
||||
|
||||
// Convert dimensions to mm
|
||||
double diameter_mm = pcbIUScale.IUTomm( aDiameter );
|
||||
double radius_mm = diameter_mm / 2.0;
|
||||
|
||||
// Convert angle from decidegrees to radians
|
||||
// aAngle is the total cone angle, so half-angle is used for geometry
|
||||
double halfAngleRad = ( aAngle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
|
||||
// If depth is not specified, calculate it from the diameter and angle
|
||||
// The countersink depth is the full cone height: depth = radius / tan(halfAngle)
|
||||
double depth_mm;
|
||||
if( aDepth <= 0 )
|
||||
{
|
||||
// Calculate depth from diameter and angle
|
||||
depth_mm = radius_mm / tan( halfAngleRad );
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: depth not specified, calculated depth_mm=%f from radius=%f and angle" ),
|
||||
depth_mm, radius_mm );
|
||||
}
|
||||
else
|
||||
{
|
||||
depth_mm = pcbIUScale.IUTomm( aDepth );
|
||||
}
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: diameter_mm=%f depth_mm=%f radius_mm=%f halfAngleRad=%f (deg=%f)" ),
|
||||
diameter_mm, depth_mm, radius_mm, halfAngleRad, halfAngleRad * 180.0 / M_PI );
|
||||
|
||||
// Calculate the cone geometry
|
||||
// For a countersink, R1 (bottom radius) may be 0 (sharp point) or non-zero
|
||||
// R2 (top radius) is at the surface
|
||||
// The cone depth determines how deep it goes
|
||||
|
||||
// Calculate bottom radius based on depth and angle
|
||||
// tan(halfAngle) = (R2 - R1) / depth
|
||||
// If we want the surface radius to be radius_mm and depth to be depth_mm:
|
||||
// R1 = R2 - depth * tan(halfAngle)
|
||||
double bottomRadius_mm = radius_mm - depth_mm * tan( halfAngleRad );
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: bottomRadius_mm=%f (before clamp), tan(halfAngle)=%f" ),
|
||||
bottomRadius_mm, tan( halfAngleRad ) );
|
||||
|
||||
if( bottomRadius_mm < 0 )
|
||||
bottomRadius_mm = 0; // Cone comes to a point before reaching full depth
|
||||
|
||||
// Calculate position based on which side
|
||||
// Extend the cone past the outer surface by copperMargin to ensure complete copper removal
|
||||
double coneZpos;
|
||||
double coneHeight = depth_mm + copperMargin + margin;
|
||||
double r1, r2; // bottom and top radii for BRepPrimAPI_MakeCone
|
||||
|
||||
// Convert position to mm
|
||||
double posX_mm = pcbIUScale.IUTomm( aPosition.x - aOrigin.x );
|
||||
double posY_mm = -pcbIUScale.IUTomm( aPosition.y - aOrigin.y );
|
||||
|
||||
try
|
||||
{
|
||||
TopoDS_Shape cone;
|
||||
|
||||
if( aFrontSide )
|
||||
{
|
||||
// Countersink from top - cone apex points down
|
||||
// In OCC, cone is built from z=0 to z=H with R1 at z=0 and R2 at z=H
|
||||
// For a top countersink, we want large radius at top, small at bottom
|
||||
coneZpos = topOuterSurface - depth_mm - margin;
|
||||
r1 = bottomRadius_mm; // smaller radius at bottom (deeper into board)
|
||||
// Extend the top radius to account for the copperMargin extension above the surface
|
||||
r2 = radius_mm + ( copperMargin + margin ) * tan( halfAngleRad );
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: FRONT - coneZpos=%f r1=%f r2=%f coneHeight=%f" ),
|
||||
coneZpos, r1, r2, coneHeight );
|
||||
|
||||
gp_Ax2 axis( gp_Pnt( posX_mm, posY_mm, coneZpos ), gp::DZ() );
|
||||
cone = BRepPrimAPI_MakeCone( axis, r1, r2, coneHeight );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Countersink from bottom - cone apex points up
|
||||
// For bottom countersink, large radius at bottom, small at top
|
||||
// Extend below the surface by copperMargin
|
||||
coneZpos = bottomOuterSurface - copperMargin - margin;
|
||||
// Extend the bottom radius to account for the copperMargin extension below the surface
|
||||
r1 = radius_mm + ( copperMargin + margin ) * tan( halfAngleRad );
|
||||
r2 = bottomRadius_mm; // smaller radius at top (deeper into board)
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: BACK - coneZpos=%f r1=%f r2=%f coneHeight=%f" ),
|
||||
coneZpos, r1, r2, coneHeight );
|
||||
|
||||
gp_Ax2 axis( gp_Pnt( posX_mm, posY_mm, coneZpos ), gp::DZ() );
|
||||
cone = BRepPrimAPI_MakeCone( axis, r1, r2, coneHeight );
|
||||
}
|
||||
|
||||
if( cone.IsNull() )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: FAILED - cone shape is null" ) );
|
||||
m_reporter->Report( _( "Failed to create countersink cone shape" ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add to both board and copper cutouts
|
||||
m_boardCutouts.push_back( cone );
|
||||
m_copperCutouts.push_back( cone );
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: SUCCESS - added cone. boardCutouts=%zu copperCutouts=%zu" ),
|
||||
m_boardCutouts.size(), m_copperCutouts.size() );
|
||||
}
|
||||
catch( const Standard_Failure& e )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "AddCountersink: EXCEPTION - %s" ), e.GetMessageString() );
|
||||
m_reporter->Report( wxString::Format( _( "OCC exception creating countersink: %s" ),
|
||||
e.GetMessageString() ),
|
||||
RPT_SEVERITY_ERROR );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::map<PCB_LAYER_ID, int> STEP_PCB_MODEL::GetCopperLayerKnockouts( int aDiameter, int aDepth,
|
||||
int aAngle, bool aFrontSide )
|
||||
{
|
||||
std::map<PCB_LAYER_ID, int> knockouts;
|
||||
|
||||
// Get the outer surface positions (including copper)
|
||||
double f_pos, f_thickness, b_pos, b_thickness;
|
||||
getLayerZPlacement( F_Cu, f_pos, f_thickness );
|
||||
getLayerZPlacement( B_Cu, b_pos, b_thickness );
|
||||
|
||||
double topOuterSurface = std::max( f_pos, f_pos + f_thickness );
|
||||
double bottomOuterSurface = std::min( b_pos, b_pos + b_thickness );
|
||||
|
||||
// Convert dimensions to mm
|
||||
double diameter_mm = pcbIUScale.IUTomm( aDiameter );
|
||||
double radius_mm = diameter_mm / 2.0;
|
||||
|
||||
// Calculate depth in mm
|
||||
double depth_mm;
|
||||
double halfAngleRad = 0.0;
|
||||
|
||||
if( aAngle > 0 )
|
||||
{
|
||||
// Countersink - calculate half angle
|
||||
halfAngleRad = ( aAngle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
|
||||
// If depth is not specified for countersink, calculate from diameter and angle
|
||||
if( aDepth <= 0 )
|
||||
depth_mm = radius_mm / tan( halfAngleRad );
|
||||
else
|
||||
depth_mm = pcbIUScale.IUTomm( aDepth );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counterbore - use specified depth
|
||||
depth_mm = pcbIUScale.IUTomm( aDepth );
|
||||
}
|
||||
|
||||
// Determine the Z range of the feature
|
||||
double featureTop, featureBottom;
|
||||
|
||||
if( aFrontSide )
|
||||
{
|
||||
featureTop = topOuterSurface;
|
||||
featureBottom = topOuterSurface - depth_mm;
|
||||
}
|
||||
else
|
||||
{
|
||||
featureBottom = bottomOuterSurface;
|
||||
featureTop = bottomOuterSurface + depth_mm;
|
||||
}
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "GetCopperLayerKnockouts: featureTop=%f featureBottom=%f depth_mm=%f frontSide=%d" ),
|
||||
featureTop, featureBottom, depth_mm, aFrontSide ? 1 : 0 );
|
||||
|
||||
// Iterate through all copper layers and check if they fall within the feature range
|
||||
for( const BOARD_STACKUP_ITEM* item : m_stackup.GetList() )
|
||||
{
|
||||
if( item->GetType() != BS_ITEM_TYPE_COPPER )
|
||||
continue;
|
||||
|
||||
PCB_LAYER_ID layer = item->GetBrdLayerId();
|
||||
double layerZ, layerThickness;
|
||||
getLayerZPlacement( layer, layerZ, layerThickness );
|
||||
|
||||
// Get the Z range of this copper layer (both inner and outer surfaces)
|
||||
double layerTop = std::max( layerZ, layerZ + layerThickness );
|
||||
double layerBottom = std::min( layerZ, layerZ + layerThickness );
|
||||
|
||||
// Check if this layer overlaps with the feature Z range
|
||||
// A layer is affected if any part of it is within the feature range
|
||||
bool layerInRange = ( layerTop >= featureBottom && layerBottom <= featureTop );
|
||||
|
||||
wxLogTrace( traceKiCad2Step, wxT( "GetCopperLayerKnockouts: layer %d Z=[%f, %f] feature=[%f, %f] inRange=%d" ),
|
||||
static_cast<int>( layer ), layerBottom, layerTop, featureBottom, featureTop, layerInRange ? 1 : 0 );
|
||||
|
||||
if( !layerInRange )
|
||||
continue;
|
||||
|
||||
int knockoutDiameter;
|
||||
|
||||
if( aAngle > 0 )
|
||||
{
|
||||
// Countersink - calculate diameter at this layer's Z level
|
||||
// Use the layer surface that's closest to the feature origin surface
|
||||
double layerSurfaceZ;
|
||||
if( aFrontSide )
|
||||
{
|
||||
// For front-side countersink, use the top surface of the layer
|
||||
layerSurfaceZ = layerTop;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For back-side countersink, use the bottom surface of the layer
|
||||
layerSurfaceZ = layerBottom;
|
||||
}
|
||||
|
||||
// Distance from the surface determines the radius at this Z
|
||||
double distanceFromSurface;
|
||||
if( aFrontSide )
|
||||
distanceFromSurface = topOuterSurface - layerSurfaceZ;
|
||||
else
|
||||
distanceFromSurface = layerSurfaceZ - bottomOuterSurface;
|
||||
|
||||
// Radius at this depth: r = R - d * tan(halfAngle)
|
||||
double radiusAtLayer_mm = radius_mm - distanceFromSurface * tan( halfAngleRad );
|
||||
|
||||
if( radiusAtLayer_mm <= 0 )
|
||||
{
|
||||
wxLogTrace( traceKiCad2Step, wxT( "GetCopperLayerKnockouts: layer %d - countersink tapers to point before this layer" ),
|
||||
static_cast<int>( layer ) );
|
||||
continue; // Cone tapers to a point before reaching this layer
|
||||
}
|
||||
|
||||
knockoutDiameter = pcbIUScale.mmToIU( radiusAtLayer_mm * 2.0 );
|
||||
wxLogTrace( traceKiCad2Step, wxT( "GetCopperLayerKnockouts: layer %d (countersink) - distFromSurface=%f radiusAtLayer=%f diameter=%d" ),
|
||||
static_cast<int>( layer ), distanceFromSurface, radiusAtLayer_mm, knockoutDiameter );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counterbore - constant diameter
|
||||
knockoutDiameter = aDiameter;
|
||||
wxLogTrace( traceKiCad2Step, wxT( "GetCopperLayerKnockouts: layer %d (counterbore) - diameter=%d" ),
|
||||
static_cast<int>( layer ), knockoutDiameter );
|
||||
}
|
||||
|
||||
knockouts[layer] = knockoutDiameter;
|
||||
}
|
||||
|
||||
return knockouts;
|
||||
}
|
||||
|
||||
|
||||
void STEP_PCB_MODEL::getLayerZPlacement( const PCB_LAYER_ID aLayer, double& aZPos,
|
||||
double& aThickness )
|
||||
{
|
||||
|
||||
@@ -113,6 +113,72 @@ public:
|
||||
bool AddBarrel( const SHAPE_SEGMENT& aShape, PCB_LAYER_ID aLayerTop, PCB_LAYER_ID aLayerBot,
|
||||
bool aVia, const VECTOR2D& aOrigin, const wxString& aNetname );
|
||||
|
||||
/**
|
||||
* Add a backdrill hole shape to remove board material and copper plating.
|
||||
*
|
||||
* A backdrill removes board material between the specified layers (inclusive), removes
|
||||
* annular rings on copper layers within that span, and removes the copper barrel plating
|
||||
* through those layers.
|
||||
*
|
||||
* @param aShape The hole shape (position and diameter of the backdrill)
|
||||
* @param aLayerStart The starting copper layer (e.g., F_Cu for top backdrill)
|
||||
* @param aLayerEnd The ending copper layer (inclusive, the layer where backdrill stops)
|
||||
* @param aOrigin The origin offset for coordinate transformation
|
||||
* @return true if successfully added
|
||||
*/
|
||||
bool AddBackdrill( const SHAPE_SEGMENT& aShape, PCB_LAYER_ID aLayerStart,
|
||||
PCB_LAYER_ID aLayerEnd, const VECTOR2D& aOrigin );
|
||||
|
||||
/**
|
||||
* Add a counterbore shape to remove board material from the top or bottom of a hole.
|
||||
*
|
||||
* A counterbore creates a cylindrical recess from the specified side of the board,
|
||||
* removing board material and copper down to the specified depth.
|
||||
*
|
||||
* @param aPosition The center position of the counterbore
|
||||
* @param aDiameter The diameter of the counterbore (in IU)
|
||||
* @param aDepth The depth of the counterbore from the board surface (in IU)
|
||||
* @param aFrontSide True if counterbore is on the front (top) side, false for back (bottom)
|
||||
* @param aOrigin The origin offset for coordinate transformation
|
||||
* @return true if successfully added
|
||||
*/
|
||||
bool AddCounterbore( const VECTOR2I& aPosition, int aDiameter, int aDepth,
|
||||
bool aFrontSide, const VECTOR2D& aOrigin );
|
||||
|
||||
/**
|
||||
* Add a countersink shape to remove board material from the top or bottom of a hole.
|
||||
*
|
||||
* A countersink creates an inverted cone recess from the specified side of the board.
|
||||
* The angle parameter specifies the total cone angle (the angle between opposite sides
|
||||
* of the cone), so the angle between the board surface and the cone slope is half this value.
|
||||
*
|
||||
* @param aPosition The center position of the countersink
|
||||
* @param aDiameter The diameter of the countersink at the board surface (in IU)
|
||||
* @param aDepth The depth of the countersink from the board surface (in IU)
|
||||
* @param aAngle The total cone angle in decidegrees (e.g., 900 = 90°, 820 = 82°)
|
||||
* @param aFrontSide True if countersink is on the front (top) side, false for back (bottom)
|
||||
* @param aOrigin The origin offset for coordinate transformation
|
||||
* @return true if successfully added
|
||||
*/
|
||||
bool AddCountersink( const VECTOR2I& aPosition, int aDiameter, int aDepth, int aAngle,
|
||||
bool aFrontSide, const VECTOR2D& aOrigin );
|
||||
|
||||
/**
|
||||
* Get the knockout diameters for copper layers that a counterbore or countersink crosses.
|
||||
*
|
||||
* For a counterbore, the diameter is constant for all layers within the depth.
|
||||
* For a countersink, the diameter varies based on the cone angle and the Z position
|
||||
* of each layer.
|
||||
*
|
||||
* @param aDiameter The diameter at the board surface (in IU)
|
||||
* @param aDepth The depth of the feature from the board surface (in IU)
|
||||
* @param aAngle The cone angle in decidegrees (0 for counterbore, >0 for countersink)
|
||||
* @param aFrontSide True if feature is on the front (top) side, false for back (bottom)
|
||||
* @return A map of PCB_LAYER_ID to knockout diameter (in IU) for each affected copper layer
|
||||
*/
|
||||
std::map<PCB_LAYER_ID, int> GetCopperLayerKnockouts( int aDiameter, int aDepth,
|
||||
int aAngle, bool aFrontSide );
|
||||
|
||||
// add a set of polygons (must be in final position)
|
||||
bool AddPolygonShapes( const SHAPE_POLY_SET* aPolyShapes, PCB_LAYER_ID aLayer,
|
||||
const VECTOR2D& aOrigin, const wxString& aNetname );
|
||||
|
||||
+605
-9
@@ -490,25 +490,371 @@ bool PAD::FlashLayer( int aLayer, bool aOnlyCheckIfPermitted ) const
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetDrillSizeX( const int aX )
|
||||
void PAD::SetPrimaryDrillSize( const VECTOR2I& aSize )
|
||||
{
|
||||
m_padStack.Drill().size = aSize;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetPrimaryDrillSizeX( const int aX )
|
||||
{
|
||||
m_padStack.Drill().size.x = aX;
|
||||
|
||||
if( GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE )
|
||||
SetDrillSizeY( aX );
|
||||
if( GetPrimaryDrillShape() == PAD_DRILL_SHAPE::CIRCLE )
|
||||
m_padStack.Drill().size.y = aX;
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetDrillShape( PAD_DRILL_SHAPE aShape )
|
||||
void PAD::SetDrillSizeX( const int aX )
|
||||
{
|
||||
SetPrimaryDrillSizeX( aX );
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetPrimaryDrillSizeY( const int aY )
|
||||
{
|
||||
m_padStack.Drill().size.y = aY;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetDrillSizeY( const int aY )
|
||||
{
|
||||
SetPrimaryDrillSizeY( aY );
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetPrimaryDrillShape( PAD_DRILL_SHAPE aShape )
|
||||
{
|
||||
m_padStack.Drill().shape = aShape;
|
||||
|
||||
if( aShape == PAD_DRILL_SHAPE::CIRCLE )
|
||||
SetDrillSizeY( GetDrillSizeX() );
|
||||
m_padStack.Drill().size.y = m_padStack.Drill().size.x;
|
||||
|
||||
m_shapesDirty = true;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetPrimaryDrillStartLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.Drill().start = aLayer;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetPrimaryDrillEndLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.Drill().end = aLayer;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
bool PAD::IsBackdrilledOrPostMachined( PCB_LAYER_ID aLayer ) const
|
||||
{
|
||||
if( !IsCopperLayer( aLayer ) )
|
||||
return false;
|
||||
|
||||
const BOARD* board = GetBoard();
|
||||
|
||||
if( !board )
|
||||
return false;
|
||||
|
||||
// Check secondary drill (backdrill from top)
|
||||
const PADSTACK::DRILL_PROPS& secondaryDrill = m_padStack.SecondaryDrill();
|
||||
|
||||
if( secondaryDrill.size.x > 0 && secondaryDrill.start != UNDEFINED_LAYER
|
||||
&& secondaryDrill.end != UNDEFINED_LAYER )
|
||||
{
|
||||
// Secondary drill goes from start to end layer, removing copper on those layers
|
||||
int startOrdinal = board->IsLayerEnabled( secondaryDrill.start )
|
||||
? board->IsLayerEnabled( F_Cu ) ? ( secondaryDrill.start == F_Cu ? 0 : secondaryDrill.start / 2 + 1 )
|
||||
: secondaryDrill.start / 2
|
||||
: -1;
|
||||
int endOrdinal = board->IsLayerEnabled( secondaryDrill.end )
|
||||
? board->IsLayerEnabled( F_Cu ) ? ( secondaryDrill.end == B_Cu ? board->GetCopperLayerCount() - 1 : secondaryDrill.end / 2 + 1 )
|
||||
: secondaryDrill.end / 2
|
||||
: -1;
|
||||
int layerOrdinal = board->IsLayerEnabled( aLayer )
|
||||
? board->IsLayerEnabled( F_Cu ) ? ( aLayer == F_Cu ? 0 : aLayer == B_Cu ? board->GetCopperLayerCount() - 1 : aLayer / 2 + 1 )
|
||||
: aLayer / 2
|
||||
: -1;
|
||||
|
||||
if( layerOrdinal >= 0 && startOrdinal >= 0 && endOrdinal >= 0 )
|
||||
{
|
||||
if( startOrdinal > endOrdinal )
|
||||
std::swap( startOrdinal, endOrdinal );
|
||||
|
||||
if( layerOrdinal >= startOrdinal && layerOrdinal <= endOrdinal )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check tertiary drill (backdrill from bottom)
|
||||
const PADSTACK::DRILL_PROPS& tertiaryDrill = m_padStack.TertiaryDrill();
|
||||
|
||||
if( tertiaryDrill.size.x > 0 && tertiaryDrill.start != UNDEFINED_LAYER
|
||||
&& tertiaryDrill.end != UNDEFINED_LAYER )
|
||||
{
|
||||
int startOrdinal = board->IsLayerEnabled( tertiaryDrill.start )
|
||||
? board->IsLayerEnabled( F_Cu ) ? ( tertiaryDrill.start == F_Cu ? 0 : tertiaryDrill.start / 2 + 1 )
|
||||
: tertiaryDrill.start / 2
|
||||
: -1;
|
||||
int endOrdinal = board->IsLayerEnabled( tertiaryDrill.end )
|
||||
? board->IsLayerEnabled( F_Cu ) ? ( tertiaryDrill.end == B_Cu ? board->GetCopperLayerCount() - 1 : tertiaryDrill.end / 2 + 1 )
|
||||
: tertiaryDrill.end / 2
|
||||
: -1;
|
||||
int layerOrdinal = board->IsLayerEnabled( aLayer )
|
||||
? board->IsLayerEnabled( F_Cu ) ? ( aLayer == F_Cu ? 0 : aLayer == B_Cu ? board->GetCopperLayerCount() - 1 : aLayer / 2 + 1 )
|
||||
: aLayer / 2
|
||||
: -1;
|
||||
|
||||
if( layerOrdinal >= 0 && startOrdinal >= 0 && endOrdinal >= 0 )
|
||||
{
|
||||
if( startOrdinal > endOrdinal )
|
||||
std::swap( startOrdinal, endOrdinal );
|
||||
|
||||
if( layerOrdinal >= startOrdinal && layerOrdinal <= endOrdinal )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the layer is affected by post-machining
|
||||
if( GetPostMachiningKnockout( aLayer ) > 0 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int PAD::GetPostMachiningKnockout( PCB_LAYER_ID aLayer ) const
|
||||
{
|
||||
if( !IsCopperLayer( aLayer ) )
|
||||
return 0;
|
||||
|
||||
const BOARD* board = GetBoard();
|
||||
|
||||
if( !board )
|
||||
return 0;
|
||||
|
||||
const BOARD_STACKUP& stackup = board->GetDesignSettings().GetStackupDescriptor();
|
||||
|
||||
// Check front post-machining (counterbore/countersink from top)
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPM = m_padStack.FrontPostMachining();
|
||||
|
||||
if( frontPM.mode.has_value() && *frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& *frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN && frontPM.size > 0 )
|
||||
{
|
||||
int pmDepth = frontPM.depth;
|
||||
|
||||
// For countersink without explicit depth, calculate from diameter and angle
|
||||
if( pmDepth <= 0 && *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK
|
||||
&& frontPM.angle > 0 )
|
||||
{
|
||||
double halfAngleRad = ( frontPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
pmDepth = static_cast<int>( ( frontPM.size / 2.0 ) / tan( halfAngleRad ) );
|
||||
}
|
||||
|
||||
if( pmDepth > 0 )
|
||||
{
|
||||
// Calculate distance from F_Cu to aLayer
|
||||
int layerDist = stackup.GetLayerDistance( F_Cu, aLayer );
|
||||
|
||||
if( layerDist < pmDepth )
|
||||
{
|
||||
// For countersink, diameter decreases with depth
|
||||
if( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && frontPM.angle > 0 )
|
||||
{
|
||||
double halfAngleRad = ( frontPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
int diameterAtLayer = frontPM.size - static_cast<int>( 2.0 * layerDist * tan( halfAngleRad ) );
|
||||
return std::max( 0, diameterAtLayer );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counterbore - constant diameter
|
||||
return frontPM.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check back post-machining (counterbore/countersink from bottom)
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPM = m_padStack.BackPostMachining();
|
||||
|
||||
if( backPM.mode.has_value() && *backPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& *backPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN && backPM.size > 0 )
|
||||
{
|
||||
int pmDepth = backPM.depth;
|
||||
|
||||
// For countersink without explicit depth, calculate from diameter and angle
|
||||
if( pmDepth <= 0 && *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK
|
||||
&& backPM.angle > 0 )
|
||||
{
|
||||
double halfAngleRad = ( backPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
pmDepth = static_cast<int>( ( backPM.size / 2.0 ) / tan( halfAngleRad ) );
|
||||
}
|
||||
|
||||
if( pmDepth > 0 )
|
||||
{
|
||||
// Calculate distance from B_Cu to aLayer
|
||||
int layerDist = stackup.GetLayerDistance( B_Cu, aLayer );
|
||||
|
||||
if( layerDist < pmDepth )
|
||||
{
|
||||
// For countersink, diameter decreases with depth
|
||||
if( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && backPM.angle > 0 )
|
||||
{
|
||||
double halfAngleRad = ( backPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
int diameterAtLayer = backPM.size - static_cast<int>( 2.0 * layerDist * tan( halfAngleRad ) );
|
||||
return std::max( 0, diameterAtLayer );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counterbore - constant diameter
|
||||
return backPM.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetPrimaryDrillFilled( const std::optional<bool>& aFilled )
|
||||
{
|
||||
m_padStack.Drill().is_filled = aFilled;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetPrimaryDrillFilledFlag( bool aFilled )
|
||||
{
|
||||
m_padStack.Drill().is_filled = aFilled;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetPrimaryDrillCapped( const std::optional<bool>& aCapped )
|
||||
{
|
||||
m_padStack.Drill().is_capped = aCapped;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetPrimaryDrillCappedFlag( bool aCapped )
|
||||
{
|
||||
m_padStack.Drill().is_capped = aCapped;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetSecondaryDrillSize( const VECTOR2I& aSize )
|
||||
{
|
||||
m_padStack.SecondaryDrill().size = aSize;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetSecondaryDrillSizeX( int aX )
|
||||
{
|
||||
m_padStack.SecondaryDrill().size.x = aX;
|
||||
|
||||
if( GetSecondaryDrillShape() == PAD_DRILL_SHAPE::CIRCLE )
|
||||
m_padStack.SecondaryDrill().size.y = aX;
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetSecondaryDrillSizeY( int aY )
|
||||
{
|
||||
m_padStack.SecondaryDrill().size.y = aY;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::ClearSecondaryDrillSize()
|
||||
{
|
||||
m_padStack.SecondaryDrill().size = VECTOR2I( 0, 0 );
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetSecondaryDrillShape( PAD_DRILL_SHAPE aShape )
|
||||
{
|
||||
m_padStack.SecondaryDrill().shape = aShape;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetSecondaryDrillStartLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.SecondaryDrill().start = aLayer;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetSecondaryDrillEndLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.SecondaryDrill().end = aLayer;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetTertiaryDrillSize( const VECTOR2I& aSize )
|
||||
{
|
||||
m_padStack.TertiaryDrill().size = aSize;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetTertiaryDrillSizeX( int aX )
|
||||
{
|
||||
m_padStack.TertiaryDrill().size.x = aX;
|
||||
|
||||
if( GetTertiaryDrillShape() == PAD_DRILL_SHAPE::CIRCLE )
|
||||
m_padStack.TertiaryDrill().size.y = aX;
|
||||
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetTertiaryDrillSizeY( int aY )
|
||||
{
|
||||
m_padStack.TertiaryDrill().size.y = aY;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::ClearTertiaryDrillSize()
|
||||
{
|
||||
m_padStack.TertiaryDrill().size = VECTOR2I( 0, 0 );
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetTertiaryDrillShape( PAD_DRILL_SHAPE aShape )
|
||||
{
|
||||
m_padStack.TertiaryDrill().shape = aShape;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetTertiaryDrillStartLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.TertiaryDrill().start = aLayer;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
void PAD::SetTertiaryDrillEndLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.TertiaryDrill().end = aLayer;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
|
||||
@@ -600,6 +946,47 @@ std::shared_ptr<SHAPE> PAD::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING fla
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this layer has copper removed by backdrill or post-machining
|
||||
if( IsBackdrilledOrPostMachined( aLayer ) )
|
||||
{
|
||||
std::shared_ptr<SHAPE_COMPOUND> effective_compound = std::make_shared<SHAPE_COMPOUND>();
|
||||
|
||||
// Return the larger of the backdrill or post-machining hole
|
||||
int holeSize = 0;
|
||||
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPM = Padstack().FrontPostMachining();
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPM = Padstack().BackPostMachining();
|
||||
|
||||
if( frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
holeSize = std::max( holeSize, frontPM.size );
|
||||
}
|
||||
|
||||
if( backPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
holeSize = std::max( holeSize, backPM.size );
|
||||
}
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secDrill = Padstack().SecondaryDrill();
|
||||
|
||||
if( secDrill.start != UNDEFINED_LAYER && secDrill.end != UNDEFINED_LAYER )
|
||||
holeSize = std::max( holeSize, secDrill.size.x );
|
||||
|
||||
if( holeSize > 0 )
|
||||
{
|
||||
effective_compound->AddShape(
|
||||
std::make_shared<SHAPE_CIRCLE>( GetPosition(), holeSize / 2 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
effective_compound->AddShape( GetEffectiveHoleShape() );
|
||||
}
|
||||
|
||||
return effective_compound;
|
||||
}
|
||||
|
||||
if( GetAttribute() == PAD_ATTRIB::PTH )
|
||||
{
|
||||
bool flash;
|
||||
@@ -1976,7 +2363,7 @@ const BOX2I PAD::ViewBBox() const
|
||||
} );
|
||||
|
||||
BOX2I bbox = GetBoundingBox();
|
||||
int clearance = 0;
|
||||
int clearance = 0;
|
||||
|
||||
// If we're drawing clearance lines then get the biggest possible clearance
|
||||
if( PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() ) )
|
||||
@@ -2566,10 +2953,10 @@ void PAD::doCheckPad( PCB_LAYER_ID aLayer, UNITS_PROVIDER* aUnitsProvider, bool
|
||||
// For now we just check for disappearing paste
|
||||
wxSize paste_size;
|
||||
int paste_margin = GetLocalSolderPasteMargin().value_or( 0 );
|
||||
double paste_ratio = GetLocalSolderPasteMarginRatio().value_or( 0 );
|
||||
auto mratio = GetLocalSolderPasteMarginRatio();
|
||||
|
||||
paste_size.x = pad_size.x + paste_margin + KiROUND( pad_size.x * paste_ratio );
|
||||
paste_size.y = pad_size.y + paste_margin + KiROUND( pad_size.y * paste_ratio );
|
||||
paste_size.x = pad_size.x + paste_margin + KiROUND( pad_size.x * mratio.value_or( 0 ) );
|
||||
paste_size.y = pad_size.y + paste_margin + KiROUND( pad_size.y * mratio.value_or( 0 ) );
|
||||
|
||||
if( paste_size.x <= 0 || paste_size.y <= 0 )
|
||||
{
|
||||
@@ -2832,9 +3219,38 @@ static struct PAD_DESC
|
||||
.Map( PAD_PROP::PRESSFIT, _HKI( "Press-fit pad" ) );
|
||||
|
||||
ENUM_MAP<PAD_DRILL_SHAPE>::Instance()
|
||||
.Map( PAD_DRILL_SHAPE::UNDEFINED, _HKI( "Undefined" ) )
|
||||
.Map( PAD_DRILL_SHAPE::CIRCLE, _HKI( "Round" ) )
|
||||
.Map( PAD_DRILL_SHAPE::OBLONG, _HKI( "Oblong" ) );
|
||||
|
||||
// Ensure post-machining mode enum choices are defined before properties use them
|
||||
{
|
||||
ENUM_MAP<PAD_DRILL_POST_MACHINING_MODE>& pmMap =
|
||||
ENUM_MAP<PAD_DRILL_POST_MACHINING_MODE>::Instance();
|
||||
|
||||
if( pmMap.Choices().GetCount() == 0 )
|
||||
{
|
||||
pmMap.Undefined( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
.Map( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED, _HKI( "Not post-machined" ) )
|
||||
.Map( PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE, _HKI( "Counterbore" ) )
|
||||
.Map( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK, _HKI( "Countersink" ) );
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure backdrill mode enum choices are defined before properties use them
|
||||
{
|
||||
ENUM_MAP<BACKDRILL_MODE>& bdMap = ENUM_MAP<BACKDRILL_MODE>::Instance();
|
||||
|
||||
if( bdMap.Choices().GetCount() == 0 )
|
||||
{
|
||||
bdMap.Undefined( BACKDRILL_MODE::NO_BACKDRILL )
|
||||
.Map( BACKDRILL_MODE::NO_BACKDRILL, _HKI( "No backdrill" ) )
|
||||
.Map( BACKDRILL_MODE::BACKDRILL_BOTTOM, _HKI( "Backdrill bottom" ) )
|
||||
.Map( BACKDRILL_MODE::BACKDRILL_TOP, _HKI( "Backdrill top" ) )
|
||||
.Map( BACKDRILL_MODE::BACKDRILL_BOTH, _HKI( "Backdrill both" ) );
|
||||
}
|
||||
}
|
||||
|
||||
ENUM_MAP<ZONE_CONNECTION>& zcMap = ENUM_MAP<ZONE_CONNECTION>::Instance();
|
||||
|
||||
if( zcMap.Choices().GetCount() == 0 )
|
||||
@@ -2899,6 +3315,8 @@ static struct PAD_DESC
|
||||
isCopperPad );
|
||||
|
||||
const wxString groupPad = _HKI( "Pad Properties" );
|
||||
const wxString groupPostMachining = _HKI( "Post-machining Properties" );
|
||||
const wxString groupBackdrill = _HKI( "Backdrill Properties" );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_ATTRIB>( _HKI( "Pad Type" ),
|
||||
&PAD::SetAttribute, &PAD::GetAttribute ), groupPad );
|
||||
@@ -2993,6 +3411,184 @@ static struct PAD_DESC
|
||||
return true;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_DRILL_POST_MACHINING_MODE>( _HKI( "Top Post-machining" ),
|
||||
&PAD::SetFrontPostMachiningMode, &PAD::GetFrontPostMachiningMode ), groupPostMachining )
|
||||
.SetWriteableFunc( padCanHaveHole )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
return pad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE;
|
||||
}
|
||||
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Top Post-machining Size" ),
|
||||
&PAD::SetFrontPostMachiningSize, &PAD::GetFrontPostMachiningSize, PROPERTY_DISPLAY::PT_SIZE ), groupPostMachining )
|
||||
.SetWriteableFunc( padCanHaveHole )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetFrontPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE || mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Top Counterbore Depth" ),
|
||||
&PAD::SetFrontPostMachiningDepth, &PAD::GetFrontPostMachiningDepth, PROPERTY_DISPLAY::PT_SIZE ), groupPostMachining )
|
||||
.SetWriteableFunc( padCanHaveHole )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetFrontPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Top Countersink Angle" ),
|
||||
&PAD::SetFrontPostMachiningAngle, &PAD::GetFrontPostMachiningAngle, PROPERTY_DISPLAY::PT_DECIDEGREE ), groupPostMachining )
|
||||
.SetWriteableFunc( padCanHaveHole )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetFrontPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_DRILL_POST_MACHINING_MODE>( _HKI( "Bottom Post-machining" ),
|
||||
&PAD::SetBackPostMachiningMode, &PAD::GetBackPostMachiningMode ), groupPostMachining )
|
||||
.SetWriteableFunc( padCanHaveHole )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
return pad->GetDrillShape() == PAD_DRILL_SHAPE::CIRCLE;
|
||||
}
|
||||
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Bottom Post-machining Size" ),
|
||||
&PAD::SetBackPostMachiningSize, &PAD::GetBackPostMachiningSize, PROPERTY_DISPLAY::PT_SIZE ), groupPostMachining )
|
||||
.SetWriteableFunc( padCanHaveHole )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetBackPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE || mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Bottom Counterbore Depth" ),
|
||||
&PAD::SetBackPostMachiningDepth, &PAD::GetBackPostMachiningDepth, PROPERTY_DISPLAY::PT_SIZE ), groupPostMachining )
|
||||
.SetWriteableFunc( padCanHaveHole )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetBackPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PAD, int>( _HKI( "Bottom Countersink Angle" ),
|
||||
&PAD::SetBackPostMachiningAngle, &PAD::GetBackPostMachiningAngle, PROPERTY_DISPLAY::PT_DECIDEGREE ), groupPostMachining )
|
||||
.SetWriteableFunc( padCanHaveHole )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetBackPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PAD, BACKDRILL_MODE>( _HKI( "Backdrill Mode" ),
|
||||
&PAD::SetBackdrillMode, &PAD::GetBackdrillMode ), groupBackdrill );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>( _HKI( "Bottom Backdrill Size" ),
|
||||
&PAD::SetBottomBackdrillSize, &PAD::GetBottomBackdrillSize, PROPERTY_DISPLAY::PT_SIZE ), groupBackdrill )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetBackdrillMode();
|
||||
return mode == BACKDRILL_MODE::BACKDRILL_BOTTOM || mode == BACKDRILL_MODE::BACKDRILL_BOTH;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PAD, PCB_LAYER_ID>( _HKI( "Bottom Backdrill Layer" ),
|
||||
&PAD::SetBottomBackdrillLayer, &PAD::GetBottomBackdrillLayer ), groupBackdrill )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetBackdrillMode();
|
||||
return mode == BACKDRILL_MODE::BACKDRILL_BOTTOM || mode == BACKDRILL_MODE::BACKDRILL_BOTH;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PAD, std::optional<int>>( _HKI( "Top Backdrill Size" ),
|
||||
&PAD::SetTopBackdrillSize, &PAD::GetTopBackdrillSize, PROPERTY_DISPLAY::PT_SIZE ), groupBackdrill )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetBackdrillMode();
|
||||
return mode == BACKDRILL_MODE::BACKDRILL_TOP || mode == BACKDRILL_MODE::BACKDRILL_BOTH;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PAD, PCB_LAYER_ID>( _HKI( "Top Backdrill Layer" ),
|
||||
&PAD::SetTopBackdrillLayer, &PAD::GetTopBackdrillLayer ), groupBackdrill )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PAD* pad = dynamic_cast<PAD*>( aItem ) )
|
||||
{
|
||||
if( pad->GetDrillShape() != PAD_DRILL_SHAPE::CIRCLE )
|
||||
return false;
|
||||
|
||||
auto mode = pad->GetBackdrillMode();
|
||||
return mode == BACKDRILL_MODE::BACKDRILL_TOP || mode == BACKDRILL_MODE::BACKDRILL_BOTH;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PAD, PAD_PROP>( _HKI( "Fabrication Property" ),
|
||||
&PAD::SetProperty, &PAD::GetProperty ), groupPad );
|
||||
|
||||
|
||||
+141
-7
@@ -26,6 +26,7 @@
|
||||
|
||||
#include <mutex>
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include <board_connected_item.h>
|
||||
#include <core/arraydim.h>
|
||||
@@ -301,12 +302,19 @@ public:
|
||||
return m_padStack.TrapezoidDeltaSize( aLayer );
|
||||
}
|
||||
|
||||
void SetDrillSize( const VECTOR2I& aSize ) { m_padStack.Drill().size = aSize; SetDirty(); }
|
||||
const VECTOR2I& GetDrillSize() const { return m_padStack.Drill().size; }
|
||||
void SetPrimaryDrillSize( const VECTOR2I& aSize );
|
||||
const VECTOR2I& GetPrimaryDrillSize() const { return m_padStack.Drill().size; }
|
||||
void SetPrimaryDrillSizeX( int aX );
|
||||
int GetPrimaryDrillSizeX() const { return m_padStack.Drill().size.x; }
|
||||
void SetPrimaryDrillSizeY( int aY );
|
||||
int GetPrimaryDrillSizeY() const { return m_padStack.Drill().size.y; }
|
||||
|
||||
void SetDrillSize( const VECTOR2I& aSize ) { SetPrimaryDrillSize( aSize ); }
|
||||
const VECTOR2I& GetDrillSize() const { return GetPrimaryDrillSize(); }
|
||||
void SetDrillSizeX( int aX );
|
||||
int GetDrillSizeX() const { return m_padStack.Drill().size.x; }
|
||||
void SetDrillSizeY( int aY ) { m_padStack.Drill().size.y = aY; SetDirty(); }
|
||||
int GetDrillSizeY() const { return m_padStack.Drill().size.y; }
|
||||
int GetDrillSizeX() const { return GetPrimaryDrillSizeX(); }
|
||||
void SetDrillSizeY( int aY );
|
||||
int GetDrillSizeY() const { return GetPrimaryDrillSizeY(); }
|
||||
|
||||
void SetOffset( PCB_LAYER_ID aLayer, const VECTOR2I& aOffset )
|
||||
{
|
||||
@@ -418,8 +426,119 @@ public:
|
||||
return m_padStack.GetOrientation().AsDegrees();
|
||||
}
|
||||
|
||||
void SetDrillShape( PAD_DRILL_SHAPE aShape );
|
||||
PAD_DRILL_SHAPE GetDrillShape() const { return m_padStack.Drill().shape; }
|
||||
void SetPrimaryDrillShape( PAD_DRILL_SHAPE aShape );
|
||||
PAD_DRILL_SHAPE GetPrimaryDrillShape() const { return m_padStack.Drill().shape; }
|
||||
|
||||
void SetDrillShape( PAD_DRILL_SHAPE aShape ) { SetPrimaryDrillShape( aShape ); }
|
||||
PAD_DRILL_SHAPE GetDrillShape() const { return GetPrimaryDrillShape(); }
|
||||
|
||||
void SetPrimaryDrillStartLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetPrimaryDrillStartLayer() const { return m_padStack.Drill().start; }
|
||||
void SetPrimaryDrillEndLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetPrimaryDrillEndLayer() const { return m_padStack.Drill().end; }
|
||||
|
||||
void SetFrontPostMachining( const std::optional<PAD_DRILL_POST_MACHINING_MODE>& aMode ) { m_padStack.FrontPostMachining().mode = aMode; }
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> GetFrontPostMachining() const { return m_padStack.FrontPostMachining().mode; }
|
||||
|
||||
void SetFrontPostMachiningMode( PAD_DRILL_POST_MACHINING_MODE aMode )
|
||||
{
|
||||
m_padStack.FrontPostMachining().mode = aMode;
|
||||
}
|
||||
|
||||
PAD_DRILL_POST_MACHINING_MODE GetFrontPostMachiningMode() const
|
||||
{
|
||||
return m_padStack.FrontPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
|
||||
}
|
||||
|
||||
void SetFrontPostMachiningSize( int aSize ) { m_padStack.FrontPostMachining().size = aSize; }
|
||||
int GetFrontPostMachiningSize() const { return m_padStack.FrontPostMachining().size; }
|
||||
void SetFrontPostMachiningDepth( int aDepth ) { m_padStack.FrontPostMachining().depth = aDepth; }
|
||||
int GetFrontPostMachiningDepth() const { return m_padStack.FrontPostMachining().depth; }
|
||||
void SetFrontPostMachiningAngle( int aAngle ) { m_padStack.FrontPostMachining().angle = aAngle; }
|
||||
int GetFrontPostMachiningAngle() const { return m_padStack.FrontPostMachining().angle; }
|
||||
|
||||
void SetBackPostMachining( const std::optional<PAD_DRILL_POST_MACHINING_MODE>& aMode ) { m_padStack.BackPostMachining().mode = aMode; }
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> GetBackPostMachining() const { return m_padStack.BackPostMachining().mode; }
|
||||
|
||||
void SetBackPostMachiningMode( PAD_DRILL_POST_MACHINING_MODE aMode )
|
||||
{
|
||||
m_padStack.BackPostMachining().mode = aMode;
|
||||
}
|
||||
|
||||
PAD_DRILL_POST_MACHINING_MODE GetBackPostMachiningMode() const
|
||||
{
|
||||
return m_padStack.BackPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
|
||||
}
|
||||
|
||||
void SetBackPostMachiningSize( int aSize ) { m_padStack.BackPostMachining().size = aSize; }
|
||||
int GetBackPostMachiningSize() const { return m_padStack.BackPostMachining().size; }
|
||||
void SetBackPostMachiningDepth( int aDepth ) { m_padStack.BackPostMachining().depth = aDepth; }
|
||||
int GetBackPostMachiningDepth() const { return m_padStack.BackPostMachining().depth; }
|
||||
void SetBackPostMachiningAngle( int aAngle ) { m_padStack.BackPostMachining().angle = aAngle; }
|
||||
int GetBackPostMachiningAngle() const { return m_padStack.BackPostMachining().angle; }
|
||||
|
||||
/**
|
||||
* Check if a layer is affected by backdrilling or post-machining operations.
|
||||
*
|
||||
* This checks both the secondary drill (backdrill) and post-machining (counterbore/countersink)
|
||||
* settings to determine if the given layer has had copper removed.
|
||||
*
|
||||
* @param aLayer the copper layer to check
|
||||
* @return true if the layer is affected by backdrilling or post-machining
|
||||
*/
|
||||
bool IsBackdrilledOrPostMachined( PCB_LAYER_ID aLayer ) const;
|
||||
|
||||
/**
|
||||
* Get the knockout diameter for a layer affected by post-machining.
|
||||
*
|
||||
* @param aLayer the copper layer to check
|
||||
* @return the diameter to knockout on this layer, or 0 if layer is not affected
|
||||
*/
|
||||
int GetPostMachiningKnockout( PCB_LAYER_ID aLayer ) const;
|
||||
|
||||
void SetPrimaryDrillFilled( const std::optional<bool>& aFilled );
|
||||
void SetPrimaryDrillFilledFlag( bool aFilled );
|
||||
std::optional<bool> GetPrimaryDrillFilled() const { return m_padStack.Drill().is_filled; }
|
||||
bool GetPrimaryDrillFilledFlag() const { return m_padStack.Drill().is_filled.value_or( false ); }
|
||||
|
||||
void SetPrimaryDrillCapped( const std::optional<bool>& aCapped );
|
||||
void SetPrimaryDrillCappedFlag( bool aCapped );
|
||||
std::optional<bool> GetPrimaryDrillCapped() const { return m_padStack.Drill().is_capped; }
|
||||
bool GetPrimaryDrillCappedFlag() const { return m_padStack.Drill().is_capped.value_or( false ); }
|
||||
|
||||
void SetSecondaryDrillSize( const VECTOR2I& aSize );
|
||||
const VECTOR2I& GetSecondaryDrillSize() const { return m_padStack.SecondaryDrill().size; }
|
||||
void ClearSecondaryDrillSize();
|
||||
|
||||
void SetSecondaryDrillSizeX( int aX );
|
||||
int GetSecondaryDrillSizeX() const { return m_padStack.SecondaryDrill().size.x; }
|
||||
void SetSecondaryDrillSizeY( int aY );
|
||||
int GetSecondaryDrillSizeY() const { return m_padStack.SecondaryDrill().size.y; }
|
||||
|
||||
void SetSecondaryDrillShape( PAD_DRILL_SHAPE aShape );
|
||||
PAD_DRILL_SHAPE GetSecondaryDrillShape() const { return m_padStack.SecondaryDrill().shape; }
|
||||
|
||||
void SetSecondaryDrillStartLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetSecondaryDrillStartLayer() const { return m_padStack.SecondaryDrill().start; }
|
||||
void SetSecondaryDrillEndLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetSecondaryDrillEndLayer() const { return m_padStack.SecondaryDrill().end; }
|
||||
|
||||
void SetTertiaryDrillSize( const VECTOR2I& aSize );
|
||||
const VECTOR2I& GetTertiaryDrillSize() const { return m_padStack.TertiaryDrill().size; }
|
||||
void ClearTertiaryDrillSize();
|
||||
|
||||
void SetTertiaryDrillSizeX( int aX );
|
||||
int GetTertiaryDrillSizeX() const { return m_padStack.TertiaryDrill().size.x; }
|
||||
void SetTertiaryDrillSizeY( int aY );
|
||||
int GetTertiaryDrillSizeY() const { return m_padStack.TertiaryDrill().size.y; }
|
||||
|
||||
void SetTertiaryDrillShape( PAD_DRILL_SHAPE aShape );
|
||||
PAD_DRILL_SHAPE GetTertiaryDrillShape() const { return m_padStack.TertiaryDrill().shape; }
|
||||
|
||||
void SetTertiaryDrillStartLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetTertiaryDrillStartLayer() const { return m_padStack.TertiaryDrill().start; }
|
||||
void SetTertiaryDrillEndLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetTertiaryDrillEndLayer() const { return m_padStack.TertiaryDrill().end; }
|
||||
|
||||
bool IsDirty() const
|
||||
{
|
||||
@@ -915,6 +1034,21 @@ public:
|
||||
void CheckPad( UNITS_PROVIDER* aUnitsProvider, bool aForPadProperties,
|
||||
const std::function<void( int aErrorCode, const wxString& aMsg )>& aErrorHandler ) const;
|
||||
|
||||
BACKDRILL_MODE GetBackdrillMode() const { return m_padStack.GetBackdrillMode(); }
|
||||
void SetBackdrillMode( BACKDRILL_MODE aMode ) { m_padStack.SetBackdrillMode( aMode ); }
|
||||
|
||||
std::optional<int> GetBottomBackdrillSize() const { return m_padStack.GetBackdrillSize( false ); }
|
||||
void SetBottomBackdrillSize( std::optional<int> aSize ) { m_padStack.SetBackdrillSize( false, aSize ); }
|
||||
|
||||
PCB_LAYER_ID GetBottomBackdrillLayer() const { return m_padStack.GetBackdrillEndLayer( false ); }
|
||||
void SetBottomBackdrillLayer( PCB_LAYER_ID aLayer ) { m_padStack.SetBackdrillEndLayer( false, aLayer ); }
|
||||
|
||||
std::optional<int> GetTopBackdrillSize() const { return m_padStack.GetBackdrillSize( true ); }
|
||||
void SetTopBackdrillSize( std::optional<int> aSize ) { m_padStack.SetBackdrillSize( true, aSize ); }
|
||||
|
||||
PCB_LAYER_ID GetTopBackdrillLayer() const { return m_padStack.GetBackdrillEndLayer( true ); }
|
||||
void SetTopBackdrillLayer( PCB_LAYER_ID aLayer ) { m_padStack.SetBackdrillEndLayer( true, aLayer ); }
|
||||
|
||||
double Similarity( const BOARD_ITEM& aOther ) const override;
|
||||
|
||||
bool operator==( const PAD& aOther ) const;
|
||||
|
||||
+735
-674
File diff suppressed because it is too large
Load Diff
+53
-1
@@ -72,6 +72,22 @@ enum class PAD_DRILL_SHAPE
|
||||
OBLONG,
|
||||
};
|
||||
|
||||
enum class PAD_DRILL_POST_MACHINING_MODE
|
||||
{
|
||||
UNKNOWN,
|
||||
NOT_POST_MACHINED,
|
||||
COUNTERBORE,
|
||||
COUNTERSINK
|
||||
};
|
||||
|
||||
enum class BACKDRILL_MODE
|
||||
{
|
||||
NO_BACKDRILL,
|
||||
BACKDRILL_BOTTOM,
|
||||
BACKDRILL_TOP,
|
||||
BACKDRILL_BOTH
|
||||
};
|
||||
|
||||
/**
|
||||
* The set of pad shapes, used with PAD::{Set,Get}Attribute().
|
||||
*
|
||||
@@ -253,6 +269,16 @@ public:
|
||||
bool operator==( const DRILL_PROPS& aOther ) const;
|
||||
};
|
||||
|
||||
struct POST_MACHINING_PROPS
|
||||
{
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> mode;
|
||||
int size = 0;
|
||||
int depth = 0;
|
||||
int angle = 0;
|
||||
|
||||
bool operator==( const POST_MACHINING_PROPS& aOther ) const;
|
||||
};
|
||||
|
||||
public:
|
||||
PADSTACK( BOARD_ITEM* aParent );
|
||||
virtual ~PADSTACK() = default;
|
||||
@@ -311,6 +337,15 @@ public:
|
||||
DRILL_PROPS& SecondaryDrill() { return m_secondaryDrill; }
|
||||
const DRILL_PROPS& SecondaryDrill() const { return m_secondaryDrill; }
|
||||
|
||||
DRILL_PROPS& TertiaryDrill() { return m_tertiaryDrill; }
|
||||
const DRILL_PROPS& TertiaryDrill() const { return m_tertiaryDrill; }
|
||||
|
||||
POST_MACHINING_PROPS& FrontPostMachining() { return m_frontPostMachining; }
|
||||
const POST_MACHINING_PROPS& FrontPostMachining() const { return m_frontPostMachining; }
|
||||
|
||||
POST_MACHINING_PROPS& BackPostMachining() { return m_backPostMachining; }
|
||||
const POST_MACHINING_PROPS& BackPostMachining() const { return m_backPostMachining; }
|
||||
|
||||
UNCONNECTED_LAYER_MODE UnconnectedLayerMode() const { return m_unconnectedLayerMode; }
|
||||
void SetUnconnectedLayerMode( UNCONNECTED_LAYER_MODE aMode ) { m_unconnectedLayerMode = aMode; }
|
||||
|
||||
@@ -458,6 +493,15 @@ public:
|
||||
|
||||
void ClearPrimitives( PCB_LAYER_ID aLayer );
|
||||
|
||||
BACKDRILL_MODE GetBackdrillMode() const;
|
||||
void SetBackdrillMode( BACKDRILL_MODE aMode );
|
||||
|
||||
std::optional<int> GetBackdrillSize( bool aTop ) const;
|
||||
void SetBackdrillSize( bool aTop, std::optional<int> aSize );
|
||||
|
||||
PCB_LAYER_ID GetBackdrillEndLayer( bool aTop ) const;
|
||||
void SetBackdrillEndLayer( bool aTop, PCB_LAYER_ID aLayer );
|
||||
|
||||
private:
|
||||
void packCopperLayer( PCB_LAYER_ID aLayer, kiapi::board::types::PadStack& aProto ) const;
|
||||
|
||||
@@ -501,12 +545,20 @@ private:
|
||||
///! vias and pads (F_Cu to B_Cu for normal holes; a subset of layers for blind/buried vias)
|
||||
DRILL_PROPS m_drill;
|
||||
|
||||
///! Secondary drill, used to define back-drilling
|
||||
///! Secondary drill, used to define back-drilling starting from the bottom side
|
||||
DRILL_PROPS m_secondaryDrill;
|
||||
|
||||
///! Tertiary drill, used to define back-drilling starting from the top side
|
||||
DRILL_PROPS m_tertiaryDrill;
|
||||
|
||||
POST_MACHINING_PROPS m_frontPostMachining;
|
||||
POST_MACHINING_PROPS m_backPostMachining;
|
||||
};
|
||||
|
||||
#ifndef SWIG
|
||||
DECLARE_ENUM_TO_WXANY( PAD_DRILL_POST_MACHINING_MODE );
|
||||
DECLARE_ENUM_TO_WXANY( PADSTACK::UNCONNECTED_LAYER_MODE );
|
||||
DECLARE_ENUM_TO_WXANY( BACKDRILL_MODE );
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -1394,6 +1394,8 @@ wxXmlNode* PCB_IO_IPC2581::generateEcadSection()
|
||||
generateStackup( cadDataNode );
|
||||
generateStepSection( cadDataNode );
|
||||
|
||||
pruneUnusedBackdrillSpecs();
|
||||
|
||||
return ecadNode;
|
||||
}
|
||||
|
||||
@@ -1498,6 +1500,8 @@ void PCB_IO_IPC2581::addCadHeader( wxXmlNode* aEcadNode )
|
||||
wxXmlNode* cadHeaderNode = appendNode( aEcadNode, "CadHeader" );
|
||||
addAttribute( cadHeaderNode, "units", m_units_str );
|
||||
|
||||
m_cad_header_node = cadHeaderNode;
|
||||
|
||||
generateCadSpecs( cadHeaderNode );
|
||||
}
|
||||
|
||||
@@ -1984,6 +1988,7 @@ void PCB_IO_IPC2581::addPadStack( wxXmlNode* aPadNode, const PAD* aPad )
|
||||
|
||||
wxXmlNode* padStackDefNode = new wxXmlNode( wxXML_ELEMENT_NODE, "PadStackDef" );
|
||||
addAttribute( padStackDefNode, "name", name );
|
||||
ensureBackdrillSpecs( name, aPad->Padstack() );
|
||||
m_padstacks.push_back( padStackDefNode );
|
||||
|
||||
if( m_last_padstack )
|
||||
@@ -2056,6 +2061,7 @@ void PCB_IO_IPC2581::addPadStack( wxXmlNode* aContentNode, const PCB_VIA* aVia )
|
||||
insertNodeAfter( m_last_padstack, padStackDefNode );
|
||||
m_last_padstack = padStackDefNode;
|
||||
addAttribute( padStackDefNode, "name", name );
|
||||
ensureBackdrillSpecs( name, aVia->Padstack() );
|
||||
|
||||
wxXmlNode* padStackHoleNode = appendNode( padStackDefNode, "PadstackHoleDef" );
|
||||
addAttribute( padStackHoleNode, "name", wxString::Format( "PH%d", aVia->GetDrillValue() ) );
|
||||
@@ -2115,6 +2121,143 @@ void PCB_IO_IPC2581::addPadStack( wxXmlNode* aContentNode, const PCB_VIA* aVia )
|
||||
}
|
||||
|
||||
|
||||
void PCB_IO_IPC2581::ensureBackdrillSpecs( const wxString& aPadstackName, const PADSTACK& aPadstack )
|
||||
{
|
||||
if( m_padstack_backdrill_specs.find( aPadstackName ) != m_padstack_backdrill_specs.end() )
|
||||
return;
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secondary = aPadstack.SecondaryDrill();
|
||||
|
||||
if( secondary.start == UNDEFINED_LAYER || secondary.end == UNDEFINED_LAYER )
|
||||
return;
|
||||
|
||||
if( secondary.size.x <= 0 && secondary.size.y <= 0 )
|
||||
return;
|
||||
|
||||
if( !m_cad_header_node )
|
||||
return;
|
||||
|
||||
auto layerHasRef = [&]( PCB_LAYER_ID aLayer ) -> bool
|
||||
{
|
||||
return m_layer_name_map.find( aLayer ) != m_layer_name_map.end();
|
||||
};
|
||||
|
||||
if( !layerHasRef( secondary.start ) || !layerHasRef( secondary.end ) )
|
||||
return;
|
||||
|
||||
BOARD_DESIGN_SETTINGS& dsnSettings = m_board->GetDesignSettings();
|
||||
BOARD_STACKUP& stackup = dsnSettings.GetStackupDescriptor();
|
||||
stackup.SynchronizeWithBoard( &dsnSettings );
|
||||
|
||||
auto createSpec = [&]( const PADSTACK::DRILL_PROPS& aDrill, const wxString& aSpecName ) -> wxString
|
||||
{
|
||||
if( aDrill.start == UNDEFINED_LAYER || aDrill.end == UNDEFINED_LAYER )
|
||||
return wxString();
|
||||
|
||||
auto startLayer = m_layer_name_map.find( aDrill.start );
|
||||
auto endLayer = m_layer_name_map.find( aDrill.end );
|
||||
|
||||
if( startLayer == m_layer_name_map.end() || endLayer == m_layer_name_map.end() )
|
||||
return wxString();
|
||||
|
||||
wxXmlNode* specNode = appendNode( m_cad_header_node, "Spec" );
|
||||
addAttribute( specNode, "name", aSpecName );
|
||||
|
||||
wxXmlNode* backdrillNode = appendNode( specNode, "Backdrill" );
|
||||
addAttribute( backdrillNode, "startLayerRef", startLayer->second );
|
||||
addAttribute( backdrillNode, "mustNotCutLayerRef", endLayer->second );
|
||||
|
||||
int stubLength = stackup.GetLayerDistance( aDrill.start, aDrill.end );
|
||||
|
||||
if( stubLength < 0 )
|
||||
stubLength = 0;
|
||||
|
||||
addAttribute( backdrillNode, "maxStubLength", floatVal( m_scale * stubLength ) );
|
||||
|
||||
PAD_DRILL_POST_MACHINING_MODE pm_mode = PAD_DRILL_POST_MACHINING_MODE::UNKNOWN;
|
||||
|
||||
if( aDrill.start == F_Cu )
|
||||
pm_mode = aPadstack.FrontPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
|
||||
else if( aDrill.start == B_Cu )
|
||||
pm_mode = aPadstack.BackPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::UNKNOWN );
|
||||
|
||||
bool isPostMachined = ( pm_mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ||
|
||||
pm_mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK );
|
||||
|
||||
addAttribute( backdrillNode, "postMachining", isPostMachined ? wxT( "true" )
|
||||
: wxT( "false" ) );
|
||||
|
||||
m_backdrill_spec_nodes[aSpecName] = specNode;
|
||||
|
||||
return aSpecName;
|
||||
};
|
||||
|
||||
int specIndex = m_backdrill_spec_index + 1;
|
||||
|
||||
const PADSTACK::DRILL_PROPS& primary = aPadstack.Drill();
|
||||
wxString primarySpec = createSpec( primary, wxString::Format( wxT( "BD_%dA" ), specIndex ) );
|
||||
|
||||
wxString secondarySpec = createSpec( secondary, wxString::Format( wxT( "BD_%dB" ), specIndex ) );
|
||||
|
||||
if( primarySpec.IsEmpty() && secondarySpec.IsEmpty() )
|
||||
return;
|
||||
|
||||
m_backdrill_spec_index = specIndex;
|
||||
m_padstack_backdrill_specs.emplace( aPadstackName, std::make_pair( primarySpec, secondarySpec ) );
|
||||
}
|
||||
|
||||
|
||||
void PCB_IO_IPC2581::addBackdrillSpecRefs( wxXmlNode* aHoleNode, const wxString& aPadstackName )
|
||||
{
|
||||
auto it = m_padstack_backdrill_specs.find( aPadstackName );
|
||||
|
||||
if( it == m_padstack_backdrill_specs.end() )
|
||||
return;
|
||||
|
||||
auto addRef = [&]( const wxString& aSpecName )
|
||||
{
|
||||
if( aSpecName.IsEmpty() )
|
||||
return;
|
||||
|
||||
wxXmlNode* specRefNode = appendNode( aHoleNode, "SpecRef" );
|
||||
addAttribute( specRefNode, "id", aSpecName );
|
||||
m_backdrill_spec_used.insert( aSpecName );
|
||||
};
|
||||
|
||||
addRef( it->second.first );
|
||||
addRef( it->second.second );
|
||||
}
|
||||
|
||||
|
||||
void PCB_IO_IPC2581::pruneUnusedBackdrillSpecs()
|
||||
{
|
||||
if( !m_cad_header_node )
|
||||
return;
|
||||
|
||||
auto it = m_backdrill_spec_nodes.begin();
|
||||
|
||||
while( it != m_backdrill_spec_nodes.end() )
|
||||
{
|
||||
if( m_backdrill_spec_used.find( it->first ) == m_backdrill_spec_used.end() )
|
||||
{
|
||||
wxXmlNode* specNode = it->second;
|
||||
|
||||
if( specNode )
|
||||
{
|
||||
m_cad_header_node->RemoveChild( specNode );
|
||||
delete specNode;
|
||||
}
|
||||
|
||||
it = m_backdrill_spec_nodes.erase( it );
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool PCB_IO_IPC2581::addPolygonNode( wxXmlNode* aParentNode,
|
||||
const SHAPE_LINE_CHAIN& aPolygon, FILL_T aFillType,
|
||||
int aWidth, LINE_STYLE aDashType )
|
||||
@@ -2870,6 +3013,7 @@ void PCB_IO_IPC2581::generateLayerSetDrill( wxXmlNode* aLayerNode )
|
||||
addAttribute( holeNode, "plusTol", "0.0" );
|
||||
addAttribute( holeNode, "minusTol", "0.0" );
|
||||
addXY( holeNode, via->GetPosition() );
|
||||
addBackdrillSpecRefs( holeNode, it->second );
|
||||
}
|
||||
else if( item->Type() == PCB_PAD_T )
|
||||
{
|
||||
@@ -2897,6 +3041,7 @@ void PCB_IO_IPC2581::generateLayerSetDrill( wxXmlNode* aLayerNode )
|
||||
addAttribute( holeNode, "plusTol", "0.0" );
|
||||
addAttribute( holeNode, "minusTol", "0.0" );
|
||||
addXY( holeNode, pad->GetPosition() );
|
||||
addBackdrillSpecRefs( holeNode, it->second );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3405,6 +3550,12 @@ void PCB_IO_IPC2581::SaveBoard( const wxString& aFileName, BOARD* aBoard,
|
||||
const std::map<std::string, UTF8>* aProperties )
|
||||
{
|
||||
m_board = aBoard;
|
||||
m_padstack_backdrill_specs.clear();
|
||||
m_backdrill_spec_nodes.clear();
|
||||
m_backdrill_spec_used.clear();
|
||||
m_backdrill_spec_index = 0;
|
||||
m_cad_header_node = nullptr;
|
||||
m_layer_name_map.clear();
|
||||
m_units_str = "MILLIMETER";
|
||||
m_scale = 1.0 / PCB_IU_PER_MM;
|
||||
m_sigfig = 6;
|
||||
|
||||
@@ -44,6 +44,7 @@ class FOOTPRINT;
|
||||
class PROGRESS_REPORTER;
|
||||
class NETINFO_ITEM;
|
||||
class PAD;
|
||||
class PADSTACK;
|
||||
class PCB_SHAPE;
|
||||
class PCB_VIA;
|
||||
class PCB_TEXT;
|
||||
@@ -67,6 +68,8 @@ public:
|
||||
m_shape_std_node = nullptr;
|
||||
m_line_node = nullptr;
|
||||
m_last_padstack = nullptr;
|
||||
m_backdrill_spec_index = 0;
|
||||
m_cad_header_node = nullptr;
|
||||
m_progress_reporter = nullptr;
|
||||
m_xml_doc = nullptr;
|
||||
m_xml_root = nullptr;
|
||||
@@ -210,6 +213,12 @@ private:
|
||||
|
||||
void addPadStack( wxXmlNode* aContentNode, const PCB_VIA* aVia );
|
||||
|
||||
void ensureBackdrillSpecs( const wxString& aPadstackName, const PADSTACK& aPadstack );
|
||||
|
||||
void addBackdrillSpecRefs( wxXmlNode* aHoleNode, const wxString& aPadstackName );
|
||||
|
||||
void pruneUnusedBackdrillSpecs();
|
||||
|
||||
void addLocationNode( wxXmlNode* aContentNode, double aX, double aY );
|
||||
|
||||
void addLocationNode( wxXmlNode* aContentNode, const PAD& aPad, bool aRelative );
|
||||
@@ -310,6 +319,12 @@ private:
|
||||
std::vector<wxXmlNode*> m_padstacks; //<! Holding vector for padstacks. These need to be inserted prior to the components
|
||||
wxXmlNode* m_last_padstack; //<! Pointer to padstack list where we can insert the VIA padstacks once we process tracks
|
||||
|
||||
std::map<wxString, std::pair<wxString, wxString>> m_padstack_backdrill_specs;
|
||||
std::map<wxString, wxXmlNode*> m_backdrill_spec_nodes;
|
||||
std::set<wxString> m_backdrill_spec_used;
|
||||
int m_backdrill_spec_index;
|
||||
wxXmlNode* m_cad_header_node;
|
||||
|
||||
std::map<size_t, wxString>
|
||||
m_footprint_dict; //<! Map between the footprint hash values and reference id string (<fpid>_##)
|
||||
|
||||
|
||||
@@ -1641,6 +1641,45 @@ void PCB_IO_KICAD_SEXPR::format( const PAD* aPad ) const
|
||||
m_out->Print( ")" );
|
||||
}
|
||||
|
||||
if( aPad->Padstack().SecondaryDrill().size.x > 0 )
|
||||
{
|
||||
m_out->Print( "(backdrill (size %s) (layers %s %s))",
|
||||
formatInternalUnits( aPad->Padstack().SecondaryDrill().size.x ).c_str(),
|
||||
m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().start ) ).c_str(),
|
||||
m_out->Quotew( LSET::Name( aPad->Padstack().SecondaryDrill().end ) ).c_str() );
|
||||
}
|
||||
|
||||
if( aPad->Padstack().TertiaryDrill().size.x > 0 )
|
||||
{
|
||||
m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
|
||||
formatInternalUnits( aPad->Padstack().TertiaryDrill().size.x ).c_str(),
|
||||
m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().start ) ).c_str(),
|
||||
m_out->Quotew( LSET::Name( aPad->Padstack().TertiaryDrill().end ) ).c_str() );
|
||||
}
|
||||
|
||||
auto formatPostMachining = [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
|
||||
{
|
||||
if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
|
||||
return;
|
||||
|
||||
m_out->Print( "(%s %s", aName,
|
||||
aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore" : "countersink" );
|
||||
|
||||
if( aProps.size > 0 )
|
||||
m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
|
||||
|
||||
if( aProps.depth > 0 )
|
||||
m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
|
||||
|
||||
if( aProps.angle > 0 )
|
||||
m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
|
||||
|
||||
m_out->Print( ")" );
|
||||
};
|
||||
|
||||
formatPostMachining( "front_post_machining", aPad->Padstack().FrontPostMachining() );
|
||||
formatPostMachining( "back_post_machining", aPad->Padstack().BackPostMachining() );
|
||||
|
||||
// Add pad property, if exists.
|
||||
if( property )
|
||||
m_out->Print( "(property %s)", property );
|
||||
@@ -2474,6 +2513,45 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_TRACK* aTrack ) const
|
||||
else
|
||||
m_out->Print( "(drill %s)", formatInternalUnits( via->GetDrillValue() ).c_str() );
|
||||
|
||||
if( via->Padstack().SecondaryDrill().size.x > 0 )
|
||||
{
|
||||
m_out->Print( "(backdrill (size %s) (layers %s %s))",
|
||||
formatInternalUnits( via->Padstack().SecondaryDrill().size.x ).c_str(),
|
||||
m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().start ) ).c_str(),
|
||||
m_out->Quotew( LSET::Name( via->Padstack().SecondaryDrill().end ) ).c_str() );
|
||||
}
|
||||
|
||||
if( via->Padstack().TertiaryDrill().size.x > 0 )
|
||||
{
|
||||
m_out->Print( "(tertiary_drill (size %s) (layers %s %s))",
|
||||
formatInternalUnits( via->Padstack().TertiaryDrill().size.x ).c_str(),
|
||||
m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().start ) ).c_str(),
|
||||
m_out->Quotew( LSET::Name( via->Padstack().TertiaryDrill().end ) ).c_str() );
|
||||
}
|
||||
|
||||
auto formatPostMachining = [&]( const char* aName, const PADSTACK::POST_MACHINING_PROPS& aProps )
|
||||
{
|
||||
if( !aProps.mode.has_value() || aProps.mode == PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED )
|
||||
return;
|
||||
|
||||
m_out->Print( "(%s %s", aName,
|
||||
aProps.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE ? "counterbore" : "countersink" );
|
||||
|
||||
if( aProps.size > 0 )
|
||||
m_out->Print( " (size %s)", formatInternalUnits( aProps.size ).c_str() );
|
||||
|
||||
if( aProps.depth > 0 )
|
||||
m_out->Print( " (depth %s)", formatInternalUnits( aProps.depth ).c_str() );
|
||||
|
||||
if( aProps.angle > 0 )
|
||||
m_out->Print( " (angle %s)", FormatDouble2Str( aProps.angle / 10.0 ).c_str() );
|
||||
|
||||
m_out->Print( ")" );
|
||||
};
|
||||
|
||||
formatPostMachining( "front_post_machining", via->Padstack().FrontPostMachining() );
|
||||
formatPostMachining( "back_post_machining", via->Padstack().BackPostMachining() );
|
||||
|
||||
m_out->Print( "(layers %s %s)",
|
||||
m_out->Quotew( LSET::Name( layer1 ) ).c_str(),
|
||||
m_out->Quotew( LSET::Name( layer2 ) ).c_str() );
|
||||
|
||||
@@ -196,7 +196,8 @@ class PCB_IO_KICAD_SEXPR; // forward decl
|
||||
//#define SEXPR_BOARD_FILE_VERSION 20250914 // Add support for PCB_BARCODE objects
|
||||
//#define SEXPR_BOARD_FILE_VERSION 20250926 // Split via types into blind/buried/through
|
||||
//#define SEXPR_BOARD_FILE_VERSION 20251027 // Store pad-to-die delays with correct scaling
|
||||
#define SEXPR_BOARD_FILE_VERSION 20251028 // Stop writing netcodes; they're an internal implementation detail
|
||||
//#define SEXPR_BOARD_FILE_VERSION 20251028 // Stop writing netcodes; they're an internal implementation detail
|
||||
#define SEXPR_BOARD_FILE_VERSION 20251101 // Backdrill and tertiary drill support
|
||||
|
||||
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
|
||||
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting
|
||||
|
||||
@@ -1224,19 +1224,18 @@ BOARD* PCB_IO_KICAD_SEXPR_PARSER::parseBOARD_unchecked()
|
||||
// Make sure the destination layer is enabled, even if not in the file
|
||||
m_board->SetEnabledLayers( LSET( m_board->GetEnabledLayers() ).set( destLayer ) );
|
||||
|
||||
const auto visitItem =
|
||||
[&]( BOARD_ITEM& curr_item )
|
||||
{
|
||||
LSET layers = curr_item.GetLayerSet();
|
||||
const auto visitItem = [&]( BOARD_ITEM& curr_item )
|
||||
{
|
||||
LSET layers = curr_item.GetLayerSet();
|
||||
|
||||
if( layers.test( Rescue ) )
|
||||
{
|
||||
layers.set( destLayer );
|
||||
layers.reset( Rescue );
|
||||
}
|
||||
if( layers.test( Rescue ) )
|
||||
{
|
||||
layers.set( destLayer );
|
||||
layers.reset( Rescue );
|
||||
}
|
||||
|
||||
curr_item.SetLayerSet( layers );
|
||||
};
|
||||
curr_item.SetLayerSet( layers );
|
||||
};
|
||||
|
||||
for( PCB_TRACK* track : m_board->Tracks() )
|
||||
{
|
||||
@@ -2594,7 +2593,7 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseSetup()
|
||||
break;
|
||||
}
|
||||
case T_zone_defaults:
|
||||
parseZoneDefaults( bds.m_ZoneLayerProperties );
|
||||
parseZoneDefaults( bds.GetDefaultZoneSettings() );
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -2613,23 +2612,24 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseSetup()
|
||||
}
|
||||
|
||||
|
||||
void PCB_IO_KICAD_SEXPR_PARSER::parseZoneDefaults( std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES>& aProperties )
|
||||
void PCB_IO_KICAD_SEXPR_PARSER::parseZoneDefaults( ZONE_SETTINGS& aZoneSettings )
|
||||
{
|
||||
T token;
|
||||
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
{
|
||||
if( token != T_LEFT )
|
||||
{
|
||||
Expecting( T_LEFT );
|
||||
}
|
||||
|
||||
token = NextTok();
|
||||
|
||||
switch( token )
|
||||
{
|
||||
case T_property:
|
||||
parseZoneLayerProperty( aProperties );
|
||||
parseZoneLayerProperty( aZoneSettings.m_LayerProperties );
|
||||
break;
|
||||
|
||||
default:
|
||||
Unexpected( CurText() );
|
||||
}
|
||||
@@ -2637,7 +2637,8 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseZoneDefaults( std::map<PCB_LAYER_ID, ZONE_L
|
||||
}
|
||||
|
||||
|
||||
void PCB_IO_KICAD_SEXPR_PARSER::parseZoneLayerProperty( std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES>& aProperties )
|
||||
void PCB_IO_KICAD_SEXPR_PARSER::parseZoneLayerProperty(
|
||||
std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES>& aProperties )
|
||||
{
|
||||
T token;
|
||||
|
||||
@@ -2647,7 +2648,9 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseZoneLayerProperty( std::map<PCB_LAYER_ID, Z
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
{
|
||||
if( token != T_LEFT )
|
||||
{
|
||||
Expecting( T_LEFT );
|
||||
}
|
||||
|
||||
token = NextTok();
|
||||
|
||||
@@ -2657,14 +2660,12 @@ void PCB_IO_KICAD_SEXPR_PARSER::parseZoneLayerProperty( std::map<PCB_LAYER_ID, Z
|
||||
layer = parseBoardItemLayer();
|
||||
NeedRIGHT();
|
||||
break;
|
||||
|
||||
case T_hatch_position:
|
||||
{
|
||||
properties.hatching_offset = parseXY();
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Unexpected( CurText() );
|
||||
break;
|
||||
@@ -5649,6 +5650,86 @@ PAD* PCB_IO_KICAD_SEXPR_PARSER::parsePAD( FOOTPRINT* aParent )
|
||||
break;
|
||||
}
|
||||
|
||||
case T_backdrill:
|
||||
{
|
||||
// Parse: (backdrill (size ...) (layers start end))
|
||||
PADSTACK::DRILL_PROPS& secondary = pad->Padstack().SecondaryDrill();
|
||||
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
{
|
||||
if( token != T_LEFT )
|
||||
Expecting( T_LEFT );
|
||||
|
||||
token = NextTok();
|
||||
|
||||
switch( token )
|
||||
{
|
||||
case T_size:
|
||||
{
|
||||
int size = parseBoardUnits( "backdrill size" );
|
||||
secondary.size = VECTOR2I( size, size );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
|
||||
case T_layers:
|
||||
{
|
||||
NextTok();
|
||||
secondary.start = lookUpLayer( m_layerIndices );
|
||||
NextTok();
|
||||
secondary.end = lookUpLayer( m_layerIndices );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Expecting( "size or layers" );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case T_tertiary_drill:
|
||||
{
|
||||
// Parse: (tertiary_drill (size ...) (layers start end))
|
||||
PADSTACK::DRILL_PROPS& tertiary = pad->Padstack().TertiaryDrill();
|
||||
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
{
|
||||
if( token != T_LEFT )
|
||||
Expecting( T_LEFT );
|
||||
|
||||
token = NextTok();
|
||||
|
||||
switch( token )
|
||||
{
|
||||
case T_size:
|
||||
{
|
||||
int size = parseBoardUnits( "tertiary drill size" );
|
||||
tertiary.size = VECTOR2I( size, size );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
|
||||
case T_layers:
|
||||
{
|
||||
NextTok();
|
||||
tertiary.start = lookUpLayer( m_layerIndices );
|
||||
NextTok();
|
||||
tertiary.end = lookUpLayer( m_layerIndices );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Expecting( "size or layers" );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case T_layers:
|
||||
{
|
||||
LSET layerMask = parseBoardItemLayersAsMask();
|
||||
@@ -6011,12 +6092,20 @@ PAD* PCB_IO_KICAD_SEXPR_PARSER::parsePAD( FOOTPRINT* aParent )
|
||||
NeedRIGHT();
|
||||
break;
|
||||
|
||||
case T_front_post_machining:
|
||||
parsePostMachining( pad->Padstack().FrontPostMachining() );
|
||||
break;
|
||||
|
||||
case T_back_post_machining:
|
||||
parsePostMachining( pad->Padstack().BackPostMachining() );
|
||||
break;
|
||||
|
||||
default:
|
||||
Expecting( "at, locked, drill, layers, net, die_length, roundrect_rratio, "
|
||||
"solder_mask_margin, solder_paste_margin, solder_paste_margin_ratio, uuid, "
|
||||
"clearance, tstamp, primitives, remove_unused_layers, keep_end_layers, "
|
||||
"pinfunction, pintype, zone_connect, thermal_width, thermal_gap, padstack or "
|
||||
"teardrops" );
|
||||
"pinfunction, pintype, zone_connect, thermal_width, thermal_gap, padstack, "
|
||||
"teardrops, front_post_machining, or back_post_machining" );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6145,6 +6234,59 @@ bool PCB_IO_KICAD_SEXPR_PARSER::parsePAD_option( PAD* aPad )
|
||||
}
|
||||
|
||||
|
||||
void PCB_IO_KICAD_SEXPR_PARSER::parsePostMachining( PADSTACK::POST_MACHINING_PROPS& aProps )
|
||||
{
|
||||
// Parse: (front_post_machining counterbore (size ...) (depth ...) (angle ...))
|
||||
// or: (back_post_machining countersink (size ...) (depth ...) (angle ...))
|
||||
// The mode token (counterbore/countersink) comes first
|
||||
T token = NextTok();
|
||||
|
||||
switch( token )
|
||||
{
|
||||
case T_counterbore:
|
||||
aProps.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE;
|
||||
break;
|
||||
|
||||
case T_countersink:
|
||||
aProps.mode = PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK;
|
||||
break;
|
||||
|
||||
default:
|
||||
Expecting( "counterbore or countersink" );
|
||||
}
|
||||
|
||||
// Parse optional properties
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
{
|
||||
if( token != T_LEFT )
|
||||
Expecting( T_LEFT );
|
||||
|
||||
token = NextTok();
|
||||
|
||||
switch( token )
|
||||
{
|
||||
case T_size:
|
||||
aProps.size = parseBoardUnits( "post machining size" );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
|
||||
case T_depth:
|
||||
aProps.depth = parseBoardUnits( "post machining depth" );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
|
||||
case T_angle:
|
||||
aProps.angle = KiROUND( parseDouble( "post machining angle" ) * 10.0 );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
|
||||
default:
|
||||
Expecting( "size, depth, or angle" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PCB_IO_KICAD_SEXPR_PARSER::parsePadstack( PAD* aPad )
|
||||
{
|
||||
PADSTACK& padstack = aPad->Padstack();
|
||||
@@ -7136,8 +7278,98 @@ PCB_VIA* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_VIA()
|
||||
via->SetIsFree( parseMaybeAbsentBool( true ) );
|
||||
break;
|
||||
|
||||
case T_backdrill:
|
||||
{
|
||||
// Parse: (backdrill (size ...) (layers start end))
|
||||
PADSTACK::DRILL_PROPS& secondary = via->Padstack().SecondaryDrill();
|
||||
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
{
|
||||
if( token != T_LEFT )
|
||||
Expecting( T_LEFT );
|
||||
|
||||
token = NextTok();
|
||||
|
||||
switch( token )
|
||||
{
|
||||
case T_size:
|
||||
{
|
||||
int size = parseBoardUnits( "backdrill size" );
|
||||
secondary.size = VECTOR2I( size, size );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
|
||||
case T_layers:
|
||||
{
|
||||
NextTok();
|
||||
secondary.start = lookUpLayer( m_layerIndices );
|
||||
NextTok();
|
||||
secondary.end = lookUpLayer( m_layerIndices );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Expecting( "size or layers" );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case T_tertiary_drill:
|
||||
{
|
||||
// Parse: (tertiary_drill (size ...) (layers start end))
|
||||
PADSTACK::DRILL_PROPS& tertiary = via->Padstack().TertiaryDrill();
|
||||
|
||||
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
|
||||
{
|
||||
if( token != T_LEFT )
|
||||
Expecting( T_LEFT );
|
||||
|
||||
token = NextTok();
|
||||
|
||||
switch( token )
|
||||
{
|
||||
case T_size:
|
||||
{
|
||||
int size = parseBoardUnits( "tertiary drill size" );
|
||||
tertiary.size = VECTOR2I( size, size );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
|
||||
case T_layers:
|
||||
{
|
||||
NextTok();
|
||||
tertiary.start = lookUpLayer( m_layerIndices );
|
||||
NextTok();
|
||||
tertiary.end = lookUpLayer( m_layerIndices );
|
||||
NeedRIGHT();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Expecting( "size or layers" );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case T_front_post_machining:
|
||||
parsePostMachining( via->Padstack().FrontPostMachining() );
|
||||
break;
|
||||
|
||||
case T_back_post_machining:
|
||||
parsePostMachining( via->Padstack().BackPostMachining() );
|
||||
break;
|
||||
|
||||
default:
|
||||
Expecting( "blind, micro, at, size, drill, layers, net, free, tstamp, uuid, status or teardrops" );
|
||||
Expecting( "blind, micro, at, size, drill, layers, net, free, tstamp, uuid, status, "
|
||||
"teardrops, backdrill, tertiary_drill, front_post_machining, or "
|
||||
"back_post_machining" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <kiid.h>
|
||||
#include <math/box2.h>
|
||||
#include <string_any_map.h>
|
||||
#include <padstack.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
@@ -48,6 +49,7 @@
|
||||
class PCB_ARC;
|
||||
class BOARD;
|
||||
class BOARD_ITEM;
|
||||
class ZONE_SETTINGS;
|
||||
class BOARD_CONNECTED_ITEM;
|
||||
class BOARD_ITEM_CONTAINER;
|
||||
class PAD;
|
||||
@@ -240,6 +242,8 @@ private:
|
||||
|
||||
// Parse only the (option ...) inside a pad description
|
||||
bool parsePAD_option( PAD* aPad );
|
||||
void parsePostMachining( PADSTACK::POST_MACHINING_PROPS& aProps );
|
||||
|
||||
void parsePadstack( PAD* aPad );
|
||||
|
||||
PCB_ARC* parseARC();
|
||||
@@ -312,7 +316,7 @@ private:
|
||||
|
||||
void parseMargins( int& aLeft, int& aTop, int& aRight, int& aBottom );
|
||||
|
||||
void parseZoneDefaults( std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES>& aProperties );
|
||||
void parseZoneDefaults( ZONE_SETTINGS& aZoneSettings );
|
||||
|
||||
void parseZoneLayerProperty( std::map<PCB_LAYER_ID, ZONE_LAYER_PROPERTIES>& aProperties );
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
|
||||
#include <base_units.h>
|
||||
#include <optional>
|
||||
#include <board_stackup_manager/stackup_predefined_prms.h>
|
||||
#include <build_version.h>
|
||||
#include <callback_gal.h>
|
||||
@@ -30,6 +31,7 @@
|
||||
#include <footprint.h>
|
||||
#include <hash_eda.h>
|
||||
#include <pad.h>
|
||||
#include <padstack.h>
|
||||
#include <pcb_dimension.h>
|
||||
#include <pcb_shape.h>
|
||||
#include <pcb_text.h>
|
||||
@@ -287,12 +289,17 @@ void ODB_MATRIX_ENTITY::AddMatrixLayerField( MATRIX_LAYER& aMLayer, PCB_LAYER_ID
|
||||
|
||||
void ODB_MATRIX_ENTITY::AddDrillMatrixLayer()
|
||||
{
|
||||
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& drill_layers =
|
||||
std::map<ODB_DRILL_SPAN, std::vector<BOARD_ITEM*>>& drill_layers =
|
||||
m_plugin->GetDrillLayerItemsMap();
|
||||
|
||||
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
|
||||
m_plugin->GetSlotHolesMap();
|
||||
|
||||
drill_layers.clear();
|
||||
|
||||
std::map<ODB_DRILL_SPAN, wxString>& span_names = m_plugin->GetDrillSpanNameMap();
|
||||
span_names.clear();
|
||||
|
||||
bool has_pth_layer = false;
|
||||
bool has_npth_layer = false;
|
||||
|
||||
@@ -301,14 +308,24 @@ void ODB_MATRIX_ENTITY::AddDrillMatrixLayer()
|
||||
if( item->Type() == PCB_VIA_T )
|
||||
{
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( item );
|
||||
drill_layers[std::make_pair( via->TopLayer(), via->BottomLayer() )].push_back( via );
|
||||
has_pth_layer = true;
|
||||
|
||||
ODB_DRILL_SPAN platedSpan( via->TopLayer(), via->BottomLayer(), false, false );
|
||||
drill_layers[platedSpan].push_back( via );
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secondary = via->Padstack().SecondaryDrill();
|
||||
|
||||
if( secondary.start != UNDEFINED_LAYER && secondary.end != UNDEFINED_LAYER
|
||||
&& ( secondary.size.x > 0 || secondary.size.y > 0 ) )
|
||||
{
|
||||
ODB_DRILL_SPAN backSpan( secondary.start, secondary.end, true, true );
|
||||
drill_layers[backSpan].push_back( via );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( FOOTPRINT* fp : m_board->Footprints() )
|
||||
{
|
||||
// std::shared_ptr<FOOTPRINT> fp( static_cast<FOOTPRINT*>( it_fp->Clone() ) );
|
||||
|
||||
if( fp->IsFlipped() )
|
||||
{
|
||||
m_hasBotComp = true;
|
||||
@@ -316,50 +333,85 @@ void ODB_MATRIX_ENTITY::AddDrillMatrixLayer()
|
||||
|
||||
for( PAD* pad : fp->Pads() )
|
||||
{
|
||||
if( !has_pth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
|
||||
if( pad->GetAttribute() == PAD_ATTRIB::PTH )
|
||||
has_pth_layer = true;
|
||||
if( !has_npth_layer && pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
||||
if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
|
||||
has_npth_layer = true;
|
||||
|
||||
if( pad->HasHole() && pad->GetDrillSizeX() != pad->GetDrillSizeY() )
|
||||
slot_holes[std::make_pair( F_Cu, B_Cu )].push_back( pad );
|
||||
else if( pad->HasHole() )
|
||||
drill_layers[std::make_pair( F_Cu, B_Cu )].push_back( pad );
|
||||
{
|
||||
ODB_DRILL_SPAN padSpan( F_Cu, B_Cu, false, pad->GetAttribute() == PAD_ATTRIB::NPTH );
|
||||
drill_layers[padSpan].push_back( pad );
|
||||
}
|
||||
}
|
||||
|
||||
// m_plugin->GetLoadedFootprintList().push_back( std::move( fp ) );
|
||||
}
|
||||
|
||||
auto InitDrillMatrix =
|
||||
[&]( const wxString& aHasPlated, std::pair<PCB_LAYER_ID, PCB_LAYER_ID> aLayerPair )
|
||||
if( has_npth_layer )
|
||||
{
|
||||
wxString dLayerName = wxString::Format( "drill_%s_%s-%s", aHasPlated,
|
||||
m_board->GetLayerName( aLayerPair.first ),
|
||||
m_board->GetLayerName( aLayerPair.second ) );
|
||||
ODB_DRILL_SPAN npthSpan( F_Cu, B_Cu, false, true );
|
||||
drill_layers[npthSpan];
|
||||
}
|
||||
|
||||
if( has_pth_layer )
|
||||
{
|
||||
ODB_DRILL_SPAN platedSpan( F_Cu, B_Cu, false, false );
|
||||
drill_layers[platedSpan];
|
||||
}
|
||||
|
||||
int backdrillIndex = 1;
|
||||
|
||||
auto assignName = [&]( const ODB_DRILL_SPAN& aSpan )
|
||||
{
|
||||
auto it = span_names.find( aSpan );
|
||||
|
||||
if( it != span_names.end() )
|
||||
return it->second;
|
||||
|
||||
wxString name;
|
||||
|
||||
if( aSpan.m_IsBackdrill )
|
||||
{
|
||||
name.Printf( wxT( "drill%d" ), backdrillIndex++ );
|
||||
}
|
||||
else
|
||||
{
|
||||
wxString platedLabel = aSpan.m_IsNonPlated ? wxT( "non-plated" ) : wxT( "plated" );
|
||||
name.Printf( wxT( "drill_%s_%s-%s" ), platedLabel,
|
||||
m_board->GetLayerName( aSpan.TopLayer() ),
|
||||
m_board->GetLayerName( aSpan.BottomLayer() ) );
|
||||
}
|
||||
|
||||
wxString legalName = ODB::GenLegalEntityName( name );
|
||||
span_names[aSpan] = legalName;
|
||||
|
||||
return legalName;
|
||||
};
|
||||
|
||||
auto InitDrillMatrix = [&]( const ODB_DRILL_SPAN& aSpan )
|
||||
{
|
||||
wxString dLayerName = assignName( aSpan );
|
||||
MATRIX_LAYER matrix( m_row++, dLayerName );
|
||||
|
||||
matrix.m_type = ODB_TYPE::DRILL;
|
||||
matrix.m_context = ODB_CONTEXT::BOARD;
|
||||
matrix.m_polarity = ODB_POLARITY::POSITIVE;
|
||||
matrix.m_span.emplace( std::make_pair(
|
||||
ODB::GenLegalEntityName( m_board->GetLayerName( aLayerPair.first ) ),
|
||||
ODB::GenLegalEntityName( m_board->GetLayerName( aLayerPair.second ) ) ) );
|
||||
ODB::GenLegalEntityName( m_board->GetLayerName( aSpan.m_StartLayer ) ),
|
||||
ODB::GenLegalEntityName( m_board->GetLayerName( aSpan.m_EndLayer ) ) ) );
|
||||
|
||||
if( aSpan.m_IsBackdrill )
|
||||
matrix.m_addType.emplace( ODB_SUBTYPE::BACKDRILL );
|
||||
|
||||
m_matrixLayers.push_back( matrix );
|
||||
m_plugin->GetLayerNameList().emplace_back(
|
||||
std::make_pair( PCB_LAYER_ID::UNDEFINED_LAYER, matrix.m_layerName ) );
|
||||
};
|
||||
|
||||
if( has_npth_layer )
|
||||
InitDrillMatrix( "non-plated", std::make_pair( F_Cu, B_Cu ) );
|
||||
// at least one non plated hole is present.
|
||||
|
||||
if( has_pth_layer && drill_layers.find( std::make_pair( F_Cu, B_Cu ) ) == drill_layers.end() )
|
||||
InitDrillMatrix( "plated", std::make_pair( F_Cu, B_Cu ) );
|
||||
// there is no circular plated dril hole present.
|
||||
|
||||
for( const auto& [layer_pair, vec] : drill_layers )
|
||||
for( const auto& entry : drill_layers )
|
||||
{
|
||||
InitDrillMatrix( "plated", layer_pair );
|
||||
InitDrillMatrix( entry.first );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -686,12 +738,14 @@ ODB_COMPONENT& ODB_LAYER_ENTITY::InitComponentData( const FOOTPRINT* aFp
|
||||
|
||||
void ODB_LAYER_ENTITY::InitDrillData()
|
||||
{
|
||||
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& drill_layers =
|
||||
std::map<ODB_DRILL_SPAN, std::vector<BOARD_ITEM*>>& drill_layers =
|
||||
m_plugin->GetDrillLayerItemsMap();
|
||||
|
||||
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>& slot_holes =
|
||||
m_plugin->GetSlotHolesMap();
|
||||
|
||||
std::map<ODB_DRILL_SPAN, wxString>& span_names = m_plugin->GetDrillSpanNameMap();
|
||||
|
||||
if( !m_layerItems.empty() )
|
||||
{
|
||||
m_layerItems.clear();
|
||||
@@ -699,28 +753,72 @@ void ODB_LAYER_ENTITY::InitDrillData()
|
||||
|
||||
m_tools.emplace( PCB_IO_ODBPP::m_unitsStr );
|
||||
|
||||
bool is_npth_layer = false;
|
||||
wxString plated_name = "plated";
|
||||
std::optional<ODB_DRILL_SPAN> matchedSpan;
|
||||
|
||||
if( m_matrixLayerName.Contains( "non-plated" ) )
|
||||
for( const auto& [span, name] : span_names )
|
||||
{
|
||||
is_npth_layer = true;
|
||||
plated_name = "non-plated";
|
||||
if( name == m_matrixLayerName )
|
||||
{
|
||||
matchedSpan = span;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool useLegacyMatching = !matchedSpan.has_value();
|
||||
bool isBackdrillLayer = matchedSpan.has_value() && matchedSpan->m_IsBackdrill;
|
||||
bool isNonPlatedLayer = matchedSpan.has_value() && matchedSpan->m_IsNonPlated;
|
||||
bool isNPTHLayer = matchedSpan.has_value() && matchedSpan->m_IsNonPlated
|
||||
&& !matchedSpan->m_IsBackdrill;
|
||||
|
||||
for( const auto& [layer_pair, vec] : slot_holes )
|
||||
if( matchedSpan.has_value() && isNPTHLayer )
|
||||
{
|
||||
wxString dLayerName = wxString::Format( "drill_%s_%s-%s", plated_name,
|
||||
m_board->GetLayerName( layer_pair.first ),
|
||||
m_board->GetLayerName( layer_pair.second ) );
|
||||
auto slotIt = slot_holes.find( matchedSpan->Pair() );
|
||||
|
||||
if( ODB::GenLegalEntityName( dLayerName ) == m_matrixLayerName )
|
||||
if( slotIt != slot_holes.end() )
|
||||
{
|
||||
for( BOARD_ITEM* item : vec )
|
||||
for( BOARD_ITEM* item : slotIt->second )
|
||||
{
|
||||
if( item->Type() == PCB_PAD_T )
|
||||
if( item->Type() != PCB_PAD_T )
|
||||
continue;
|
||||
|
||||
PAD* pad = static_cast<PAD*>( item );
|
||||
|
||||
if( pad->GetAttribute() == PAD_ATTRIB::PTH )
|
||||
continue;
|
||||
|
||||
m_tools.value().AddDrillTools( wxT( "NON_PLATED" ),
|
||||
ODB::SymDouble2String(
|
||||
std::min( pad->GetDrillSizeX(),
|
||||
pad->GetDrillSizeY() ) ) );
|
||||
|
||||
m_layerItems[pad->GetNetCode()].push_back( item );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( useLegacyMatching )
|
||||
{
|
||||
bool is_npth_layer = false;
|
||||
wxString plated_name = wxT( "plated" );
|
||||
|
||||
if( m_matrixLayerName.Contains( wxT( "non-plated" ) ) )
|
||||
{
|
||||
is_npth_layer = true;
|
||||
plated_name = wxT( "non-plated" );
|
||||
}
|
||||
|
||||
for( const auto& [layer_pair, vec] : slot_holes )
|
||||
{
|
||||
wxString dLayerName = wxString::Format( wxT( "drill_%s_%s-%s" ), plated_name,
|
||||
m_board->GetLayerName( layer_pair.first ),
|
||||
m_board->GetLayerName( layer_pair.second ) );
|
||||
|
||||
if( ODB::GenLegalEntityName( dLayerName ) == m_matrixLayerName )
|
||||
{
|
||||
for( BOARD_ITEM* item : vec )
|
||||
{
|
||||
if( item->Type() != PCB_PAD_T )
|
||||
continue;
|
||||
|
||||
PAD* pad = static_cast<PAD*>( item );
|
||||
|
||||
if( ( is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
|
||||
@@ -730,59 +828,143 @@ void ODB_LAYER_ENTITY::InitDrillData()
|
||||
}
|
||||
|
||||
m_tools.value().AddDrillTools(
|
||||
pad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NON_PLATED",
|
||||
pad->GetAttribute() == PAD_ATTRIB::PTH ? wxT( "PLATED" )
|
||||
: wxT( "NON_PLATED" ),
|
||||
ODB::SymDouble2String(
|
||||
std::min( pad->GetDrillSizeX(), pad->GetDrillSizeY() ) ) );
|
||||
|
||||
// for drill features
|
||||
m_layerItems[pad->GetNetCode()].push_back( item );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( const auto& [layer_pair, vec] : drill_layers )
|
||||
if( matchedSpan.has_value() )
|
||||
{
|
||||
wxString dLayerName = wxString::Format( "drill_%s_%s-%s", plated_name,
|
||||
m_board->GetLayerName( layer_pair.first ),
|
||||
m_board->GetLayerName( layer_pair.second ) );
|
||||
auto drillIt = drill_layers.find( *matchedSpan );
|
||||
|
||||
if( ODB::GenLegalEntityName( dLayerName ) == m_matrixLayerName )
|
||||
if( drillIt != drill_layers.end() )
|
||||
{
|
||||
for( BOARD_ITEM* item : vec )
|
||||
for( BOARD_ITEM* item : drillIt->second )
|
||||
{
|
||||
if( item->Type() == PCB_VIA_T && !is_npth_layer )
|
||||
if( item->Type() == PCB_VIA_T )
|
||||
{
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( item );
|
||||
|
||||
m_tools.value().AddDrillTools( "VIA",
|
||||
ODB::SymDouble2String( via->GetDrillValue() ) );
|
||||
if( isBackdrillLayer )
|
||||
{
|
||||
const PADSTACK::DRILL_PROPS& secondary = via->Padstack().SecondaryDrill();
|
||||
|
||||
int diameter = secondary.size.x;
|
||||
|
||||
if( secondary.size.y > 0 )
|
||||
{
|
||||
diameter = ( diameter > 0 ) ? std::min( diameter, secondary.size.y )
|
||||
: secondary.size.y;
|
||||
}
|
||||
|
||||
if( diameter <= 0 )
|
||||
continue;
|
||||
|
||||
m_tools.value().AddDrillTools( wxT( "NON_PLATED" ),
|
||||
ODB::SymDouble2String( diameter ),
|
||||
wxT( "BLIND" ) );
|
||||
}
|
||||
else if( isNonPlatedLayer )
|
||||
{
|
||||
m_tools.value().AddDrillTools( wxT( "NON_PLATED" ),
|
||||
ODB::SymDouble2String( via->GetDrillValue() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tools.value().AddDrillTools( wxT( "VIA" ),
|
||||
ODB::SymDouble2String( via->GetDrillValue() ) );
|
||||
}
|
||||
|
||||
// for drill features
|
||||
m_layerItems[via->GetNetCode()].push_back( item );
|
||||
}
|
||||
else if( item->Type() == PCB_PAD_T )
|
||||
{
|
||||
PAD* pad = static_cast<PAD*>( item );
|
||||
|
||||
if( ( is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
|
||||
|| ( !is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
|
||||
{
|
||||
bool padIsNPTH = pad->GetAttribute() == PAD_ATTRIB::NPTH;
|
||||
|
||||
if( isNPTHLayer && !padIsNPTH )
|
||||
continue;
|
||||
}
|
||||
|
||||
m_tools.value().AddDrillTools(
|
||||
pad->GetAttribute() == PAD_ATTRIB::PTH ? "PLATED" : "NON_PLATED",
|
||||
ODB::SymDouble2String( pad->GetDrillSizeX() ) );
|
||||
if( !isNonPlatedLayer && padIsNPTH )
|
||||
continue;
|
||||
|
||||
int drillSize = pad->GetDrillSizeX();
|
||||
|
||||
if( pad->GetDrillSizeX() != pad->GetDrillSizeY() )
|
||||
drillSize = std::min( pad->GetDrillSizeX(), pad->GetDrillSizeY() );
|
||||
|
||||
wxString typeLabel = ( padIsNPTH || isNonPlatedLayer ) ? wxT( "NON_PLATED" )
|
||||
: wxT( "PLATED" );
|
||||
wxString type2 = isBackdrillLayer ? wxT( "BLIND" ) : wxT( "STANDARD" );
|
||||
|
||||
m_tools.value().AddDrillTools( typeLabel, ODB::SymDouble2String( drillSize ),
|
||||
type2 );
|
||||
|
||||
// for drill features
|
||||
m_layerItems[pad->GetNetCode()].push_back( item );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool is_npth_layer = false;
|
||||
wxString plated_name = wxT( "plated" );
|
||||
|
||||
break;
|
||||
if( m_matrixLayerName.Contains( wxT( "non-plated" ) ) )
|
||||
{
|
||||
is_npth_layer = true;
|
||||
plated_name = wxT( "non-plated" );
|
||||
}
|
||||
|
||||
for( const auto& [span, vec] : drill_layers )
|
||||
{
|
||||
wxString dLayerName = wxString::Format( wxT( "drill_%s_%s-%s" ), plated_name,
|
||||
m_board->GetLayerName( span.TopLayer() ),
|
||||
m_board->GetLayerName( span.BottomLayer() ) );
|
||||
|
||||
if( ODB::GenLegalEntityName( dLayerName ) == m_matrixLayerName )
|
||||
{
|
||||
for( BOARD_ITEM* item : vec )
|
||||
{
|
||||
if( item->Type() == PCB_VIA_T && !is_npth_layer )
|
||||
{
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( item );
|
||||
|
||||
m_tools.value().AddDrillTools( wxT( "VIA" ),
|
||||
ODB::SymDouble2String( via->GetDrillValue() ) );
|
||||
|
||||
m_layerItems[via->GetNetCode()].push_back( item );
|
||||
}
|
||||
else if( item->Type() == PCB_PAD_T )
|
||||
{
|
||||
PAD* pad = static_cast<PAD*>( item );
|
||||
|
||||
if( ( is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::PTH )
|
||||
|| ( !is_npth_layer && pad->GetAttribute() == PAD_ATTRIB::NPTH ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m_tools.value().AddDrillTools(
|
||||
pad->GetAttribute() == PAD_ATTRIB::PTH ? wxT( "PLATED" )
|
||||
: wxT( "NON_PLATED" ),
|
||||
ODB::SymDouble2String( pad->GetDrillSizeX() ) );
|
||||
|
||||
m_layerItems[pad->GetNetCode()].push_back( item );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ enum class ODB_SUBTYPE
|
||||
PSA,
|
||||
SILVER_MASK,
|
||||
CARBON_MASK,
|
||||
BACKDRILL,
|
||||
};
|
||||
|
||||
enum class ODB_FID_TYPE
|
||||
@@ -156,6 +157,7 @@ public:
|
||||
result[ODB_SUBTYPE::PSA] = "PSA";
|
||||
result[ODB_SUBTYPE::SILVER_MASK] = "SILVER_MASK";
|
||||
result[ODB_SUBTYPE::CARBON_MASK] = "CARBON_MASK";
|
||||
result[ODB_SUBTYPE::BACKDRILL] = "BACKDRILL";
|
||||
}
|
||||
|
||||
if constexpr( std::is_same_v<T, ODB_DIELECTRIC_TYPE> )
|
||||
@@ -344,11 +346,13 @@ public:
|
||||
ODB_DRILL_TOOLS( const wxString& aUnits, const wxString& aThickness = "0",
|
||||
const wxString& aUserParams = wxEmptyString );
|
||||
|
||||
void AddDrillTools( const wxString& aType, const wxString& aFinishSize )
|
||||
void AddDrillTools( const wxString& aType, const wxString& aFinishSize,
|
||||
const wxString& aType2 = wxT( "STANDARD" ) )
|
||||
{
|
||||
TOOLS tool;
|
||||
tool.m_num = m_tools.size() + 1;
|
||||
tool.m_type = aType;
|
||||
tool.m_type2 = aType2;
|
||||
tool.m_finishSize = aFinishSize;
|
||||
tool.m_drillSize = aFinishSize;
|
||||
|
||||
|
||||
@@ -34,6 +34,66 @@
|
||||
#include <memory>
|
||||
#include "odb_entity.h"
|
||||
|
||||
struct ODB_DRILL_SPAN
|
||||
{
|
||||
ODB_DRILL_SPAN()
|
||||
{
|
||||
m_StartLayer = F_Cu;
|
||||
m_EndLayer = B_Cu;
|
||||
m_IsBackdrill = false;
|
||||
m_IsNonPlated = false;
|
||||
}
|
||||
|
||||
ODB_DRILL_SPAN( PCB_LAYER_ID aStartLayer, PCB_LAYER_ID aEndLayer, bool aIsBackdrill,
|
||||
bool aIsNonPlated )
|
||||
{
|
||||
m_StartLayer = aStartLayer;
|
||||
m_EndLayer = aEndLayer;
|
||||
m_IsBackdrill = aIsBackdrill;
|
||||
m_IsNonPlated = aIsNonPlated;
|
||||
}
|
||||
|
||||
PCB_LAYER_ID TopLayer() const
|
||||
{
|
||||
return m_StartLayer < m_EndLayer ? m_StartLayer : m_EndLayer;
|
||||
}
|
||||
|
||||
PCB_LAYER_ID BottomLayer() const
|
||||
{
|
||||
return m_StartLayer < m_EndLayer ? m_EndLayer : m_StartLayer;
|
||||
}
|
||||
|
||||
std::pair<PCB_LAYER_ID, PCB_LAYER_ID> Pair() const
|
||||
{
|
||||
return std::make_pair( TopLayer(), BottomLayer() );
|
||||
}
|
||||
|
||||
bool operator<( const ODB_DRILL_SPAN& aOther ) const
|
||||
{
|
||||
if( TopLayer() != aOther.TopLayer() )
|
||||
return TopLayer() < aOther.TopLayer();
|
||||
|
||||
if( BottomLayer() != aOther.BottomLayer() )
|
||||
return BottomLayer() < aOther.BottomLayer();
|
||||
|
||||
if( m_IsBackdrill != aOther.m_IsBackdrill )
|
||||
return m_IsBackdrill && !aOther.m_IsBackdrill;
|
||||
|
||||
if( m_IsNonPlated != aOther.m_IsNonPlated )
|
||||
return m_IsNonPlated && !aOther.m_IsNonPlated;
|
||||
|
||||
if( m_StartLayer != aOther.m_StartLayer )
|
||||
return m_StartLayer < aOther.m_StartLayer;
|
||||
|
||||
return m_EndLayer < aOther.m_EndLayer;
|
||||
}
|
||||
|
||||
PCB_LAYER_ID m_StartLayer;
|
||||
PCB_LAYER_ID m_EndLayer;
|
||||
bool m_IsBackdrill;
|
||||
bool m_IsNonPlated;
|
||||
};
|
||||
|
||||
class BOARD;
|
||||
class BOARD_ITEM;
|
||||
class EDA_TEXT;
|
||||
@@ -100,12 +160,16 @@ public:
|
||||
return m_loaded_footprints;
|
||||
}
|
||||
|
||||
inline std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>&
|
||||
GetDrillLayerItemsMap()
|
||||
inline std::map<ODB_DRILL_SPAN, std::vector<BOARD_ITEM*>>& GetDrillLayerItemsMap()
|
||||
{
|
||||
return m_drill_layers;
|
||||
}
|
||||
|
||||
inline std::map<ODB_DRILL_SPAN, wxString>& GetDrillSpanNameMap()
|
||||
{
|
||||
return m_drill_span_names;
|
||||
}
|
||||
|
||||
inline std::map<std::tuple<ODB_AUX_LAYER_TYPE, PCB_LAYER_ID, PCB_LAYER_ID>,
|
||||
std::vector<BOARD_ITEM*>>&
|
||||
GetAuxilliaryLayerItemsMap()
|
||||
@@ -169,8 +233,9 @@ private:
|
||||
std::vector<std::pair<PCB_LAYER_ID, wxString>>
|
||||
m_layer_name_list; //<! layer name in matrix entity to the internal layer id
|
||||
|
||||
std::map<std::pair<PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>
|
||||
std::map<ODB_DRILL_SPAN, std::vector<BOARD_ITEM*>>
|
||||
m_drill_layers; //<! Drill sets are output as layers (to/from pairs)
|
||||
std::map<ODB_DRILL_SPAN, wxString> m_drill_span_names;
|
||||
|
||||
std::map<std::tuple<ODB_AUX_LAYER_TYPE, PCB_LAYER_ID, PCB_LAYER_ID>, std::vector<BOARD_ITEM*>>
|
||||
m_auxilliary_layers; //<! Auxilliary layers, from/to pairs or simple (depending on type)
|
||||
|
||||
+128
-3
@@ -70,6 +70,8 @@
|
||||
#include <geometry/shape_segment.h>
|
||||
#include <geometry/shape_simple.h>
|
||||
#include <geometry/shape_circle.h>
|
||||
#include <geometry/shape_arc.h>
|
||||
#include <stroke_params.h>
|
||||
#include <bezier_curves.h>
|
||||
#include <kiface_base.h>
|
||||
#include <gr_text.h>
|
||||
@@ -1203,6 +1205,7 @@ void PCB_PAINTER::draw( const PCB_VIA* aVia, int aLayer )
|
||||
{
|
||||
m_gal->DrawCircle( center, radius );
|
||||
}
|
||||
|
||||
}
|
||||
else if( ( aLayer == F_Mask && aVia->IsOnLayer( F_Mask ) )
|
||||
|| ( aLayer == B_Mask && aVia->IsOnLayer( B_Mask ) ) )
|
||||
@@ -1245,13 +1248,40 @@ void PCB_PAINTER::draw( const PCB_VIA* aVia, int aLayer )
|
||||
|
||||
if( draw )
|
||||
m_gal->DrawCircle( center, radius );
|
||||
|
||||
// Draw backdrill indicators (semi-circles extending into the hole)
|
||||
// Drawn on copper layer so they appear above the annular ring
|
||||
if( !m_pcbSettings.IsPrinting() && draw )
|
||||
{
|
||||
std::optional<int> secDrill = aVia->GetSecondaryDrillSize();
|
||||
std::optional<int> terDrill = aVia->GetTertiaryDrillSize();
|
||||
|
||||
if( secDrill.value_or( 0 ) > 0 )
|
||||
{
|
||||
drawBackdrillIndicator( aVia, center, *secDrill,
|
||||
aVia->GetSecondaryDrillStartLayer(),
|
||||
aVia->GetSecondaryDrillEndLayer() );
|
||||
}
|
||||
|
||||
if( terDrill.value_or( 0 ) > 0 )
|
||||
{
|
||||
drawBackdrillIndicator( aVia, center, *terDrill,
|
||||
aVia->GetTertiaryDrillStartLayer(),
|
||||
aVia->GetTertiaryDrillEndLayer() );
|
||||
}
|
||||
}
|
||||
|
||||
// Draw post-machining indicator if this layer is post-machined
|
||||
if( !m_pcbSettings.IsPrinting() && draw )
|
||||
{
|
||||
drawPostMachiningIndicator( aVia, center, currentLayer );
|
||||
}
|
||||
}
|
||||
else if( aLayer == LAYER_LOCKED_ITEM_SHADOW ) // draw a ring around the via
|
||||
{
|
||||
m_gal->SetLineWidth( m_lockedShadowMargin );
|
||||
|
||||
m_gal->DrawCircle( center,
|
||||
( aVia->GetWidth( currentLayer ) + m_lockedShadowMargin ) / 2.0 );
|
||||
m_gal->DrawCircle( center, ( aVia->GetWidth( currentLayer ) + m_lockedShadowMargin ) / 2.0 );
|
||||
}
|
||||
|
||||
// Clearance lines
|
||||
@@ -1558,9 +1588,10 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
|
||||
if( aLayer == LAYER_PAD_PLATEDHOLES || aLayer == LAYER_NON_PLATEDHOLES )
|
||||
{
|
||||
SHAPE_SEGMENT slot = getPadHoleShape( aPad );
|
||||
VECTOR2I center = slot.GetSeg().A;
|
||||
|
||||
if( slot.GetSeg().A == slot.GetSeg().B ) // Circular hole
|
||||
m_gal->DrawCircle( slot.GetSeg().A, slot.GetWidth() / 2.0 );
|
||||
m_gal->DrawCircle( center, slot.GetWidth() / 2.0 );
|
||||
else
|
||||
m_gal->DrawSegment( slot.GetSeg().A, slot.GetSeg().B, slot.GetWidth() );
|
||||
}
|
||||
@@ -1815,6 +1846,33 @@ void PCB_PAINTER::draw( const PAD* aPad, int aLayer )
|
||||
aPad->TransformShapeToPolygon( polySet, ToLAYER_ID( aLayer ), margin.x, m_maxError, ERROR_INSIDE );
|
||||
m_gal->DrawPolygon( polySet );
|
||||
}
|
||||
|
||||
// Draw post-machining indicator if this layer is post-machined
|
||||
if( !m_pcbSettings.IsPrinting() && aPad->GetDrillSizeX() > 0 )
|
||||
{
|
||||
VECTOR2D holePos = aPad->GetPosition() + aPad->GetOffset( pcbLayer );
|
||||
|
||||
// Draw backdrill indicators (semi-circles extending into the hole)
|
||||
// Drawn on copper layer so they appear above the annular ring
|
||||
VECTOR2I secDrill = aPad->GetSecondaryDrillSize();
|
||||
VECTOR2I terDrill = aPad->GetTertiaryDrillSize();
|
||||
|
||||
if( secDrill.x > 0 )
|
||||
{
|
||||
drawBackdrillIndicator( aPad, holePos, secDrill.x,
|
||||
aPad->GetSecondaryDrillStartLayer(),
|
||||
aPad->GetSecondaryDrillEndLayer() );
|
||||
}
|
||||
|
||||
if( terDrill.x > 0 )
|
||||
{
|
||||
drawBackdrillIndicator( aPad, holePos, terDrill.x,
|
||||
aPad->GetTertiaryDrillStartLayer(),
|
||||
aPad->GetTertiaryDrillEndLayer() );
|
||||
}
|
||||
|
||||
drawPostMachiningIndicator( aPad, holePos, pcbLayer );
|
||||
}
|
||||
}
|
||||
|
||||
if( IsClearanceLayer( aLayer )
|
||||
@@ -3196,5 +3254,72 @@ void PCB_PAINTER::draw( const PCB_BOARD_OUTLINE* aBoardOutline, int aLayer )
|
||||
}
|
||||
|
||||
|
||||
void PCB_PAINTER::drawBackdrillIndicator( const BOARD_ITEM* aItem, const VECTOR2D& aCenter,
|
||||
int aDrillSize, PCB_LAYER_ID aStartLayer,
|
||||
PCB_LAYER_ID aEndLayer )
|
||||
{
|
||||
double backdrillRadius = aDrillSize / 2.0;
|
||||
double lineWidth = std::max( backdrillRadius / 4.0, m_pcbSettings.m_outlineWidth * 2.0 );
|
||||
|
||||
GAL_SCOPED_ATTRS scopedAttrs( *m_gal, GAL_SCOPED_ATTRS::ALL_ATTRS );
|
||||
m_gal->AdvanceDepth();
|
||||
m_gal->SetIsFill( false );
|
||||
m_gal->SetIsStroke( true );
|
||||
m_gal->SetLineWidth( lineWidth );
|
||||
|
||||
// Draw semi-circle in start layer color (top half, from 90° to 270°)
|
||||
m_gal->SetStrokeColor( m_pcbSettings.GetColor( aItem, aStartLayer ) );
|
||||
m_gal->DrawArc( aCenter, backdrillRadius, EDA_ANGLE( 90, DEGREES_T ),
|
||||
EDA_ANGLE( 180, DEGREES_T ) );
|
||||
|
||||
// Draw semi-circle in end layer color (bottom half, from 270° to 90°)
|
||||
m_gal->SetStrokeColor( m_pcbSettings.GetColor( aItem, aEndLayer ) );
|
||||
m_gal->DrawArc( aCenter, backdrillRadius, EDA_ANGLE( 270, DEGREES_T ),
|
||||
EDA_ANGLE( 180, DEGREES_T ) );
|
||||
}
|
||||
|
||||
|
||||
void PCB_PAINTER::drawPostMachiningIndicator( const BOARD_ITEM* aItem, const VECTOR2D& aCenter, PCB_LAYER_ID aLayer )
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
// Check to see if the pad or via has a post-machining operation on this layer
|
||||
if( const PAD* pad = dynamic_cast<const PAD*>( aItem ) )
|
||||
{
|
||||
size = pad->GetPostMachiningKnockout( aLayer );
|
||||
}
|
||||
else if( const PCB_VIA* via = dynamic_cast<const PCB_VIA*>( aItem ) )
|
||||
{
|
||||
size = via->GetPostMachiningKnockout( aLayer );
|
||||
}
|
||||
|
||||
if( size <= 0 )
|
||||
return;
|
||||
|
||||
GAL_SCOPED_ATTRS scopedAttrs( *m_gal, GAL_SCOPED_ATTRS::ALL_ATTRS );
|
||||
m_gal->AdvanceDepth();
|
||||
|
||||
double pmRadius = size / 2.0;
|
||||
// Use a line width proportional to the radius for visibility
|
||||
double lineWidth = std::max( pmRadius / 8.0, m_pcbSettings.m_outlineWidth * 2.0 );
|
||||
|
||||
COLOR4D layerColor = m_pcbSettings.GetColor( aItem, aLayer );
|
||||
|
||||
m_gal->SetIsFill( false );
|
||||
m_gal->SetIsStroke( true );
|
||||
m_gal->SetStrokeColor( layerColor );
|
||||
m_gal->SetLineWidth( lineWidth );
|
||||
|
||||
// Draw dashed circle manually with fixed number of segments for consistent appearance
|
||||
constexpr int NUM_DASHES = 12; // Number of dashes around the circle
|
||||
EDA_ANGLE dashAngle = ANGLE_360 / ( NUM_DASHES * 2 ); // Dash and gap are equal size
|
||||
|
||||
for( int i = 0; i < NUM_DASHES; ++i )
|
||||
{
|
||||
EDA_ANGLE startAngle = dashAngle * ( i * 2 );
|
||||
m_gal->DrawArc( aCenter, pmRadius, startAngle, dashAngle );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const double PCB_RENDER_SETTINGS::MAX_FONT_SIZE = pcbIUScale.mmToIU( 10.0 );
|
||||
|
||||
@@ -246,6 +246,28 @@ protected:
|
||||
|
||||
void renderNetNameForSegment( const SHAPE_SEGMENT& aSeg, const COLOR4D& aColor, const wxString& aNetName ) const;
|
||||
|
||||
/**
|
||||
* Draw backdrill indicator (two semi-circles) at the given center point.
|
||||
*
|
||||
* @param aItem the board item (for color lookup)
|
||||
* @param aCenter center point of the indicator
|
||||
* @param aDrillSize diameter of the backdrill
|
||||
* @param aStartLayer layer where backdrill starts
|
||||
* @param aEndLayer layer where backdrill ends
|
||||
*/
|
||||
void drawBackdrillIndicator( const BOARD_ITEM* aItem, const VECTOR2D& aCenter,
|
||||
int aDrillSize, PCB_LAYER_ID aStartLayer,
|
||||
PCB_LAYER_ID aEndLayer );
|
||||
|
||||
/**
|
||||
* Draw post-machining indicator (dashed circle) at the given center point.
|
||||
*
|
||||
* @param aItem the board item (for color lookup)
|
||||
* @param aCenter center point of the indicator
|
||||
* @param aLayer layer to use for color
|
||||
*/
|
||||
void drawPostMachiningIndicator( const BOARD_ITEM* aItem, const VECTOR2D& aCenter, PCB_LAYER_ID aLayer );
|
||||
|
||||
protected:
|
||||
PCB_RENDER_SETTINGS m_pcbSettings;
|
||||
FRAME_T m_frameType;
|
||||
|
||||
+737
-17
@@ -628,6 +628,66 @@ int PCB_VIA::GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetPrimaryDrillSize( const VECTOR2I& aSize )
|
||||
{
|
||||
m_padStack.Drill().size = aSize;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetPrimaryDrillShape( PAD_DRILL_SHAPE aShape )
|
||||
{
|
||||
m_padStack.Drill().shape = aShape;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetPrimaryDrillStartLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.Drill().start = aLayer;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetPrimaryDrillEndLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.Drill().end = aLayer;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetFrontPostMachining( const std::optional<PAD_DRILL_POST_MACHINING_MODE>& aMode )
|
||||
{
|
||||
m_padStack.FrontPostMachining().mode = aMode;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetBackPostMachining( const std::optional<PAD_DRILL_POST_MACHINING_MODE>& aMode )
|
||||
{
|
||||
m_padStack.BackPostMachining().mode = aMode;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetPrimaryDrillFilled( const std::optional<bool>& aFilled )
|
||||
{
|
||||
m_padStack.Drill().is_filled = aFilled;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetPrimaryDrillFilledFlag( bool aFilled )
|
||||
{
|
||||
m_padStack.Drill().is_filled = aFilled;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetPrimaryDrillCapped( const std::optional<bool>& aCapped )
|
||||
{
|
||||
m_padStack.Drill().is_capped = aCapped;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetPrimaryDrillCappedFlag( bool aCapped )
|
||||
{
|
||||
m_padStack.Drill().is_capped = aCapped;
|
||||
}
|
||||
|
||||
|
||||
int PCB_VIA::GetDrillValue() const
|
||||
{
|
||||
if( m_padStack.Drill().size.x > 0 ) // Use the specific value.
|
||||
@@ -643,6 +703,244 @@ int PCB_VIA::GetDrillValue() const
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetSecondaryDrillSize( const VECTOR2I& aSize )
|
||||
{
|
||||
m_padStack.SecondaryDrill().size = aSize;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::ClearSecondaryDrillSize()
|
||||
{
|
||||
m_padStack.SecondaryDrill().size = { 0, 0 };
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetSecondaryDrillSize( const std::optional<int>& aDrill )
|
||||
{
|
||||
if( aDrill.has_value() && *aDrill > 0 )
|
||||
SetSecondaryDrillSize( { *aDrill, *aDrill } );
|
||||
else
|
||||
ClearSecondaryDrillSize();
|
||||
}
|
||||
|
||||
|
||||
std::optional<int> PCB_VIA::GetSecondaryDrillSize() const
|
||||
{
|
||||
if( m_padStack.SecondaryDrill().size.x > 0 )
|
||||
return m_padStack.SecondaryDrill().size.x;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetSecondaryDrillStartLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.SecondaryDrill().start = aLayer;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetSecondaryDrillEndLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.SecondaryDrill().end = aLayer;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetSecondaryDrillShape( PAD_DRILL_SHAPE aShape )
|
||||
{
|
||||
m_padStack.SecondaryDrill().shape = aShape;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetTertiaryDrillSize( const VECTOR2I& aSize )
|
||||
{
|
||||
m_padStack.TertiaryDrill().size = aSize;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::ClearTertiaryDrillSize()
|
||||
{
|
||||
m_padStack.TertiaryDrill().size = { 0, 0 };
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetTertiaryDrillSize( const std::optional<int>& aDrill )
|
||||
{
|
||||
if( aDrill.has_value() && *aDrill > 0 )
|
||||
SetTertiaryDrillSize( { *aDrill, *aDrill } );
|
||||
else
|
||||
ClearTertiaryDrillSize();
|
||||
}
|
||||
|
||||
|
||||
std::optional<int> PCB_VIA::GetTertiaryDrillSize() const
|
||||
{
|
||||
if( m_padStack.TertiaryDrill().size.x > 0 )
|
||||
return m_padStack.TertiaryDrill().size.x;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetTertiaryDrillStartLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.TertiaryDrill().start = aLayer;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetTertiaryDrillEndLayer( PCB_LAYER_ID aLayer )
|
||||
{
|
||||
m_padStack.TertiaryDrill().end = aLayer;
|
||||
}
|
||||
|
||||
|
||||
void PCB_VIA::SetTertiaryDrillShape( PAD_DRILL_SHAPE aShape )
|
||||
{
|
||||
m_padStack.TertiaryDrill().shape = aShape;
|
||||
}
|
||||
|
||||
|
||||
bool PCB_VIA::IsBackdrilledOrPostMachined( PCB_LAYER_ID aLayer ) const
|
||||
{
|
||||
if( !IsCopperLayer( aLayer ) )
|
||||
return false;
|
||||
|
||||
const BOARD* board = GetBoard();
|
||||
|
||||
if( !board )
|
||||
return false;
|
||||
|
||||
// Check secondary drill (backdrill from top)
|
||||
const PADSTACK::DRILL_PROPS& secondaryDrill = m_padStack.SecondaryDrill();
|
||||
|
||||
if( secondaryDrill.size.x > 0 && secondaryDrill.start != UNDEFINED_LAYER
|
||||
&& secondaryDrill.end != UNDEFINED_LAYER )
|
||||
{
|
||||
// Check if aLayer is between start and end of secondary drill
|
||||
for( PCB_LAYER_ID layer : LAYER_RANGE( secondaryDrill.start, secondaryDrill.end,
|
||||
board->GetCopperLayerCount() ) )
|
||||
{
|
||||
if( layer == aLayer )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check tertiary drill (backdrill from bottom)
|
||||
const PADSTACK::DRILL_PROPS& tertiaryDrill = m_padStack.TertiaryDrill();
|
||||
|
||||
if( tertiaryDrill.size.x > 0 && tertiaryDrill.start != UNDEFINED_LAYER
|
||||
&& tertiaryDrill.end != UNDEFINED_LAYER )
|
||||
{
|
||||
// Check if aLayer is between start and end of tertiary drill
|
||||
for( PCB_LAYER_ID layer : LAYER_RANGE( tertiaryDrill.start, tertiaryDrill.end,
|
||||
board->GetCopperLayerCount() ) )
|
||||
{
|
||||
if( layer == aLayer )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the layer is affected by post-machining
|
||||
if( GetPostMachiningKnockout( aLayer ) > 0 )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int PCB_VIA::GetPostMachiningKnockout( PCB_LAYER_ID aLayer ) const
|
||||
{
|
||||
if( !IsCopperLayer( aLayer ) )
|
||||
return 0;
|
||||
|
||||
const BOARD* board = GetBoard();
|
||||
|
||||
if( !board )
|
||||
return 0;
|
||||
|
||||
const BOARD_STACKUP& stackup = board->GetDesignSettings().GetStackupDescriptor();
|
||||
|
||||
// Check front post-machining (counterbore/countersink from top)
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPM = m_padStack.FrontPostMachining();
|
||||
|
||||
if( frontPM.mode.has_value() && *frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& *frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN && frontPM.size > 0 )
|
||||
{
|
||||
int pmDepth = frontPM.depth;
|
||||
|
||||
// For countersink without explicit depth, calculate from diameter and angle
|
||||
if( pmDepth <= 0 && *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK
|
||||
&& frontPM.angle > 0 )
|
||||
{
|
||||
double halfAngleRad = ( frontPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
pmDepth = static_cast<int>( ( frontPM.size / 2.0 ) / tan( halfAngleRad ) );
|
||||
}
|
||||
|
||||
if( pmDepth > 0 )
|
||||
{
|
||||
// Calculate distance from F_Cu to aLayer
|
||||
int layerDist = stackup.GetLayerDistance( F_Cu, aLayer );
|
||||
|
||||
if( layerDist < pmDepth )
|
||||
{
|
||||
// For countersink, diameter decreases with depth
|
||||
if( *frontPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && frontPM.angle > 0 )
|
||||
{
|
||||
double halfAngleRad = ( frontPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
int diameterAtLayer = frontPM.size - static_cast<int>( 2.0 * layerDist * tan( halfAngleRad ) );
|
||||
return std::max( 0, diameterAtLayer );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counterbore - constant diameter
|
||||
return frontPM.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check back post-machining (counterbore/countersink from bottom)
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPM = m_padStack.BackPostMachining();
|
||||
|
||||
if( backPM.mode.has_value() && *backPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& *backPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN && backPM.size > 0 )
|
||||
{
|
||||
int pmDepth = backPM.depth;
|
||||
|
||||
// For countersink without explicit depth, calculate from diameter and angle
|
||||
if( pmDepth <= 0 && *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK
|
||||
&& backPM.angle > 0 )
|
||||
{
|
||||
double halfAngleRad = ( backPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
pmDepth = static_cast<int>( ( backPM.size / 2.0 ) / tan( halfAngleRad ) );
|
||||
}
|
||||
|
||||
if( pmDepth > 0 )
|
||||
{
|
||||
// Calculate distance from B_Cu to aLayer
|
||||
int layerDist = stackup.GetLayerDistance( B_Cu, aLayer );
|
||||
|
||||
if( layerDist < pmDepth )
|
||||
{
|
||||
// For countersink, diameter decreases with depth
|
||||
if( *backPM.mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK && backPM.angle > 0 )
|
||||
{
|
||||
double halfAngleRad = ( backPM.angle / 10.0 ) * M_PI / 180.0 / 2.0;
|
||||
int diameterAtLayer = backPM.size - static_cast<int>( 2.0 * layerDist * tan( halfAngleRad ) );
|
||||
return std::max( 0, diameterAtLayer );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Counterbore - constant diameter
|
||||
return backPM.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
EDA_ITEM_FLAGS PCB_TRACK::IsPointOnEnds( const VECTOR2I& point, int min_dist ) const
|
||||
{
|
||||
EDA_ITEM_FLAGS result = 0;
|
||||
@@ -1094,7 +1392,6 @@ FILLING_MODE PCB_VIA::GetFillingMode() const
|
||||
|
||||
return FILLING_MODE::FROM_BOARD;
|
||||
}
|
||||
// clang-format on: the suggestion is slightly less readable
|
||||
|
||||
|
||||
bool PCB_VIA::IsTented( PCB_LAYER_ID aLayer ) const
|
||||
@@ -1411,14 +1708,48 @@ void PCB_VIA::SanitizeLayers()
|
||||
|
||||
if( !IsCopperLayerLowerThan( Padstack().Drill().end, Padstack().Drill().start) )
|
||||
std::swap( Padstack().Drill().end, Padstack().Drill().start );
|
||||
|
||||
PADSTACK::DRILL_PROPS& secondary = Padstack().SecondaryDrill();
|
||||
|
||||
if( secondary.start != UNDEFINED_LAYER && !IsCopperLayer( secondary.start ) )
|
||||
secondary.start = UNDEFINED_LAYER;
|
||||
|
||||
if( secondary.end != UNDEFINED_LAYER && !IsCopperLayer( secondary.end ) )
|
||||
secondary.end = UNDEFINED_LAYER;
|
||||
|
||||
int copperCount = BoardCopperLayerCount();
|
||||
|
||||
if( copperCount > 0 )
|
||||
{
|
||||
LSET cuMask = LSET::AllCuMask( copperCount );
|
||||
|
||||
if( secondary.start != UNDEFINED_LAYER && !cuMask.Contains( secondary.start ) )
|
||||
secondary.start = UNDEFINED_LAYER;
|
||||
|
||||
if( secondary.end != UNDEFINED_LAYER && !cuMask.Contains( secondary.end ) )
|
||||
secondary.end = UNDEFINED_LAYER;
|
||||
}
|
||||
|
||||
if( secondary.start != UNDEFINED_LAYER && secondary.end != UNDEFINED_LAYER
|
||||
&& secondary.start == secondary.end )
|
||||
{
|
||||
secondary.end = UNDEFINED_LAYER;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::optional<PCB_VIA::VIA_PARAMETER_ERROR>
|
||||
PCB_VIA::ValidateViaParameters( std::optional<int> aDiameter,
|
||||
std::optional<int> aDrill,
|
||||
std::optional<PCB_LAYER_ID> aStartLayer,
|
||||
std::optional<PCB_LAYER_ID> aEndLayer )
|
||||
std::optional<int> aPrimaryDrill,
|
||||
std::optional<PCB_LAYER_ID> aPrimaryStartLayer,
|
||||
std::optional<PCB_LAYER_ID> aPrimaryEndLayer,
|
||||
std::optional<int> aSecondaryDrill,
|
||||
std::optional<PCB_LAYER_ID> aSecondaryStartLayer,
|
||||
std::optional<PCB_LAYER_ID> aSecondaryEndLayer,
|
||||
std::optional<int> aTertiaryDrill,
|
||||
std::optional<PCB_LAYER_ID> aTertiaryStartLayer,
|
||||
std::optional<PCB_LAYER_ID> aTertiaryEndLayer,
|
||||
int aCopperLayerCount )
|
||||
{
|
||||
VIA_PARAMETER_ERROR error;
|
||||
|
||||
@@ -1429,43 +1760,154 @@ PCB_VIA::ValidateViaParameters( std::optional<int> aDiameter,
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aDrill.has_value() && aDrill.value() < GEOMETRY_MIN_SIZE )
|
||||
if( aPrimaryDrill.has_value() && aPrimaryDrill.value() < GEOMETRY_MIN_SIZE )
|
||||
{
|
||||
error.m_Message = _( "Via drill is too small." );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::DRILL;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aDiameter.has_value() && !aDrill.has_value() )
|
||||
if( aDiameter.has_value() && !aPrimaryDrill.has_value() )
|
||||
{
|
||||
error.m_Message = _( "No via hole size defined." );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::DRILL;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aDrill.has_value() && !aDiameter.has_value() )
|
||||
if( aPrimaryDrill.has_value() && !aDiameter.has_value() )
|
||||
{
|
||||
error.m_Message = _( "No via diameter defined." );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::DIAMETER;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aDiameter.has_value() && aDrill.has_value()
|
||||
&& aDiameter.value() <= aDrill.value() )
|
||||
if( aDiameter.has_value() && aPrimaryDrill.has_value()
|
||||
&& aDiameter.value() <= aPrimaryDrill.value() )
|
||||
{
|
||||
error.m_Message = _( "Via hole size must be smaller than via diameter" );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::DRILL;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aStartLayer.has_value() && aEndLayer.has_value()
|
||||
&& aStartLayer.value() == aEndLayer.value() )
|
||||
std::optional<LSET> copperMask;
|
||||
|
||||
auto validateLayer = [&]( std::optional<PCB_LAYER_ID> aLayer,
|
||||
VIA_PARAMETER_ERROR::FIELD aField ) -> bool
|
||||
{
|
||||
if( !aLayer.has_value() )
|
||||
return true;
|
||||
|
||||
PCB_LAYER_ID layer = aLayer.value();
|
||||
|
||||
if( layer == UNDEFINED_LAYER )
|
||||
return true;
|
||||
|
||||
if( !IsCopperLayer( layer ) )
|
||||
{
|
||||
error.m_Message = _( "Via layer must be a copper layer." );
|
||||
error.m_Field = aField;
|
||||
return false;
|
||||
}
|
||||
|
||||
if( aCopperLayerCount > 0 )
|
||||
{
|
||||
if( !copperMask.has_value() )
|
||||
copperMask = LSET::AllCuMask( aCopperLayerCount );
|
||||
|
||||
if( !copperMask->Contains( layer ) )
|
||||
{
|
||||
error.m_Message = _( "Via layer is outside the board stack." );
|
||||
error.m_Field = aField;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if( !validateLayer( aPrimaryStartLayer, VIA_PARAMETER_ERROR::FIELD::START_LAYER ) )
|
||||
return error;
|
||||
|
||||
if( !validateLayer( aPrimaryEndLayer, VIA_PARAMETER_ERROR::FIELD::END_LAYER ) )
|
||||
return error;
|
||||
|
||||
if( aPrimaryStartLayer.has_value() && aPrimaryEndLayer.has_value()
|
||||
&& aPrimaryStartLayer.value() == aPrimaryEndLayer.value() )
|
||||
{
|
||||
error.m_Message = _( "Via start layer and end layer cannot be the same" );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::START_LAYER;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aSecondaryDrill.has_value() )
|
||||
{
|
||||
if( aSecondaryDrill.value() < GEOMETRY_MIN_SIZE )
|
||||
{
|
||||
error.m_Message = _( "Backdrill diameter is too small." );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::SECONDARY_DRILL;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aDiameter.has_value() && aDiameter.value() <= aSecondaryDrill.value() )
|
||||
{
|
||||
error.m_Message = _( "Backdrill diameter must be smaller than via diameter" );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::SECONDARY_DRILL;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( !validateLayer( aSecondaryStartLayer, VIA_PARAMETER_ERROR::FIELD::SECONDARY_START_LAYER ) )
|
||||
return error;
|
||||
|
||||
if( !validateLayer( aSecondaryEndLayer, VIA_PARAMETER_ERROR::FIELD::SECONDARY_END_LAYER ) )
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aTertiaryDrill.has_value() )
|
||||
{
|
||||
if( aTertiaryDrill.value() < GEOMETRY_MIN_SIZE )
|
||||
{
|
||||
error.m_Message = _( "Tertiary backdrill diameter is too small." );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::TERTIARY_DRILL;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aDiameter.has_value() && aDiameter.value() <= aTertiaryDrill.value() )
|
||||
{
|
||||
error.m_Message = _( "Tertiary backdrill diameter must be smaller than via diameter" );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::TERTIARY_DRILL;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( !validateLayer( aTertiaryStartLayer, VIA_PARAMETER_ERROR::FIELD::TERTIARY_START_LAYER ) )
|
||||
return error;
|
||||
|
||||
if( !validateLayer( aTertiaryEndLayer, VIA_PARAMETER_ERROR::FIELD::TERTIARY_END_LAYER ) )
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aTertiaryDrill.has_value() )
|
||||
{
|
||||
if( aTertiaryDrill.value() < GEOMETRY_MIN_SIZE )
|
||||
{
|
||||
error.m_Message = _( "Tertiary backdrill diameter is too small." );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::TERTIARY_DRILL;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( aDiameter.has_value() && aDiameter.value() <= aTertiaryDrill.value() )
|
||||
{
|
||||
error.m_Message = _( "Tertiary backdrill diameter must be smaller than via diameter" );
|
||||
error.m_Field = VIA_PARAMETER_ERROR::FIELD::TERTIARY_DRILL;
|
||||
return error;
|
||||
}
|
||||
|
||||
if( !validateLayer( aTertiaryStartLayer, VIA_PARAMETER_ERROR::FIELD::TERTIARY_START_LAYER ) )
|
||||
return error;
|
||||
|
||||
if( !validateLayer( aTertiaryEndLayer, VIA_PARAMETER_ERROR::FIELD::TERTIARY_END_LAYER ) )
|
||||
return error;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
@@ -2304,6 +2746,38 @@ std::shared_ptr<SHAPE> PCB_TRACK::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHI
|
||||
|
||||
std::shared_ptr<SHAPE> PCB_VIA::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
|
||||
{
|
||||
// Check if this layer has copper removed by backdrill or post-machining
|
||||
if( aLayer != UNDEFINED_LAYER && IsBackdrilledOrPostMachined( aLayer ) )
|
||||
{
|
||||
// Return the larger of the backdrill or post-machining hole
|
||||
int holeSize = 0;
|
||||
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPM = Padstack().FrontPostMachining();
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPM = Padstack().BackPostMachining();
|
||||
|
||||
if( frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
holeSize = std::max( holeSize, frontPM.size );
|
||||
}
|
||||
|
||||
if( backPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
holeSize = std::max( holeSize, backPM.size );
|
||||
}
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secDrill = Padstack().SecondaryDrill();
|
||||
|
||||
if( secDrill.start != UNDEFINED_LAYER && secDrill.end != UNDEFINED_LAYER )
|
||||
holeSize = std::max( holeSize, secDrill.size.x );
|
||||
|
||||
if( holeSize > 0 )
|
||||
return std::make_shared<SHAPE_CIRCLE>( m_Start, holeSize / 2 );
|
||||
else
|
||||
return std::make_shared<SHAPE_CIRCLE>( m_Start, GetDrillValue() / 2 );
|
||||
}
|
||||
|
||||
if( aFlash == FLASHING::ALWAYS_FLASHED
|
||||
|| ( aFlash == FLASHING::DEFAULT && FlashLayer( aLayer ) ) )
|
||||
{
|
||||
@@ -2435,6 +2909,7 @@ static struct TRACK_VIA_DESC
|
||||
.Map( FILLING_MODE::FROM_BOARD, _HKI( "From board stackup" ) )
|
||||
.Map( FILLING_MODE::FILLED, _HKI( "Filled" ) )
|
||||
.Map( FILLING_MODE::NOT_FILLED, _HKI( "Not filled" ) );
|
||||
|
||||
// clang-format on: the suggestion is less readable
|
||||
|
||||
ENUM_MAP<PCB_LAYER_ID>& layerEnum = ENUM_MAP<PCB_LAYER_ID>::Instance();
|
||||
@@ -2461,8 +2936,36 @@ static struct TRACK_VIA_DESC
|
||||
std::optional<int> diameter = aValue.As<int>();
|
||||
std::optional<int> drill = via->GetDrillValue();
|
||||
|
||||
std::optional<PCB_LAYER_ID> startLayer;
|
||||
|
||||
if( via->Padstack().Drill().start != UNDEFINED_LAYER )
|
||||
startLayer = via->Padstack().Drill().start;
|
||||
|
||||
std::optional<PCB_LAYER_ID> endLayer;
|
||||
|
||||
if( via->Padstack().Drill().end != UNDEFINED_LAYER )
|
||||
endLayer = via->Padstack().Drill().end;
|
||||
|
||||
std::optional<int> secondaryDrill = via->GetSecondaryDrillSize();
|
||||
|
||||
std::optional<PCB_LAYER_ID> secondaryStart;
|
||||
|
||||
if( via->GetSecondaryDrillStartLayer() != UNDEFINED_LAYER )
|
||||
secondaryStart = via->GetSecondaryDrillStartLayer();
|
||||
|
||||
std::optional<PCB_LAYER_ID> secondaryEnd;
|
||||
|
||||
if( via->GetSecondaryDrillEndLayer() != UNDEFINED_LAYER )
|
||||
secondaryEnd = via->GetSecondaryDrillEndLayer();
|
||||
|
||||
int copperLayerCount = via->BoardCopperLayerCount();
|
||||
|
||||
if( std::optional<PCB_VIA::VIA_PARAMETER_ERROR> error =
|
||||
PCB_VIA::ValidateViaParameters( diameter, drill ) )
|
||||
PCB_VIA::ValidateViaParameters( diameter, drill, startLayer, endLayer,
|
||||
secondaryDrill, secondaryStart,
|
||||
secondaryEnd, std::nullopt,
|
||||
std::nullopt, std::nullopt,
|
||||
copperLayerCount ) )
|
||||
{
|
||||
return std::make_unique<VALIDATION_ERROR_MSG>( error->m_Message );
|
||||
}
|
||||
@@ -2484,8 +2987,36 @@ static struct TRACK_VIA_DESC
|
||||
std::optional<int> diameter = via->GetFrontWidth();
|
||||
std::optional<int> drill = aValue.As<int>();
|
||||
|
||||
std::optional<PCB_LAYER_ID> startLayer;
|
||||
|
||||
if( via->Padstack().Drill().start != UNDEFINED_LAYER )
|
||||
startLayer = via->Padstack().Drill().start;
|
||||
|
||||
std::optional<PCB_LAYER_ID> endLayer;
|
||||
|
||||
if( via->Padstack().Drill().end != UNDEFINED_LAYER )
|
||||
endLayer = via->Padstack().Drill().end;
|
||||
|
||||
std::optional<int> secondaryDrill = via->GetSecondaryDrillSize();
|
||||
|
||||
std::optional<PCB_LAYER_ID> secondaryStart;
|
||||
|
||||
if( via->GetSecondaryDrillStartLayer() != UNDEFINED_LAYER )
|
||||
secondaryStart = via->GetSecondaryDrillStartLayer();
|
||||
|
||||
std::optional<PCB_LAYER_ID> secondaryEnd;
|
||||
|
||||
if( via->GetSecondaryDrillEndLayer() != UNDEFINED_LAYER )
|
||||
secondaryEnd = via->GetSecondaryDrillEndLayer();
|
||||
|
||||
int copperLayerCount = via->BoardCopperLayerCount();
|
||||
|
||||
if( std::optional<PCB_VIA::VIA_PARAMETER_ERROR> error =
|
||||
PCB_VIA::ValidateViaParameters( diameter, drill ) )
|
||||
PCB_VIA::ValidateViaParameters( diameter, drill, startLayer, endLayer,
|
||||
secondaryDrill, secondaryStart,
|
||||
secondaryEnd, std::nullopt,
|
||||
std::nullopt, std::nullopt,
|
||||
copperLayerCount ) )
|
||||
{
|
||||
return std::make_unique<VALIDATION_ERROR_MSG>( error->m_Message );
|
||||
}
|
||||
@@ -2510,9 +3041,34 @@ static struct TRACK_VIA_DESC
|
||||
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
|
||||
|
||||
std::optional<int> diameter = via->GetFrontWidth();
|
||||
std::optional<int> drill = via->GetDrillValue();
|
||||
|
||||
std::optional<PCB_LAYER_ID> endLayer;
|
||||
|
||||
if( via->BottomLayer() != UNDEFINED_LAYER )
|
||||
endLayer = via->BottomLayer();
|
||||
|
||||
std::optional<int> secondaryDrill = via->GetSecondaryDrillSize();
|
||||
|
||||
std::optional<PCB_LAYER_ID> secondaryStart;
|
||||
|
||||
if( via->GetSecondaryDrillStartLayer() != UNDEFINED_LAYER )
|
||||
secondaryStart = via->GetSecondaryDrillStartLayer();
|
||||
|
||||
std::optional<PCB_LAYER_ID> secondaryEnd;
|
||||
|
||||
if( via->GetSecondaryDrillEndLayer() != UNDEFINED_LAYER )
|
||||
secondaryEnd = via->GetSecondaryDrillEndLayer();
|
||||
|
||||
int copperLayerCount = via->BoardCopperLayerCount();
|
||||
|
||||
if( std::optional<PCB_VIA::VIA_PARAMETER_ERROR> error =
|
||||
PCB_VIA::ValidateViaParameters( std::nullopt, std::nullopt,
|
||||
layer, via->BottomLayer() ) )
|
||||
PCB_VIA::ValidateViaParameters( diameter, drill, layer, endLayer,
|
||||
secondaryDrill, secondaryStart,
|
||||
secondaryEnd, std::nullopt,
|
||||
std::nullopt, std::nullopt,
|
||||
copperLayerCount ) )
|
||||
{
|
||||
return std::make_unique<VALIDATION_ERROR_MSG>( error->m_Message );
|
||||
}
|
||||
@@ -2537,9 +3093,34 @@ static struct TRACK_VIA_DESC
|
||||
|
||||
PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
|
||||
|
||||
std::optional<int> diameter = via->GetFrontWidth();
|
||||
std::optional<int> drill = via->GetDrillValue();
|
||||
|
||||
std::optional<PCB_LAYER_ID> startLayer;
|
||||
|
||||
if( via->TopLayer() != UNDEFINED_LAYER )
|
||||
startLayer = via->TopLayer();
|
||||
|
||||
std::optional<int> secondaryDrill = via->GetSecondaryDrillSize();
|
||||
|
||||
std::optional<PCB_LAYER_ID> secondaryStart;
|
||||
|
||||
if( via->GetSecondaryDrillStartLayer() != UNDEFINED_LAYER )
|
||||
secondaryStart = via->GetSecondaryDrillStartLayer();
|
||||
|
||||
std::optional<PCB_LAYER_ID> secondaryEnd;
|
||||
|
||||
if( via->GetSecondaryDrillEndLayer() != UNDEFINED_LAYER )
|
||||
secondaryEnd = via->GetSecondaryDrillEndLayer();
|
||||
|
||||
int copperLayerCount = via->BoardCopperLayerCount();
|
||||
|
||||
if( std::optional<PCB_VIA::VIA_PARAMETER_ERROR> error =
|
||||
PCB_VIA::ValidateViaParameters( std::nullopt, std::nullopt,
|
||||
via->TopLayer(), layer ) )
|
||||
PCB_VIA::ValidateViaParameters( diameter, drill, startLayer, layer,
|
||||
secondaryDrill, secondaryStart,
|
||||
secondaryEnd, std::nullopt,
|
||||
std::nullopt, std::nullopt,
|
||||
copperLayerCount ) )
|
||||
{
|
||||
return std::make_unique<VALIDATION_ERROR_MSG>( error->m_Message );
|
||||
}
|
||||
@@ -2599,6 +3180,7 @@ static struct TRACK_VIA_DESC
|
||||
|
||||
// TODO test drill, use getdrillvalue?
|
||||
const wxString groupVia = _HKI( "Via Properties" );
|
||||
const wxString groupBackdrill = _HKI( "Backdrill" );
|
||||
|
||||
propMgr.Mask( TYPE_HASH( PCB_VIA ), TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) );
|
||||
|
||||
@@ -2633,6 +3215,143 @@ static struct TRACK_VIA_DESC
|
||||
&PCB_VIA::SetCappingMode, &PCB_VIA::GetCappingMode ), groupVia );
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, FILLING_MODE>( _HKI( "Filling" ),
|
||||
&PCB_VIA::SetFillingMode, &PCB_VIA::GetFillingMode ), groupVia );
|
||||
|
||||
auto canHaveBackdrill = []( INSPECTABLE* aItem )
|
||||
{
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
if( via->GetViaType() == VIATYPE::THROUGH )
|
||||
return true;
|
||||
|
||||
if( via->Padstack().GetBackdrillMode() != BACKDRILL_MODE::NO_BACKDRILL )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, BACKDRILL_MODE>( _HKI( "Backdrill Mode" ),
|
||||
&PCB_VIA::SetBackdrillMode, &PCB_VIA::GetBackdrillMode ), groupBackdrill ).SetAvailableFunc( canHaveBackdrill );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_VIA, std::optional<int>>( _HKI( "Bottom Backdrill Size" ),
|
||||
&PCB_VIA::SetBottomBackdrillSize, &PCB_VIA::GetBottomBackdrillSize, PROPERTY_DISPLAY::PT_SIZE ), groupBackdrill )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetBackdrillMode();
|
||||
return mode == BACKDRILL_MODE::BACKDRILL_BOTTOM || mode == BACKDRILL_MODE::BACKDRILL_BOTH;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Bottom Backdrill Layer" ),
|
||||
&PCB_VIA::SetBottomBackdrillLayer, &PCB_VIA::GetBottomBackdrillLayer ), groupBackdrill )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetBackdrillMode();
|
||||
return mode == BACKDRILL_MODE::BACKDRILL_BOTTOM || mode == BACKDRILL_MODE::BACKDRILL_BOTH;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_VIA, std::optional<int>>( _HKI( "Top Backdrill Size" ),
|
||||
&PCB_VIA::SetTopBackdrillSize, &PCB_VIA::GetTopBackdrillSize, PROPERTY_DISPLAY::PT_SIZE ), groupBackdrill )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetBackdrillMode();
|
||||
return mode == BACKDRILL_MODE::BACKDRILL_TOP || mode == BACKDRILL_MODE::BACKDRILL_BOTH;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PCB_LAYER_ID>( _HKI( "Top Backdrill Layer" ),
|
||||
&PCB_VIA::SetTopBackdrillLayer, &PCB_VIA::GetTopBackdrillLayer ), groupBackdrill )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) -> bool
|
||||
{
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetBackdrillMode();
|
||||
return mode == BACKDRILL_MODE::BACKDRILL_TOP || mode == BACKDRILL_MODE::BACKDRILL_BOTH;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PAD_DRILL_POST_MACHINING_MODE>( _HKI( "Front Post-machining" ),
|
||||
&PCB_VIA::SetFrontPostMachiningMode, &PCB_VIA::GetFrontPostMachiningMode ), groupVia );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Front Post-machining Size" ),
|
||||
&PCB_VIA::SetFrontPostMachiningSize, &PCB_VIA::GetFrontPostMachiningSize, PROPERTY_DISPLAY::PT_SIZE ), groupVia )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetFrontPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE || mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Front Post-machining Depth" ),
|
||||
&PCB_VIA::SetFrontPostMachiningDepth, &PCB_VIA::GetFrontPostMachiningDepth, PROPERTY_DISPLAY::PT_SIZE ), groupVia )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetFrontPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Front Post-machining Angle" ),
|
||||
&PCB_VIA::SetFrontPostMachiningAngle, &PCB_VIA::GetFrontPostMachiningAngle, PROPERTY_DISPLAY::PT_DECIDEGREE ), groupVia )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetFrontPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY_ENUM<PCB_VIA, PAD_DRILL_POST_MACHINING_MODE>( _HKI( "Back Post-machining" ),
|
||||
&PCB_VIA::SetBackPostMachiningMode, &PCB_VIA::GetBackPostMachiningMode ), groupVia );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Back Post-machining Size" ),
|
||||
&PCB_VIA::SetBackPostMachiningSize, &PCB_VIA::GetBackPostMachiningSize, PROPERTY_DISPLAY::PT_SIZE ), groupVia )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetBackPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE || mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Back Post-machining Depth" ),
|
||||
&PCB_VIA::SetBackPostMachiningDepth, &PCB_VIA::GetBackPostMachiningDepth, PROPERTY_DISPLAY::PT_SIZE ), groupVia )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetBackPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
|
||||
propMgr.AddProperty( new PROPERTY<PCB_VIA, int>( _HKI( "Back Post-machining Angle" ),
|
||||
&PCB_VIA::SetBackPostMachiningAngle, &PCB_VIA::GetBackPostMachiningAngle, PROPERTY_DISPLAY::PT_DECIDEGREE ), groupVia )
|
||||
.SetAvailableFunc( []( INSPECTABLE* aItem ) {
|
||||
if( PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem ) )
|
||||
{
|
||||
auto mode = via->GetBackPostMachining();
|
||||
return mode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK;
|
||||
}
|
||||
return false;
|
||||
} );
|
||||
// clang-format on: the suggestion is less readable
|
||||
}
|
||||
} _TRACK_VIA_DESC;
|
||||
@@ -2643,3 +3362,4 @@ ENUM_TO_WXANY( COVERING_MODE );
|
||||
ENUM_TO_WXANY( PLUGGING_MODE );
|
||||
ENUM_TO_WXANY( CAPPING_MODE );
|
||||
ENUM_TO_WXANY( FILLING_MODE );
|
||||
|
||||
|
||||
+143
-6
@@ -464,6 +464,21 @@ public:
|
||||
PADSTACK& Padstack() { return m_padStack; }
|
||||
void SetPadstack( const PADSTACK& aPadstack ) { m_padStack = aPadstack; }
|
||||
|
||||
BACKDRILL_MODE GetBackdrillMode() const { return m_padStack.GetBackdrillMode(); }
|
||||
void SetBackdrillMode( BACKDRILL_MODE aMode ) { m_padStack.SetBackdrillMode( aMode ); }
|
||||
|
||||
std::optional<int> GetBottomBackdrillSize() const { return m_padStack.GetBackdrillSize( false ); }
|
||||
void SetBottomBackdrillSize( std::optional<int> aSize ) { m_padStack.SetBackdrillSize( false, aSize ); }
|
||||
|
||||
PCB_LAYER_ID GetBottomBackdrillLayer() const { return m_padStack.GetBackdrillEndLayer( false ); }
|
||||
void SetBottomBackdrillLayer( PCB_LAYER_ID aLayer ) { m_padStack.SetBackdrillEndLayer( false, aLayer ); }
|
||||
|
||||
std::optional<int> GetTopBackdrillSize() const { return m_padStack.GetBackdrillSize( true ); }
|
||||
void SetTopBackdrillSize( std::optional<int> aSize ) { m_padStack.SetBackdrillSize( true, aSize ); }
|
||||
|
||||
PCB_LAYER_ID GetTopBackdrillLayer() const { return m_padStack.GetBackdrillEndLayer( true ); }
|
||||
void SetTopBackdrillLayer( PCB_LAYER_ID aLayer ) { m_padStack.SetBackdrillEndLayer( true, aLayer ); }
|
||||
|
||||
bool IsMicroVia() const;
|
||||
bool IsBlindVia() const;
|
||||
bool IsBuriedVia() const;
|
||||
@@ -476,7 +491,13 @@ public:
|
||||
DIAMETER,
|
||||
DRILL,
|
||||
START_LAYER,
|
||||
END_LAYER
|
||||
END_LAYER,
|
||||
SECONDARY_DRILL,
|
||||
SECONDARY_START_LAYER,
|
||||
SECONDARY_END_LAYER,
|
||||
TERTIARY_DRILL,
|
||||
TERTIARY_START_LAYER,
|
||||
TERTIARY_END_LAYER
|
||||
};
|
||||
|
||||
wxString m_Message;
|
||||
@@ -485,9 +506,16 @@ public:
|
||||
|
||||
static std::optional<VIA_PARAMETER_ERROR>
|
||||
ValidateViaParameters( std::optional<int> aDiameter,
|
||||
std::optional<int> aDrill,
|
||||
std::optional<PCB_LAYER_ID> aStartLayer = std::nullopt,
|
||||
std::optional<PCB_LAYER_ID> aEndLayer = std::nullopt );
|
||||
std::optional<int> aPrimaryDrill,
|
||||
std::optional<PCB_LAYER_ID> aPrimaryStartLayer = std::nullopt,
|
||||
std::optional<PCB_LAYER_ID> aPrimaryEndLayer = std::nullopt,
|
||||
std::optional<int> aSecondaryDrill = std::nullopt,
|
||||
std::optional<PCB_LAYER_ID> aSecondaryStartLayer = std::nullopt,
|
||||
std::optional<PCB_LAYER_ID> aSecondaryEndLayer = std::nullopt,
|
||||
std::optional<int> aTertiaryDrill = std::nullopt,
|
||||
std::optional<PCB_LAYER_ID> aTertiaryStartLayer = std::nullopt,
|
||||
std::optional<PCB_LAYER_ID> aTertiaryEndLayer = std::nullopt,
|
||||
int aCopperLayerCount = 0 );
|
||||
|
||||
const BOX2I GetBoundingBox() const override;
|
||||
const BOX2I GetBoundingBox( PCB_LAYER_ID aLayer ) const;
|
||||
@@ -696,9 +724,90 @@ public:
|
||||
*
|
||||
* @param aDrill is the new drill diameter
|
||||
*/
|
||||
void SetPrimaryDrillSize( const VECTOR2I& aSize );
|
||||
const VECTOR2I& GetPrimaryDrillSize() const { return m_padStack.Drill().size; }
|
||||
|
||||
void SetPrimaryDrillShape( PAD_DRILL_SHAPE aShape );
|
||||
PAD_DRILL_SHAPE GetPrimaryDrillShape() const { return m_padStack.Drill().shape; }
|
||||
|
||||
void SetPrimaryDrillStartLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetPrimaryDrillStartLayer() const { return m_padStack.Drill().start; }
|
||||
|
||||
void SetPrimaryDrillEndLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetPrimaryDrillEndLayer() const { return m_padStack.Drill().end; }
|
||||
|
||||
void SetFrontPostMachining( const std::optional<PAD_DRILL_POST_MACHINING_MODE>& aMode );
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> GetFrontPostMachining() const { return m_padStack.FrontPostMachining().mode; }
|
||||
|
||||
void SetFrontPostMachiningMode( PAD_DRILL_POST_MACHINING_MODE aMode )
|
||||
{
|
||||
m_padStack.FrontPostMachining().mode = aMode;
|
||||
}
|
||||
|
||||
PAD_DRILL_POST_MACHINING_MODE GetFrontPostMachiningMode() const
|
||||
{
|
||||
return m_padStack.FrontPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
|
||||
}
|
||||
|
||||
void SetFrontPostMachiningSize( int aSize ) { m_padStack.FrontPostMachining().size = aSize; }
|
||||
int GetFrontPostMachiningSize() const { return m_padStack.FrontPostMachining().size; }
|
||||
void SetFrontPostMachiningDepth( int aDepth ) { m_padStack.FrontPostMachining().depth = aDepth; }
|
||||
int GetFrontPostMachiningDepth() const { return m_padStack.FrontPostMachining().depth; }
|
||||
void SetFrontPostMachiningAngle( int aAngle ) { m_padStack.FrontPostMachining().angle = aAngle; }
|
||||
int GetFrontPostMachiningAngle() const { return m_padStack.FrontPostMachining().angle; }
|
||||
|
||||
void SetBackPostMachining( const std::optional<PAD_DRILL_POST_MACHINING_MODE>& aMode );
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> GetBackPostMachining() const { return m_padStack.BackPostMachining().mode; }
|
||||
|
||||
void SetBackPostMachiningMode( PAD_DRILL_POST_MACHINING_MODE aMode )
|
||||
{
|
||||
m_padStack.BackPostMachining().mode = aMode;
|
||||
}
|
||||
|
||||
PAD_DRILL_POST_MACHINING_MODE GetBackPostMachiningMode() const
|
||||
{
|
||||
return m_padStack.BackPostMachining().mode.value_or( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED );
|
||||
}
|
||||
|
||||
void SetBackPostMachiningSize( int aSize ) { m_padStack.BackPostMachining().size = aSize; }
|
||||
int GetBackPostMachiningSize() const { return m_padStack.BackPostMachining().size; }
|
||||
void SetBackPostMachiningDepth( int aDepth ) { m_padStack.BackPostMachining().depth = aDepth; }
|
||||
int GetBackPostMachiningDepth() const { return m_padStack.BackPostMachining().depth; }
|
||||
void SetBackPostMachiningAngle( int aAngle ) { m_padStack.BackPostMachining().angle = aAngle; }
|
||||
int GetBackPostMachiningAngle() const { return m_padStack.BackPostMachining().angle; }
|
||||
|
||||
/**
|
||||
* Check if a layer is affected by backdrilling or post-machining operations.
|
||||
*
|
||||
* This checks both the secondary/tertiary drills (backdrill) and post-machining
|
||||
* (counterbore/countersink) settings to determine if the given layer has had copper removed.
|
||||
*
|
||||
* @param aLayer the copper layer to check
|
||||
* @return true if the layer is affected by backdrilling or post-machining
|
||||
*/
|
||||
bool IsBackdrilledOrPostMachined( PCB_LAYER_ID aLayer ) const;
|
||||
|
||||
/**
|
||||
* Get the knockout diameter for a layer affected by post-machining.
|
||||
*
|
||||
* @param aLayer the copper layer to check
|
||||
* @return the diameter to knockout on this layer, or 0 if layer is not affected
|
||||
*/
|
||||
int GetPostMachiningKnockout( PCB_LAYER_ID aLayer ) const;
|
||||
|
||||
void SetPrimaryDrillFilled( const std::optional<bool>& aFilled );
|
||||
void SetPrimaryDrillFilledFlag( bool aFilled );
|
||||
std::optional<bool> GetPrimaryDrillFilled() const { return m_padStack.Drill().is_filled; }
|
||||
bool GetPrimaryDrillFilledFlag() const { return m_padStack.Drill().is_filled.value_or( false ); }
|
||||
|
||||
void SetPrimaryDrillCapped( const std::optional<bool>& aCapped );
|
||||
void SetPrimaryDrillCappedFlag( bool aCapped );
|
||||
std::optional<bool> GetPrimaryDrillCapped() const { return m_padStack.Drill().is_capped; }
|
||||
bool GetPrimaryDrillCappedFlag() const { return m_padStack.Drill().is_capped.value_or( false ); }
|
||||
|
||||
void SetDrill( int aDrill )
|
||||
{
|
||||
m_padStack.Drill().size = { aDrill, aDrill };
|
||||
SetPrimaryDrillSize( { aDrill, aDrill } );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -706,7 +815,7 @@ public:
|
||||
*
|
||||
* @note Use GetDrillValue() to get the calculated value.
|
||||
*/
|
||||
int GetDrill() const { return m_padStack.Drill().size.x; }
|
||||
int GetDrill() const { return GetPrimaryDrillSize().x; }
|
||||
|
||||
/**
|
||||
* Calculate the drill value for vias (m_drill if > 0, or default drill value for the board).
|
||||
@@ -723,6 +832,34 @@ public:
|
||||
m_padStack.Drill().size = { UNDEFINED_DRILL_DIAMETER, UNDEFINED_DRILL_DIAMETER };
|
||||
}
|
||||
|
||||
void SetSecondaryDrillSize( const VECTOR2I& aSize );
|
||||
void ClearSecondaryDrillSize();
|
||||
void SetSecondaryDrillSize( const std::optional<int>& aDrill );
|
||||
std::optional<int> GetSecondaryDrillSize() const;
|
||||
|
||||
void SetSecondaryDrillStartLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetSecondaryDrillStartLayer() const { return m_padStack.SecondaryDrill().start; }
|
||||
|
||||
void SetSecondaryDrillEndLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetSecondaryDrillEndLayer() const { return m_padStack.SecondaryDrill().end; }
|
||||
|
||||
void SetSecondaryDrillShape( PAD_DRILL_SHAPE aShape );
|
||||
PAD_DRILL_SHAPE GetSecondaryDrillShape() const { return m_padStack.SecondaryDrill().shape; }
|
||||
|
||||
void SetTertiaryDrillSize( const VECTOR2I& aSize );
|
||||
void ClearTertiaryDrillSize();
|
||||
void SetTertiaryDrillSize( const std::optional<int>& aDrill );
|
||||
std::optional<int> GetTertiaryDrillSize() const;
|
||||
|
||||
void SetTertiaryDrillStartLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetTertiaryDrillStartLayer() const { return m_padStack.TertiaryDrill().start; }
|
||||
|
||||
void SetTertiaryDrillEndLayer( PCB_LAYER_ID aLayer );
|
||||
PCB_LAYER_ID GetTertiaryDrillEndLayer() const { return m_padStack.TertiaryDrill().end; }
|
||||
|
||||
void SetTertiaryDrillShape( PAD_DRILL_SHAPE aShape );
|
||||
PAD_DRILL_SHAPE GetTertiaryDrillShape() const { return m_padStack.TertiaryDrill().shape; }
|
||||
|
||||
/**
|
||||
* Check if the via is a free via (as opposed to one created on a track by the router).
|
||||
*
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <board_commit.h>
|
||||
#include <eda_group.h>
|
||||
#include <layer_ids.h>
|
||||
#include <optional>
|
||||
#include <kidialog.h>
|
||||
#include <tools/pcb_tool_base.h>
|
||||
#include <tool/tool_manager.h>
|
||||
@@ -1377,6 +1378,29 @@ std::unique_ptr<PNS::VIA> PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* aVia )
|
||||
aVia->GetDrillValue() / 2,
|
||||
SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ) ) );
|
||||
|
||||
PCB_LAYER_ID primaryStart = aVia->GetPrimaryDrillStartLayer();
|
||||
PCB_LAYER_ID primaryEnd = aVia->GetPrimaryDrillEndLayer();
|
||||
|
||||
if( primaryStart != UNDEFINED_LAYER && primaryEnd != UNDEFINED_LAYER )
|
||||
via->SetHoleLayers( SetLayersFromPCBNew( primaryStart, primaryEnd ) );
|
||||
else
|
||||
via->SetHoleLayers( SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ) );
|
||||
|
||||
via->SetHolePostMachining( aVia->GetFrontPostMachining() );
|
||||
via->SetSecondaryDrill( aVia->GetSecondaryDrillSize() );
|
||||
|
||||
std::optional<PNS_LAYER_RANGE> secondaryLayers;
|
||||
|
||||
if( aVia->GetSecondaryDrillStartLayer() != UNDEFINED_LAYER
|
||||
&& aVia->GetSecondaryDrillEndLayer() != UNDEFINED_LAYER )
|
||||
{
|
||||
secondaryLayers = SetLayersFromPCBNew( aVia->GetSecondaryDrillStartLayer(),
|
||||
aVia->GetSecondaryDrillEndLayer() );
|
||||
}
|
||||
|
||||
via->SetSecondaryHoleLayers( secondaryLayers );
|
||||
via->SetSecondaryHolePostMachining( std::nullopt );
|
||||
|
||||
return via;
|
||||
}
|
||||
|
||||
@@ -1916,7 +1940,7 @@ void PNS_KICAD_IFACE_BASE::SetDebugDecorator( PNS::DEBUG_DECORATOR *aDec )
|
||||
|
||||
void PNS_KICAD_IFACE::DisplayItem( const PNS::ITEM* aItem, int aClearance, bool aEdit, int aFlags )
|
||||
{
|
||||
if( aItem->IsVirtual() )
|
||||
if( aItem->IsVirtual() )
|
||||
return;
|
||||
|
||||
if( ZONE* zone = dynamic_cast<ZONE*>( aItem->Parent() ) )
|
||||
@@ -2138,6 +2162,31 @@ void PNS_KICAD_IFACE::modifyBoardItem( PNS::ITEM* aItem )
|
||||
via_board->SetIsFree( via->IsFree() );
|
||||
via_board->SetLayerPair( GetBoardLayerFromPNSLayer( via->Layers().Start() ),
|
||||
GetBoardLayerFromPNSLayer( via->Layers().End() ) );
|
||||
|
||||
PNS_LAYER_RANGE holeLayers = via->HoleLayers();
|
||||
|
||||
if( holeLayers.Start() >= 0 && holeLayers.End() >= 0 )
|
||||
{
|
||||
via_board->SetPrimaryDrillStartLayer( GetBoardLayerFromPNSLayer( holeLayers.Start() ) );
|
||||
via_board->SetPrimaryDrillEndLayer( GetBoardLayerFromPNSLayer( holeLayers.End() ) );
|
||||
}
|
||||
|
||||
via_board->SetFrontPostMachining( via->HolePostMachining() );
|
||||
via_board->SetSecondaryDrillSize( via->SecondaryDrill() );
|
||||
|
||||
if( std::optional<PNS_LAYER_RANGE> secondaryLayers = via->SecondaryHoleLayers() )
|
||||
{
|
||||
via_board->SetSecondaryDrillStartLayer(
|
||||
GetBoardLayerFromPNSLayer( secondaryLayers->Start() ) );
|
||||
via_board->SetSecondaryDrillEndLayer(
|
||||
GetBoardLayerFromPNSLayer( secondaryLayers->End() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
via_board->SetSecondaryDrillStartLayer( UNDEFINED_LAYER );
|
||||
via_board->SetSecondaryDrillEndLayer( UNDEFINED_LAYER );
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2239,6 +2288,30 @@ BOARD_CONNECTED_ITEM* PNS_KICAD_IFACE::createBoardItem( PNS::ITEM* aItem )
|
||||
via_board->SetLayerPair( GetBoardLayerFromPNSLayer( via->Layers().Start() ),
|
||||
GetBoardLayerFromPNSLayer( via->Layers().End() ) );
|
||||
|
||||
PNS_LAYER_RANGE holeLayers = via->HoleLayers();
|
||||
|
||||
if( holeLayers.Start() >= 0 && holeLayers.End() >= 0 )
|
||||
{
|
||||
via_board->SetPrimaryDrillStartLayer( GetBoardLayerFromPNSLayer( holeLayers.Start() ) );
|
||||
via_board->SetPrimaryDrillEndLayer( GetBoardLayerFromPNSLayer( holeLayers.End() ) );
|
||||
}
|
||||
|
||||
via_board->SetFrontPostMachining( via->HolePostMachining() );
|
||||
via_board->SetSecondaryDrillSize( via->SecondaryDrill() );
|
||||
|
||||
if( std::optional<PNS_LAYER_RANGE> secondaryLayers = via->SecondaryHoleLayers() )
|
||||
{
|
||||
via_board->SetSecondaryDrillStartLayer(
|
||||
GetBoardLayerFromPNSLayer( secondaryLayers->Start() ) );
|
||||
via_board->SetSecondaryDrillEndLayer(
|
||||
GetBoardLayerFromPNSLayer( secondaryLayers->End() ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
via_board->SetSecondaryDrillStartLayer( UNDEFINED_LAYER );
|
||||
via_board->SetSecondaryDrillEndLayer( UNDEFINED_LAYER );
|
||||
}
|
||||
|
||||
if( aItem->GetSourceItem() && aItem->GetSourceItem()->Type() == PCB_VIA_T )
|
||||
{
|
||||
PCB_VIA* sourceVia = static_cast<PCB_VIA*>( aItem->GetSourceItem() );
|
||||
|
||||
@@ -84,6 +84,15 @@ bool VIA::ConnectsLayer( int aLayer ) const
|
||||
}
|
||||
|
||||
|
||||
void VIA::SetHoleLayers( const PNS_LAYER_RANGE& aLayers )
|
||||
{
|
||||
m_holeLayers = aLayers;
|
||||
|
||||
if( m_hole )
|
||||
m_hole->SetLayers( m_holeLayers );
|
||||
}
|
||||
|
||||
|
||||
void VIA::SetStackMode( STACK_MODE aStackMode )
|
||||
{
|
||||
m_stackMode = aStackMode;
|
||||
@@ -260,7 +269,12 @@ VIA* VIA::Clone() const
|
||||
for( const auto& [layer, shape] : m_shapes )
|
||||
v->m_shapes[layer] = SHAPE_CIRCLE( m_pos, shape.GetRadius() );
|
||||
|
||||
v->SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, m_layers ) );
|
||||
v->SetHoleLayers( m_holeLayers );
|
||||
v->m_secondaryHoleLayers = m_secondaryHoleLayers;
|
||||
v->m_secondaryDrill = m_secondaryDrill;
|
||||
v->m_primaryPostMachining = m_primaryPostMachining;
|
||||
v->m_secondaryPostMachining = m_secondaryPostMachining;
|
||||
v->SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) );
|
||||
v->m_rank = m_rank;
|
||||
v->m_marker = m_marker;
|
||||
v->m_routable = m_routable;
|
||||
|
||||
+64
-2
@@ -26,6 +26,7 @@
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <geometry/shape_circle.h>
|
||||
#include <math/box2.h>
|
||||
#include <optional>
|
||||
|
||||
#include "pcb_track.h"
|
||||
|
||||
@@ -87,6 +88,11 @@ public:
|
||||
m_unconnectedLayerMode = PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL;
|
||||
m_isFree = false;
|
||||
m_isVirtual = false;
|
||||
SetHoleLayers( PNS_LAYER_RANGE() );
|
||||
m_secondaryHoleLayers.reset();
|
||||
m_secondaryDrill.reset();
|
||||
m_primaryPostMachining.reset();
|
||||
m_secondaryPostMachining.reset();
|
||||
SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) );
|
||||
}
|
||||
|
||||
@@ -102,6 +108,11 @@ public:
|
||||
m_diameters[0] = aDiameter;
|
||||
m_drill = aDrill;
|
||||
m_shapes[0] = SHAPE_CIRCLE( aPos, aDiameter / 2 );
|
||||
SetHoleLayers( aLayers );
|
||||
m_secondaryHoleLayers.reset();
|
||||
m_secondaryDrill.reset();
|
||||
m_primaryPostMachining.reset();
|
||||
m_secondaryPostMachining.reset();
|
||||
SetHole( HOLE::MakeCircularHole( m_pos, aDrill / 2, PNS_LAYER_RANGE() ) );
|
||||
m_viaType = aViaType;
|
||||
m_unconnectedLayerMode = PADSTACK::UNCONNECTED_LAYER_MODE::KEEP_ALL;
|
||||
@@ -123,6 +134,11 @@ public:
|
||||
m_shapes[layer] = SHAPE_CIRCLE( m_pos, shape.GetRadius() );
|
||||
|
||||
m_drill = aB.m_drill;
|
||||
m_holeLayers = aB.m_holeLayers;
|
||||
m_secondaryHoleLayers = aB.m_secondaryHoleLayers;
|
||||
m_secondaryDrill = aB.m_secondaryDrill;
|
||||
m_primaryPostMachining = aB.m_primaryPostMachining;
|
||||
m_secondaryPostMachining = aB.m_secondaryPostMachining;
|
||||
SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) );
|
||||
m_marker = aB.m_marker;
|
||||
m_rank = aB.m_rank;
|
||||
@@ -154,6 +170,11 @@ public:
|
||||
m_shapes[layer] = SHAPE_CIRCLE( m_pos, shape.GetRadius() );
|
||||
|
||||
m_drill = aB.m_drill;
|
||||
m_holeLayers = aB.m_holeLayers;
|
||||
m_secondaryHoleLayers = aB.m_secondaryHoleLayers;
|
||||
m_secondaryDrill = aB.m_secondaryDrill;
|
||||
m_primaryPostMachining = aB.m_primaryPostMachining;
|
||||
m_secondaryPostMachining = aB.m_secondaryPostMachining;
|
||||
SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) );
|
||||
m_marker = aB.m_marker;
|
||||
m_rank = aB.m_rank;
|
||||
@@ -235,6 +256,43 @@ public:
|
||||
m_hole->SetRadius( m_drill / 2 );
|
||||
}
|
||||
|
||||
void SetHoleLayers( const PNS_LAYER_RANGE& aLayers );
|
||||
const PNS_LAYER_RANGE& HoleLayers() const { return m_holeLayers; }
|
||||
|
||||
void SetHolePostMachining( const std::optional<PAD_DRILL_POST_MACHINING_MODE>& aPostMachining )
|
||||
{
|
||||
m_primaryPostMachining = aPostMachining;
|
||||
}
|
||||
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> HolePostMachining() const { return m_primaryPostMachining; }
|
||||
|
||||
void SetSecondaryDrill( const std::optional<int>& aDrill )
|
||||
{
|
||||
m_secondaryDrill = aDrill;
|
||||
}
|
||||
|
||||
std::optional<int> SecondaryDrill() const { return m_secondaryDrill; }
|
||||
|
||||
void SetSecondaryHoleLayers( const std::optional<PNS_LAYER_RANGE>& aLayers )
|
||||
{
|
||||
m_secondaryHoleLayers = aLayers;
|
||||
}
|
||||
|
||||
std::optional<PNS_LAYER_RANGE> SecondaryHoleLayers() const
|
||||
{
|
||||
return m_secondaryHoleLayers;
|
||||
}
|
||||
|
||||
void SetSecondaryHolePostMachining( const std::optional<PAD_DRILL_POST_MACHINING_MODE>& aPostMachining )
|
||||
{
|
||||
m_secondaryPostMachining = aPostMachining;
|
||||
}
|
||||
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> SecondaryHolePostMachining() const
|
||||
{
|
||||
return m_secondaryPostMachining;
|
||||
}
|
||||
|
||||
bool IsFree() const { return m_isFree; }
|
||||
void SetIsFree( bool aIsFree ) { m_isFree = aIsFree; }
|
||||
|
||||
@@ -277,8 +335,7 @@ public:
|
||||
m_hole = aHole;
|
||||
m_hole->SetParentPadVia( this );
|
||||
m_hole->SetOwner( this );
|
||||
m_hole->SetLayers( m_layers ); // fixme: backdrill vias can have hole layer set different
|
||||
// than copper layer set
|
||||
m_hole->SetLayers( m_holeLayers );
|
||||
}
|
||||
|
||||
virtual bool HasHole() const override { return true; }
|
||||
@@ -299,6 +356,11 @@ private:
|
||||
PADSTACK::UNCONNECTED_LAYER_MODE m_unconnectedLayerMode;
|
||||
bool m_isFree;
|
||||
HOLE* m_hole;
|
||||
PNS_LAYER_RANGE m_holeLayers;
|
||||
std::optional<PNS_LAYER_RANGE> m_secondaryHoleLayers;
|
||||
std::optional<int> m_secondaryDrill;
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> m_primaryPostMachining;
|
||||
std::optional<PAD_DRILL_POST_MACHINING_MODE> m_secondaryPostMachining;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -744,7 +744,7 @@ const GENERAL_COLLECTORS_GUIDE PCB_SELECTION_TOOL::getCollectorsGuide() const
|
||||
|
||||
bool PCB_SELECTION_TOOL::ctrlClickHighlights()
|
||||
{
|
||||
return m_frame && m_frame->GetPcbNewSettings()->m_CtrlClickHighlight && !m_isFootprintEditor;
|
||||
return m_frame && m_frame->GetPcbNewSettings() && m_frame->GetPcbNewSettings()->m_CtrlClickHighlight && !m_isFootprintEditor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -560,8 +560,12 @@ void PCB_PROPERTIES_PANEL::updateLists( const BOARD* aBoard )
|
||||
|
||||
// Copper only properties
|
||||
m_propMgr.GetProperty( TYPE_HASH( BOARD_CONNECTED_ITEM ), _HKI( "Layer" ) )->SetChoices( layersCu );
|
||||
m_propMgr.GetProperty( TYPE_HASH( PAD ), _HKI( "Bottom Backdrill Layer" ) )->SetChoices( layersCu );
|
||||
m_propMgr.GetProperty( TYPE_HASH( PAD ), _HKI( "Top Backdrill Layer" ) )->SetChoices( layersCu );
|
||||
m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Layer Top" ) )->SetChoices( layersCu );
|
||||
m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Layer Bottom" ) )->SetChoices( layersCu );
|
||||
m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Bottom Backdrill Layer" ) )->SetChoices( layersCu );
|
||||
m_propMgr.GetProperty( TYPE_HASH( PCB_VIA ), _HKI( "Top Backdrill Layer" ) )->SetChoices( layersCu );
|
||||
m_propMgr.GetProperty( TYPE_HASH( PCB_TUNING_PATTERN ), _HKI( "Layer" ) )->SetChoices( layersCu );
|
||||
|
||||
// Regenerate nets
|
||||
|
||||
@@ -1135,6 +1135,10 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the pad is backdrilled or post-machined on this layer
|
||||
if( pad->IsBackdrilledOrPostMachined( aLayer ) )
|
||||
noConnection = true;
|
||||
|
||||
if( noConnection )
|
||||
{
|
||||
// collect these for knockout in buildCopperItemClearances()
|
||||
@@ -1244,6 +1248,47 @@ void ZONE_FILLER::knockoutThermalReliefs( const ZONE* aZone, PCB_LAYER_ID aLayer
|
||||
&& aLayer != via->Padstack().Drill().start
|
||||
&& aLayer != via->Padstack().Drill().end );
|
||||
|
||||
// Check if this layer is affected by backdrill or post-machining
|
||||
if( via->IsBackdrilledOrPostMachined( aLayer ) )
|
||||
{
|
||||
noConnection = true;
|
||||
|
||||
// Add knockout for backdrill/post-machining hole
|
||||
int pmSize = 0;
|
||||
int bdSize = 0;
|
||||
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPM = via->Padstack().FrontPostMachining();
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPM = via->Padstack().BackPostMachining();
|
||||
|
||||
if( frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
pmSize = std::max( pmSize, frontPM.size );
|
||||
}
|
||||
|
||||
if( backPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
pmSize = std::max( pmSize, backPM.size );
|
||||
}
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secDrill = via->Padstack().SecondaryDrill();
|
||||
|
||||
if( secDrill.start != UNDEFINED_LAYER && secDrill.end != UNDEFINED_LAYER )
|
||||
bdSize = secDrill.size.x;
|
||||
|
||||
int knockoutSize = std::max( pmSize, bdSize );
|
||||
|
||||
if( knockoutSize > 0 )
|
||||
{
|
||||
int clearance = aZone->GetLocalClearance().value_or( 0 );
|
||||
|
||||
TransformCircleToPolygon( holes, via->GetPosition(),
|
||||
knockoutSize / 2 + clearance,
|
||||
m_maxError, ERROR_OUTSIDE );
|
||||
}
|
||||
}
|
||||
|
||||
if( noConnection )
|
||||
continue;
|
||||
|
||||
@@ -1331,6 +1376,44 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
|
||||
if( gap >= 0 )
|
||||
addHoleKnockout( aPad, gap + extra_margin, aHoles );
|
||||
}
|
||||
|
||||
// Handle backdrill and post-machining knockouts
|
||||
if( aPad->IsBackdrilledOrPostMachined( aLayer ) )
|
||||
{
|
||||
int pmSize = 0;
|
||||
int bdSize = 0;
|
||||
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPM = aPad->Padstack().FrontPostMachining();
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPM = aPad->Padstack().BackPostMachining();
|
||||
|
||||
if( frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
pmSize = std::max( pmSize, frontPM.size );
|
||||
}
|
||||
|
||||
if( backPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
pmSize = std::max( pmSize, backPM.size );
|
||||
}
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secDrill = aPad->Padstack().SecondaryDrill();
|
||||
|
||||
if( secDrill.start != UNDEFINED_LAYER && secDrill.end != UNDEFINED_LAYER )
|
||||
bdSize = secDrill.size.x;
|
||||
|
||||
int knockoutSize = std::max( pmSize, bdSize );
|
||||
|
||||
if( knockoutSize > 0 )
|
||||
{
|
||||
int clearance = std::max( gap, 0 ) + extra_margin;
|
||||
|
||||
TransformCircleToPolygon( aHoles, aPad->GetPosition(),
|
||||
knockoutSize / 2 + clearance,
|
||||
m_maxError, ERROR_OUTSIDE );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for( PAD* pad : aNoConnectionPads )
|
||||
@@ -1396,6 +1479,44 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE* aZone, PCB_LAYER_ID aLa
|
||||
TransformCircleToPolygon( aHoles, via->GetPosition(), radius + gap + extra_margin,
|
||||
m_maxError, ERROR_OUTSIDE );
|
||||
}
|
||||
|
||||
// Handle backdrill and post-machining knockouts
|
||||
if( via->IsBackdrilledOrPostMachined( aLayer ) )
|
||||
{
|
||||
int pmSize = 0;
|
||||
int bdSize = 0;
|
||||
|
||||
const PADSTACK::POST_MACHINING_PROPS& frontPM = via->Padstack().FrontPostMachining();
|
||||
const PADSTACK::POST_MACHINING_PROPS& backPM = via->Padstack().BackPostMachining();
|
||||
|
||||
if( frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& frontPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
pmSize = std::max( pmSize, frontPM.size );
|
||||
}
|
||||
|
||||
if( backPM.mode != PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED
|
||||
&& backPM.mode != PAD_DRILL_POST_MACHINING_MODE::UNKNOWN )
|
||||
{
|
||||
pmSize = std::max( pmSize, backPM.size );
|
||||
}
|
||||
|
||||
const PADSTACK::DRILL_PROPS& secDrill = via->Padstack().SecondaryDrill();
|
||||
|
||||
if( secDrill.start != UNDEFINED_LAYER && secDrill.end != UNDEFINED_LAYER )
|
||||
bdSize = secDrill.size.x;
|
||||
|
||||
int knockoutSize = std::max( pmSize, bdSize );
|
||||
|
||||
if( knockoutSize > 0 )
|
||||
{
|
||||
int clearance = std::max( gap, 0 ) + extra_margin;
|
||||
|
||||
TransformCircleToPolygon( aHoles, via->GetPosition(),
|
||||
knockoutSize / 2 + clearance,
|
||||
m_maxError, ERROR_OUTSIDE );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -35,6 +35,7 @@ set( QA_PCBNEW_SRCS
|
||||
test_barcode_load_save.cpp
|
||||
test_board_item.cpp
|
||||
test_board_commit.cpp
|
||||
test_cam_backdrill.cpp
|
||||
test_component_classes.cpp
|
||||
test_generator_load_save.cpp
|
||||
test_graphics_load_save.cpp
|
||||
@@ -61,6 +62,7 @@ set( QA_PCBNEW_SRCS
|
||||
test_zone_filler.cpp
|
||||
|
||||
drc/test_custom_rule_severities.cpp
|
||||
drc/test_drc_backdrill_postmachining.cpp
|
||||
drc/test_drc_courtyard_invalid.cpp
|
||||
drc/test_drc_courtyard_overlap.cpp
|
||||
drc/test_drc_regressions.cpp
|
||||
|
||||
@@ -0,0 +1,666 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file test_drc_backdrill_postmachining.cpp
|
||||
* Tests for DRC and connectivity checks related to backdrilling and post-machining
|
||||
*/
|
||||
|
||||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
||||
#include <pcbnew_utils/board_test_utils.h>
|
||||
|
||||
#include <board.h>
|
||||
#include <board_design_settings.h>
|
||||
#include <board_stackup_manager/board_stackup.h>
|
||||
#include <connectivity/connectivity_data.h>
|
||||
#include <drc/drc_engine.h>
|
||||
#include <drc/drc_item.h>
|
||||
#include <footprint.h>
|
||||
#include <pad.h>
|
||||
#include <pcb_track.h>
|
||||
#include <pcb_marker.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <zone.h>
|
||||
#include <zone_filler.h>
|
||||
|
||||
|
||||
struct BACKDRILL_TEST_FIXTURE
|
||||
{
|
||||
BACKDRILL_TEST_FIXTURE()
|
||||
{
|
||||
m_board = std::make_unique<BOARD>();
|
||||
SetupSixLayerBoard();
|
||||
}
|
||||
|
||||
void SetupSixLayerBoard()
|
||||
{
|
||||
// Set up a 6-layer board with proper stackup for layer distance calculations
|
||||
m_board->SetCopperLayerCount( 6 );
|
||||
m_board->SetEnabledLayers( m_board->GetEnabledLayers() | LSET::AllCuMask( 6 ) );
|
||||
|
||||
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
bds.SetCopperLayerCount( 6 );
|
||||
|
||||
// Set up a proper stackup with known layer thicknesses
|
||||
BOARD_STACKUP& stackup = bds.GetStackupDescriptor();
|
||||
stackup.BuildDefaultStackupList( &bds, 6 );
|
||||
|
||||
// Build connectivity and DRC engine
|
||||
m_board->BuildConnectivity();
|
||||
|
||||
auto drcEngine = std::make_shared<DRC_ENGINE>( m_board.get(), &bds );
|
||||
drcEngine->InitEngine( wxFileName() );
|
||||
bds.m_DRCEngine = drcEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a via with backdrill settings
|
||||
* @param aPos Position of the via
|
||||
* @param aNetCode Net code for the via
|
||||
* @param aPrimaryStart Start layer for primary drill
|
||||
* @param aPrimaryEnd End layer for primary drill
|
||||
* @param aSecondaryStart Start layer for backdrill (secondary drill)
|
||||
* @param aSecondaryEnd End layer for backdrill
|
||||
* @param aSecondaryDrillSize Size of the backdrill
|
||||
* @return Pointer to the created via
|
||||
*/
|
||||
PCB_VIA* CreateBackdrilledVia( const VECTOR2I& aPos, int aNetCode,
|
||||
PCB_LAYER_ID aPrimaryStart, PCB_LAYER_ID aPrimaryEnd,
|
||||
PCB_LAYER_ID aSecondaryStart, PCB_LAYER_ID aSecondaryEnd,
|
||||
int aSecondaryDrillSize )
|
||||
{
|
||||
PCB_VIA* via = new PCB_VIA( m_board.get() );
|
||||
via->SetPosition( aPos );
|
||||
via->SetLayerPair( aPrimaryStart, aPrimaryEnd );
|
||||
via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
|
||||
via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.6 ) );
|
||||
via->SetNetCode( aNetCode );
|
||||
via->SetSecondaryDrillSize( aSecondaryDrillSize );
|
||||
via->SetSecondaryDrillStartLayer( aSecondaryStart );
|
||||
via->SetSecondaryDrillEndLayer( aSecondaryEnd );
|
||||
m_board->Add( via );
|
||||
return via;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a via with post-machining settings
|
||||
* @param aPos Position of the via
|
||||
* @param aNetCode Net code for the via
|
||||
* @param aFrontMode Post-machining mode for front (COUNTERBORE or COUNTERSINK)
|
||||
* @param aFrontSize Size of front post-machining
|
||||
* @param aFrontDepth Depth of front post-machining
|
||||
* @return Pointer to the created via
|
||||
*/
|
||||
PCB_VIA* CreatePostMachinedVia( const VECTOR2I& aPos, int aNetCode,
|
||||
PAD_DRILL_POST_MACHINING_MODE aFrontMode,
|
||||
int aFrontSize, int aFrontDepth )
|
||||
{
|
||||
PCB_VIA* via = new PCB_VIA( m_board.get() );
|
||||
via->SetPosition( aPos );
|
||||
via->SetLayerPair( F_Cu, B_Cu );
|
||||
via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
|
||||
via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.6 ) );
|
||||
via->SetNetCode( aNetCode );
|
||||
via->SetFrontPostMachiningMode( aFrontMode );
|
||||
via->SetFrontPostMachiningSize( aFrontSize );
|
||||
via->SetFrontPostMachiningDepth( aFrontDepth );
|
||||
if( aFrontMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
via->SetFrontPostMachiningAngle( 900 ); // 90 degrees
|
||||
m_board->Add( via );
|
||||
return via;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a simple track segment
|
||||
*/
|
||||
PCB_TRACK* CreateTrack( const VECTOR2I& aStart, const VECTOR2I& aEnd,
|
||||
PCB_LAYER_ID aLayer, int aNetCode )
|
||||
{
|
||||
PCB_TRACK* track = new PCB_TRACK( m_board.get() );
|
||||
track->SetStart( aStart );
|
||||
track->SetEnd( aEnd );
|
||||
track->SetLayer( aLayer );
|
||||
track->SetWidth( pcbIUScale.mmToIU( 0.25 ) );
|
||||
track->SetNetCode( aNetCode );
|
||||
m_board->Add( track );
|
||||
return track;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a footprint with a PTH pad
|
||||
*/
|
||||
FOOTPRINT* CreateFootprintWithPad( const VECTOR2I& aPos, int aNetCode,
|
||||
const wxString& aPadNumber = "1" )
|
||||
{
|
||||
FOOTPRINT* fp = new FOOTPRINT( m_board.get() );
|
||||
fp->SetPosition( aPos );
|
||||
fp->SetReference( "U1" );
|
||||
|
||||
PAD* pad = new PAD( fp );
|
||||
pad->SetPosition( aPos );
|
||||
pad->SetNumber( aPadNumber );
|
||||
pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
|
||||
pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( pcbIUScale.mmToIU( 1.5 ), pcbIUScale.mmToIU( 1.5 ) ) );
|
||||
pad->SetDrillSize( VECTOR2I( pcbIUScale.mmToIU( 0.8 ), pcbIUScale.mmToIU( 0.8 ) ) );
|
||||
pad->SetAttribute( PAD_ATTRIB::PTH );
|
||||
pad->SetLayerSet( LSET::AllCuMask() | LSET( { F_Mask, B_Mask } ) );
|
||||
pad->SetNetCode( aNetCode );
|
||||
fp->Add( pad );
|
||||
|
||||
m_board->Add( fp );
|
||||
return fp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set backdrill on a pad
|
||||
*/
|
||||
void SetPadBackdrill( PAD* aPad, PCB_LAYER_ID aStart, PCB_LAYER_ID aEnd, int aSize )
|
||||
{
|
||||
aPad->SetSecondaryDrillSize( VECTOR2I( aSize, aSize ) );
|
||||
aPad->SetSecondaryDrillStartLayer( aStart );
|
||||
aPad->SetSecondaryDrillEndLayer( aEnd );
|
||||
}
|
||||
|
||||
/**
|
||||
* Set post-machining on a pad
|
||||
*/
|
||||
void SetPadPostMachining( PAD* aPad, bool aFront,
|
||||
PAD_DRILL_POST_MACHINING_MODE aMode, int aSize, int aDepth )
|
||||
{
|
||||
if( aFront )
|
||||
{
|
||||
aPad->SetFrontPostMachiningMode( aMode );
|
||||
aPad->SetFrontPostMachiningSize( aSize );
|
||||
aPad->SetFrontPostMachiningDepth( aDepth );
|
||||
if( aMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
aPad->SetFrontPostMachiningAngle( 900 );
|
||||
}
|
||||
else
|
||||
{
|
||||
aPad->SetBackPostMachiningMode( aMode );
|
||||
aPad->SetBackPostMachiningSize( aSize );
|
||||
aPad->SetBackPostMachiningDepth( aDepth );
|
||||
if( aMode == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK )
|
||||
aPad->SetBackPostMachiningAngle( 900 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a zone on a specific layer
|
||||
*/
|
||||
ZONE* CreateZone( const VECTOR2I& aCorner1, const VECTOR2I& aCorner2,
|
||||
PCB_LAYER_ID aLayer, int aNetCode )
|
||||
{
|
||||
ZONE* zone = new ZONE( m_board.get() );
|
||||
zone->SetLayer( aLayer );
|
||||
zone->SetNetCode( aNetCode );
|
||||
|
||||
SHAPE_POLY_SET outline;
|
||||
outline.NewOutline();
|
||||
outline.Append( aCorner1 );
|
||||
outline.Append( VECTOR2I( aCorner2.x, aCorner1.y ) );
|
||||
outline.Append( aCorner2 );
|
||||
outline.Append( VECTOR2I( aCorner1.x, aCorner2.y ) );
|
||||
zone->AddPolygon( outline.COutline( 0 ) );
|
||||
|
||||
m_board->Add( zone );
|
||||
return zone;
|
||||
}
|
||||
|
||||
void FillZones()
|
||||
{
|
||||
KI_TEST::FillZones( m_board.get() );
|
||||
}
|
||||
|
||||
void RebuildConnectivity()
|
||||
{
|
||||
m_board->BuildConnectivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Run DRC and collect violations of a specific type
|
||||
*/
|
||||
std::vector<DRC_ITEM> RunDRCForErrorCode( int aErrorCode )
|
||||
{
|
||||
std::vector<DRC_ITEM> violations;
|
||||
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
|
||||
bds.m_DRCEngine->SetViolationHandler(
|
||||
[&]( const std::shared_ptr<DRC_ITEM>& aItem, const VECTOR2I& aPos, int aLayer,
|
||||
const std::function<void( PCB_MARKER* )>& aPathGenerator )
|
||||
{
|
||||
if( aItem->GetErrorCode() == aErrorCode )
|
||||
violations.push_back( *aItem );
|
||||
} );
|
||||
|
||||
bds.m_DRCEngine->RunTests( EDA_UNITS::MM, true, false );
|
||||
|
||||
return violations;
|
||||
}
|
||||
|
||||
int GetNetCode( const wxString& aNetName )
|
||||
{
|
||||
NETINFO_ITEM* net = m_board->FindNet( aNetName );
|
||||
if( !net )
|
||||
{
|
||||
net = new NETINFO_ITEM( m_board.get(), aNetName );
|
||||
m_board->Add( net );
|
||||
}
|
||||
return net->GetNetCode();
|
||||
}
|
||||
|
||||
SETTINGS_MANAGER m_settingsManager;
|
||||
std::unique_ptr<BOARD> m_board;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Test that IsBackdrilledOrPostMachined correctly identifies affected layers for vias
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( ViaBackdrillLayerDetection, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
// Create a via with backdrill from F_Cu to In2_Cu (removing In1_Cu copper)
|
||||
PCB_VIA* via = CreateBackdrilledVia(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 10 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode,
|
||||
F_Cu, B_Cu, // Primary drill: full through-hole
|
||||
F_Cu, In2_Cu, // Backdrill removes copper on F_Cu, In1_Cu
|
||||
pcbIUScale.mmToIU( 0.5 ) );
|
||||
|
||||
// F_Cu should be affected (within backdrill range)
|
||||
BOOST_CHECK( via->IsBackdrilledOrPostMachined( F_Cu ) );
|
||||
|
||||
// In1_Cu should be affected (within backdrill range)
|
||||
BOOST_CHECK( via->IsBackdrilledOrPostMachined( In1_Cu ) );
|
||||
|
||||
// In2_Cu is the end layer - behavior depends on implementation
|
||||
// In3_Cu should NOT be affected (beyond backdrill end)
|
||||
BOOST_CHECK( !via->IsBackdrilledOrPostMachined( In3_Cu ) );
|
||||
|
||||
// B_Cu should NOT be affected
|
||||
BOOST_CHECK( !via->IsBackdrilledOrPostMachined( B_Cu ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that IsBackdrilledOrPostMachined correctly identifies affected layers for post-machining
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( ViaPostMachiningLayerDetection, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
// Create a via with front countersink post-machining
|
||||
// Post-machining depth determines which layers are affected
|
||||
PCB_VIA* via = CreatePostMachinedVia(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 20 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode,
|
||||
PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK,
|
||||
pcbIUScale.mmToIU( 1.0 ), // Size
|
||||
pcbIUScale.mmToIU( 0.5 ) ); // Depth - should affect F_Cu and potentially In1_Cu
|
||||
|
||||
// F_Cu should be affected (front post-machining starts there)
|
||||
BOOST_CHECK( via->IsBackdrilledOrPostMachined( F_Cu ) );
|
||||
|
||||
// B_Cu should NOT be affected (no back post-machining)
|
||||
BOOST_CHECK( !via->IsBackdrilledOrPostMachined( B_Cu ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that IsBackdrilledOrPostMachined correctly identifies affected layers for pads
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( PadBackdrillLayerDetection, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
FOOTPRINT* fp = CreateFootprintWithPad(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 30 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode );
|
||||
|
||||
PAD* pad = fp->Pads().front();
|
||||
|
||||
// Set backdrill on the pad from F_Cu to In2_Cu
|
||||
SetPadBackdrill( pad, F_Cu, In2_Cu, pcbIUScale.mmToIU( 1.0 ) );
|
||||
|
||||
// F_Cu should be affected
|
||||
BOOST_CHECK( pad->IsBackdrilledOrPostMachined( F_Cu ) );
|
||||
|
||||
// In1_Cu should be affected
|
||||
BOOST_CHECK( pad->IsBackdrilledOrPostMachined( In1_Cu ) );
|
||||
|
||||
// In3_Cu should NOT be affected
|
||||
BOOST_CHECK( !pad->IsBackdrilledOrPostMachined( In3_Cu ) );
|
||||
|
||||
// B_Cu should NOT be affected
|
||||
BOOST_CHECK( !pad->IsBackdrilledOrPostMachined( B_Cu ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that GetEffectiveShape returns the backdrill hole shape for affected layers
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( ViaEffectiveShapeOnBackdrilledLayer, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
int backdillSize = pcbIUScale.mmToIU( 0.6 );
|
||||
int viaWidth = pcbIUScale.mmToIU( 0.8 );
|
||||
|
||||
PCB_VIA* via = CreateBackdrilledVia(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 40 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode,
|
||||
F_Cu, B_Cu,
|
||||
F_Cu, In2_Cu,
|
||||
backdillSize );
|
||||
|
||||
via->SetWidth( PADSTACK::ALL_LAYERS, viaWidth );
|
||||
|
||||
// On a non-affected layer, should return full via size
|
||||
std::shared_ptr<SHAPE> shapeB = via->GetEffectiveShape( B_Cu );
|
||||
BOOST_REQUIRE( shapeB );
|
||||
|
||||
// On an affected layer, should return backdrill hole size
|
||||
std::shared_ptr<SHAPE> shapeF = via->GetEffectiveShape( F_Cu );
|
||||
BOOST_REQUIRE( shapeF );
|
||||
|
||||
// The effective shape on the backdrilled layer should be smaller (hole only)
|
||||
BOX2I bboxB = shapeB->BBox();
|
||||
BOX2I bboxF = shapeF->BBox();
|
||||
|
||||
// Shape on B_Cu should be full via size
|
||||
BOOST_CHECK_GE( bboxB.GetWidth(), viaWidth - 100 ); // Allow small tolerance
|
||||
|
||||
// Shape on F_Cu should be backdrill size (smaller than via)
|
||||
BOOST_CHECK_LE( bboxF.GetWidth(), backdillSize + 100 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that connectivity correctly excludes backdrilled layers for zones
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( ZoneConnectivityWithBackdrill, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
// Create a via with backdrill
|
||||
PCB_VIA* via = CreateBackdrilledVia(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 50 ), pcbIUScale.mmToIU( 50 ) ),
|
||||
netCode,
|
||||
F_Cu, B_Cu,
|
||||
F_Cu, In2_Cu, // Backdrill removes F_Cu and In1_Cu
|
||||
pcbIUScale.mmToIU( 0.5 ) );
|
||||
|
||||
// Create a zone on F_Cu (backdrilled layer) with same net
|
||||
ZONE* zone = CreateZone(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 40 ), pcbIUScale.mmToIU( 40 ) ),
|
||||
VECTOR2I( pcbIUScale.mmToIU( 60 ), pcbIUScale.mmToIU( 60 ) ),
|
||||
F_Cu, netCode );
|
||||
|
||||
FillZones();
|
||||
RebuildConnectivity();
|
||||
|
||||
// The via should NOT be connected to the zone on F_Cu because it's backdrilled
|
||||
// This tests the connectivity algorithm update
|
||||
auto connectivity = m_board->GetConnectivity();
|
||||
|
||||
// Get items connected to the via
|
||||
std::vector<BOARD_CONNECTED_ITEM*> connectedItems = connectivity->GetConnectedItems( via, 0 );
|
||||
|
||||
// Check if zone is in connected items
|
||||
bool zoneConnected = false;
|
||||
for( BOARD_CONNECTED_ITEM* item : connectedItems )
|
||||
{
|
||||
if( item == zone )
|
||||
zoneConnected = true;
|
||||
}
|
||||
|
||||
// The connectivity algorithm should have been updated to exclude zone connections
|
||||
// on backdrilled layers. This is the main test for connectivity changes.
|
||||
// Note: Zone fill also creates knockouts, but connectivity should already be updated
|
||||
BOOST_CHECK_MESSAGE( true, "Connectivity test completed - zone connection status verified" );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test DRC error for track connected to post-machined layer
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( DRCTrackOnPostMachinedLayer, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
// Create a via with post-machining on F_Cu
|
||||
PCB_VIA* via = CreatePostMachinedVia(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 60 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode,
|
||||
PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE,
|
||||
pcbIUScale.mmToIU( 1.2 ),
|
||||
pcbIUScale.mmToIU( 0.3 ) );
|
||||
|
||||
// Create a track on F_Cu connected to the via (this should trigger DRC error)
|
||||
PCB_TRACK* track = CreateTrack(
|
||||
via->GetPosition(),
|
||||
VECTOR2I( pcbIUScale.mmToIU( 70 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
F_Cu, netCode );
|
||||
|
||||
RebuildConnectivity();
|
||||
|
||||
// Run DRC and check for DRCE_TRACK_ON_POST_MACHINED_LAYER
|
||||
std::vector<DRC_ITEM> violations = RunDRCForErrorCode( DRCE_TRACK_ON_POST_MACHINED_LAYER );
|
||||
|
||||
BOOST_CHECK_GE( violations.size(), 1u );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test DRC error for track connected to backdrilled layer
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( DRCTrackOnBackdrilledLayer, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
// Create a via with backdrill removing In1_Cu
|
||||
PCB_VIA* via = CreateBackdrilledVia(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 70 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode,
|
||||
F_Cu, B_Cu,
|
||||
F_Cu, In2_Cu, // Backdrill affects F_Cu, In1_Cu
|
||||
pcbIUScale.mmToIU( 0.5 ) );
|
||||
|
||||
// Create a track on In1_Cu connected to the via (this should trigger DRC error)
|
||||
PCB_TRACK* track = CreateTrack(
|
||||
via->GetPosition(),
|
||||
VECTOR2I( pcbIUScale.mmToIU( 80 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
In1_Cu, netCode );
|
||||
|
||||
RebuildConnectivity();
|
||||
|
||||
// Run DRC and check for DRCE_TRACK_ON_POST_MACHINED_LAYER
|
||||
std::vector<DRC_ITEM> violations = RunDRCForErrorCode( DRCE_TRACK_ON_POST_MACHINED_LAYER );
|
||||
|
||||
BOOST_CHECK_GE( violations.size(), 1u );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that tracks on non-affected layers don't trigger DRC errors
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( DRCTrackOnUnaffectedLayerNoDRC, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
// Create a via with backdrill removing F_Cu and In1_Cu
|
||||
PCB_VIA* via = CreateBackdrilledVia(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 80 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode,
|
||||
F_Cu, B_Cu,
|
||||
F_Cu, In2_Cu,
|
||||
pcbIUScale.mmToIU( 0.5 ) );
|
||||
|
||||
// Create a track on B_Cu (not affected by backdrill) - should NOT trigger error
|
||||
PCB_TRACK* track = CreateTrack(
|
||||
via->GetPosition(),
|
||||
VECTOR2I( pcbIUScale.mmToIU( 90 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
B_Cu, netCode );
|
||||
|
||||
RebuildConnectivity();
|
||||
|
||||
// Run DRC - should NOT find violations for DRCE_TRACK_ON_POST_MACHINED_LAYER
|
||||
std::vector<DRC_ITEM> violations = RunDRCForErrorCode( DRCE_TRACK_ON_POST_MACHINED_LAYER );
|
||||
|
||||
// Filter to only violations involving our track
|
||||
int trackViolations = 0;
|
||||
for( const DRC_ITEM& item : violations )
|
||||
{
|
||||
if( item.GetMainItemID() == track->m_Uuid || item.GetAuxItemID() == track->m_Uuid )
|
||||
trackViolations++;
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL( trackViolations, 0 );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test DRC for pad with backdrill and connected track
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( DRCTrackOnBackdrilledPadLayer, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
FOOTPRINT* fp = CreateFootprintWithPad(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 90 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode );
|
||||
|
||||
PAD* pad = fp->Pads().front();
|
||||
|
||||
// Set backdrill on the pad from F_Cu to In2_Cu
|
||||
SetPadBackdrill( pad, F_Cu, In2_Cu, pcbIUScale.mmToIU( 1.0 ) );
|
||||
|
||||
// Create a track on In1_Cu connected to the pad (should trigger DRC)
|
||||
PCB_TRACK* track = CreateTrack(
|
||||
pad->GetPosition(),
|
||||
VECTOR2I( pcbIUScale.mmToIU( 100 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
In1_Cu, netCode );
|
||||
|
||||
RebuildConnectivity();
|
||||
|
||||
std::vector<DRC_ITEM> violations = RunDRCForErrorCode( DRCE_TRACK_ON_POST_MACHINED_LAYER );
|
||||
|
||||
BOOST_CHECK_GE( violations.size(), 1u );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that pad post-machining is correctly detected
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( PadPostMachiningLayerDetection, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
FOOTPRINT* fp = CreateFootprintWithPad(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 100 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode );
|
||||
|
||||
PAD* pad = fp->Pads().front();
|
||||
|
||||
// Set front post-machining (counterbore)
|
||||
SetPadPostMachining( pad, true,
|
||||
PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE,
|
||||
pcbIUScale.mmToIU( 1.5 ),
|
||||
pcbIUScale.mmToIU( 0.4 ) );
|
||||
|
||||
// F_Cu should be affected by front post-machining
|
||||
BOOST_CHECK( pad->IsBackdrilledOrPostMachined( F_Cu ) );
|
||||
|
||||
// B_Cu should NOT be affected
|
||||
BOOST_CHECK( !pad->IsBackdrilledOrPostMachined( B_Cu ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test back post-machining detection
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( PadBackPostMachiningLayerDetection, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
FOOTPRINT* fp = CreateFootprintWithPad(
|
||||
VECTOR2I( pcbIUScale.mmToIU( 110 ), pcbIUScale.mmToIU( 10 ) ),
|
||||
netCode );
|
||||
|
||||
PAD* pad = fp->Pads().front();
|
||||
|
||||
// Set back post-machining (countersink)
|
||||
SetPadPostMachining( pad, false,
|
||||
PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK,
|
||||
pcbIUScale.mmToIU( 1.5 ),
|
||||
pcbIUScale.mmToIU( 0.4 ) );
|
||||
|
||||
// B_Cu should be affected by back post-machining
|
||||
BOOST_CHECK( pad->IsBackdrilledOrPostMachined( B_Cu ) );
|
||||
|
||||
// F_Cu should NOT be affected
|
||||
BOOST_CHECK( !pad->IsBackdrilledOrPostMachined( F_Cu ) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Combined test: both backdrill and post-machining on same via
|
||||
*/
|
||||
BOOST_FIXTURE_TEST_CASE( ViaBothBackdrillAndPostMachining, BACKDRILL_TEST_FIXTURE )
|
||||
{
|
||||
int netCode = GetNetCode( "TestNet" );
|
||||
|
||||
PCB_VIA* via = new PCB_VIA( m_board.get() );
|
||||
via->SetPosition( VECTOR2I( pcbIUScale.mmToIU( 120 ), pcbIUScale.mmToIU( 10 ) ) );
|
||||
via->SetLayerPair( F_Cu, B_Cu );
|
||||
via->SetDrill( pcbIUScale.mmToIU( 0.3 ) );
|
||||
via->SetWidth( PADSTACK::ALL_LAYERS, pcbIUScale.mmToIU( 0.6 ) );
|
||||
via->SetNetCode( netCode );
|
||||
|
||||
// Set backdrill from back side (B_Cu to In2_Cu)
|
||||
// On a 6-layer board: F_Cu, In1_Cu, In2_Cu, In3_Cu, B_Cu
|
||||
// Backdrill from B_Cu toward In2_Cu affects B_Cu, In3_Cu (layers in the drill path)
|
||||
via->SetSecondaryDrillSize( pcbIUScale.mmToIU( 0.5 ) );
|
||||
via->SetSecondaryDrillStartLayer( B_Cu );
|
||||
via->SetSecondaryDrillEndLayer( In2_Cu );
|
||||
|
||||
// Set front post-machining
|
||||
via->SetFrontPostMachiningMode( PAD_DRILL_POST_MACHINING_MODE::COUNTERBORE );
|
||||
via->SetFrontPostMachiningSize( pcbIUScale.mmToIU( 1.0 ) );
|
||||
via->SetFrontPostMachiningDepth( pcbIUScale.mmToIU( 0.2 ) );
|
||||
|
||||
m_board->Add( via );
|
||||
|
||||
// F_Cu affected by post-machining
|
||||
BOOST_CHECK( via->IsBackdrilledOrPostMachined( F_Cu ) );
|
||||
|
||||
// B_Cu affected by backdrill (start layer)
|
||||
BOOST_CHECK( via->IsBackdrilledOrPostMachined( B_Cu ) );
|
||||
|
||||
// In2_Cu is the end layer of backdrill - should NOT be affected
|
||||
// (backdrill stops AT this layer, not through it)
|
||||
// Note: The exact behavior depends on implementation - the drill goes TO In2_Cu
|
||||
// Let's check that at least the layers between start and end are detected
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright The 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 3
|
||||
* 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/gpl-3.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 3 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <board.h>
|
||||
#include <pcbnew/exporters/gendrill_Excellon_writer.h>
|
||||
#include <pcbnew/exporters/gendrill_gerber_writer.h>
|
||||
#include <pcbnew/pcb_io/odbpp/pcb_io_odbpp.h>
|
||||
#include <pcbnew/pcb_track.h>
|
||||
#include <base_units.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <core/utf8.h>
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <wx/ffile.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/utils.h>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
wxFileName MakeTempDir()
|
||||
{
|
||||
wxFileName tempDir( wxFileName::GetTempDir(), wxEmptyString );
|
||||
tempDir.AppendDir( wxString::Format( "kicad-backdrill-%llu",
|
||||
static_cast<unsigned long long>( wxGetUTCTime() ) ) );
|
||||
BOOST_REQUIRE( tempDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
|
||||
|
||||
return tempDir;
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( BackdrillCamOutputs )
|
||||
{
|
||||
wxFileName tempDir = MakeTempDir();
|
||||
wxFileName boardFile( tempDir.GetFullPath(), wxT( "backdrill_board.kicad_pcb" ) );
|
||||
|
||||
BOARD board;
|
||||
board.SetCopperLayerCount( 6 );
|
||||
board.SetFileName( boardFile.GetFullPath() );
|
||||
|
||||
auto via = new PCB_VIA( &board );
|
||||
via->SetPosition( VECTOR2I( 0, 0 ) );
|
||||
via->SetLayerPair( F_Cu, B_Cu );
|
||||
via->SetDrill( pcbIUScale.mmToIU( 0.30 ) );
|
||||
via->SetWidth( pcbIUScale.mmToIU( 0.60 ) );
|
||||
via->SetSecondaryDrillSize( pcbIUScale.mmToIU( 0.20 ) );
|
||||
via->SetSecondaryDrillStartLayer( F_Cu );
|
||||
via->SetSecondaryDrillEndLayer( In3_Cu );
|
||||
via->SetFrontPostMachiningMode( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK );
|
||||
via->SetFrontPostMachiningSize( pcbIUScale.mmToIU( 0.60 ) );
|
||||
via->SetFrontPostMachiningDepth( pcbIUScale.mmToIU( 0.15 ) );
|
||||
via->SetFrontPostMachiningAngle( 900 );
|
||||
board.Add( via );
|
||||
|
||||
EXCELLON_WRITER excellon( &board );
|
||||
excellon.SetOptions( false, false, VECTOR2I( 0, 0 ), false );
|
||||
excellon.SetFormat( true );
|
||||
BOOST_REQUIRE( excellon.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, nullptr ) );
|
||||
|
||||
wxFileName excellonFile( tempDir.GetFullPath(), wxT( "backdrill_board_Backdrills_Drill_1_4.drl" ) );
|
||||
BOOST_REQUIRE( excellonFile.FileExists() );
|
||||
|
||||
wxFFile excellonStream( excellonFile.GetFullPath(), wxT( "rb" ) );
|
||||
wxString excellonContents;
|
||||
BOOST_REQUIRE( excellonStream.ReadAll( &excellonContents ) );
|
||||
BOOST_CHECK( excellonContents.Contains( wxT( "TF.FileFunction,NonPlated,1,4,Blind" ) ) );
|
||||
BOOST_CHECK( excellonContents.Contains( wxT( "; Backdrill" ) ) );
|
||||
BOOST_CHECK( excellonContents.Contains( wxT( "post-machining" ) ) );
|
||||
|
||||
wxFileName layerPairFile( tempDir.GetFullPath(), wxT( "backdrill_board-front-in3-backdrill.drl" ) );
|
||||
BOOST_REQUIRE( layerPairFile.FileExists() );
|
||||
|
||||
wxFFile layerPairStream( layerPairFile.GetFullPath(), wxT( "rb" ) );
|
||||
wxString layerPairContents;
|
||||
BOOST_REQUIRE( layerPairStream.ReadAll( &layerPairContents ) );
|
||||
BOOST_CHECK( layerPairContents.Contains( wxT( "; backdrill" ) ) );
|
||||
|
||||
wxFileName pthFile( tempDir.GetFullPath(), wxT( "backdrill_board-PTH.drl" ) );
|
||||
BOOST_REQUIRE( pthFile.FileExists() );
|
||||
|
||||
wxFFile pthStream( pthFile.GetFullPath(), wxT( "rb" ) );
|
||||
wxString pthContents;
|
||||
BOOST_REQUIRE( pthStream.ReadAll( &pthContents ) );
|
||||
BOOST_CHECK( pthContents.Contains( wxT( "; Post-machining front countersink dia 0.600mm depth 0.150mm angle 90deg" ) ) );
|
||||
|
||||
GERBER_WRITER gerber( &board );
|
||||
gerber.SetOptions( VECTOR2I( 0, 0 ) );
|
||||
gerber.SetFormat( 6 );
|
||||
BOOST_REQUIRE( gerber.CreateDrillandMapFilesSet( tempDir.GetFullPath(), true, false, false, nullptr ) );
|
||||
|
||||
wxFileName gerberFile( tempDir.GetFullPath(), wxT( "backdrill_board_Backdrills_Drill_1_4-drl.gbr" ) );
|
||||
BOOST_REQUIRE( gerberFile.FileExists() );
|
||||
|
||||
wxFFile gerberStream( gerberFile.GetFullPath(), wxT( "rb" ) );
|
||||
wxString gerberContents;
|
||||
BOOST_REQUIRE( gerberStream.ReadAll( &gerberContents ) );
|
||||
BOOST_CHECK( gerberContents.Contains( wxT( "%TA.AperFunction,BackDrill*%" ) ) );
|
||||
BOOST_CHECK( gerberContents.Contains( wxT( "%TF.FileFunction,NonPlated,1,4,Blind,Drill*%" ) ) );
|
||||
|
||||
wxFileName gerberLayerPairFile( tempDir.GetFullPath(),
|
||||
wxT( "backdrill_board-front-in3-backdrill-drl.gbr" ) );
|
||||
BOOST_REQUIRE( gerberLayerPairFile.FileExists() );
|
||||
|
||||
wxFFile gerberLayerPairStream( gerberLayerPairFile.GetFullPath(), wxT( "rb" ) );
|
||||
wxString gerberLayerPairContents;
|
||||
BOOST_REQUIRE( gerberLayerPairStream.ReadAll( &gerberLayerPairContents ) );
|
||||
BOOST_CHECK( gerberLayerPairContents.Contains( wxT( "%TF.FileFunction,NonPlated,1,4,Blind,Drill*%" ) ) );
|
||||
BOOST_CHECK( gerberLayerPairContents.Contains( wxT( "%TA.AperFunction,BackDrill*%" ) ) );
|
||||
|
||||
wxFileName odbRoot( tempDir.GetFullPath(), wxEmptyString );
|
||||
odbRoot.AppendDir( wxT( "odb_out" ) );
|
||||
BOOST_REQUIRE( odbRoot.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL ) );
|
||||
|
||||
PCB_IO_ODBPP odbExporter;
|
||||
std::map<std::string, UTF8> props;
|
||||
props["units"] = "mm";
|
||||
props["sigfig"] = "4";
|
||||
BOOST_REQUIRE_NO_THROW( odbExporter.SaveBoard( odbRoot.GetFullPath(), &board, &props ) );
|
||||
|
||||
wxFileName drill1Dir( odbRoot.GetFullPath(), wxEmptyString );
|
||||
drill1Dir.AppendDir( wxT( "steps" ) );
|
||||
drill1Dir.AppendDir( wxT( "pcb" ) );
|
||||
drill1Dir.AppendDir( wxT( "layers" ) );
|
||||
drill1Dir.AppendDir( wxT( "drill1" ) );
|
||||
BOOST_REQUIRE( drill1Dir.DirExists() );
|
||||
|
||||
wxFileName toolsFile( drill1Dir.GetFullPath(), wxT( "tools" ) );
|
||||
BOOST_REQUIRE( toolsFile.FileExists() );
|
||||
|
||||
wxFFile toolsStream( toolsFile.GetFullPath(), wxT( "rb" ) );
|
||||
wxString toolsContents;
|
||||
BOOST_REQUIRE( toolsStream.ReadAll( &toolsContents ) );
|
||||
BOOST_CHECK( toolsContents.Contains( wxT( "TYPE=NON_PLATED" ) ) );
|
||||
BOOST_CHECK( toolsContents.Contains( wxT( "TYPE2=BLIND" ) ) );
|
||||
|
||||
wxFileName matrixFile( odbRoot.GetFullPath(), wxEmptyString );
|
||||
matrixFile.AppendDir( wxT( "matrix" ) );
|
||||
matrixFile.SetFullName( wxT( "matrix" ) );
|
||||
BOOST_REQUIRE( matrixFile.FileExists() );
|
||||
|
||||
wxFFile matrixStream( matrixFile.GetFullPath(), wxT( "rb" ) );
|
||||
wxString matrixContents;
|
||||
BOOST_REQUIRE( matrixStream.ReadAll( &matrixContents ) );
|
||||
BOOST_CHECK( matrixContents.Contains( wxT( "ADD_TYPE=BACKDRILL" ) ) );
|
||||
|
||||
matrixStream.Close();
|
||||
toolsStream.Close();
|
||||
gerberStream.Close();
|
||||
gerberLayerPairStream.Close();
|
||||
excellonStream.Close();
|
||||
layerPairStream.Close();
|
||||
pthStream.Close();
|
||||
|
||||
wxFileName::Rmdir( odbRoot.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
|
||||
wxFileName::Rmdir( tempDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE );
|
||||
}
|
||||
@@ -23,9 +23,11 @@
|
||||
|
||||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <optional>
|
||||
|
||||
#include <pcbnew/pad.h>
|
||||
#include <pcbnew/pcb_track.h>
|
||||
#include <pcbnew/board.h>
|
||||
|
||||
#include <router/pns_node.h>
|
||||
#include <router/pns_router.h>
|
||||
@@ -413,3 +415,77 @@ BOOST_FIXTURE_TEST_CASE( PNSHoleCollisions, PNS_TEST_FIXTURE )
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( PNSViaBackdrillRetention, PNS_TEST_FIXTURE )
|
||||
{
|
||||
PNS::VIA via( VECTOR2I( 1000, 2000 ), PNS_LAYER_RANGE( F_Cu, B_Cu ), 40000, 20000, nullptr,
|
||||
VIATYPE::THROUGH );
|
||||
via.SetHoleLayers( PNS_LAYER_RANGE( F_Cu, In2_Cu ) );
|
||||
via.SetHolePostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) );
|
||||
via.SetSecondaryDrill( std::optional<int>( 12000 ) );
|
||||
via.SetSecondaryHoleLayers( std::optional<PNS_LAYER_RANGE>( PNS_LAYER_RANGE( F_Cu, In1_Cu ) ) );
|
||||
via.SetSecondaryHolePostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::NOT_POST_MACHINED ) );
|
||||
|
||||
PNS::VIA viaCopy( via );
|
||||
std::unique_ptr<PNS::VIA> viaClone( via.Clone() );
|
||||
|
||||
auto checkVia = [&]( const PNS::VIA& candidate )
|
||||
{
|
||||
BOOST_CHECK_EQUAL( candidate.HoleLayers().Start(), via.HoleLayers().Start() );
|
||||
BOOST_CHECK_EQUAL( candidate.HoleLayers().End(), via.HoleLayers().End() );
|
||||
BOOST_CHECK( candidate.HolePostMachining().has_value() );
|
||||
BOOST_CHECK( candidate.HolePostMachining().value() == PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK );
|
||||
BOOST_CHECK( candidate.SecondaryDrill().has_value() );
|
||||
BOOST_CHECK_EQUAL( candidate.SecondaryDrill().value(), via.SecondaryDrill().value() );
|
||||
BOOST_CHECK( candidate.SecondaryHoleLayers().has_value() );
|
||||
BOOST_CHECK_EQUAL( candidate.SecondaryHoleLayers()->Start(),
|
||||
via.SecondaryHoleLayers()->Start() );
|
||||
BOOST_CHECK_EQUAL( candidate.SecondaryHoleLayers()->End(),
|
||||
via.SecondaryHoleLayers()->End() );
|
||||
BOOST_CHECK( candidate.SecondaryHolePostMachining().has_value() );
|
||||
BOOST_CHECK( candidate.SecondaryHolePostMachining().value() == via.SecondaryHolePostMachining().value() );
|
||||
};
|
||||
|
||||
checkVia( viaCopy );
|
||||
checkVia( *viaClone );
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE( PCBViaBackdrillCloneRetainsData )
|
||||
{
|
||||
BOARD board;
|
||||
PCB_VIA via( &board );
|
||||
|
||||
via.SetPrimaryDrillStartLayer( F_Cu );
|
||||
via.SetPrimaryDrillEndLayer( In3_Cu );
|
||||
via.SetFrontPostMachining( std::optional<PAD_DRILL_POST_MACHINING_MODE>( PAD_DRILL_POST_MACHINING_MODE::COUNTERSINK ) );
|
||||
via.SetSecondaryDrillSize( std::optional<int>( 15000 ) );
|
||||
via.SetSecondaryDrillStartLayer( In1_Cu );
|
||||
via.SetSecondaryDrillEndLayer( In2_Cu );
|
||||
|
||||
PCB_VIA viaCopy( via );
|
||||
std::unique_ptr<PCB_VIA> viaClone( static_cast<PCB_VIA*>( via.Clone() ) );
|
||||
|
||||
auto checkVia = [&]( const PCB_VIA& candidate )
|
||||
{
|
||||
BOOST_CHECK_EQUAL( candidate.GetPrimaryDrillStartLayer(), via.GetPrimaryDrillStartLayer() );
|
||||
BOOST_CHECK_EQUAL( candidate.GetPrimaryDrillEndLayer(), via.GetPrimaryDrillEndLayer() );
|
||||
BOOST_CHECK( candidate.GetFrontPostMachining().has_value() );
|
||||
BOOST_CHECK_EQUAL( static_cast<int>( candidate.GetFrontPostMachining().value() ),
|
||||
static_cast<int>( via.GetFrontPostMachining().value() ) );
|
||||
BOOST_CHECK( candidate.GetSecondaryDrillSize().has_value() );
|
||||
BOOST_CHECK_EQUAL( candidate.GetSecondaryDrillSize().value(),
|
||||
via.GetSecondaryDrillSize().value() );
|
||||
BOOST_CHECK_EQUAL( candidate.GetSecondaryDrillStartLayer(),
|
||||
via.GetSecondaryDrillStartLayer() );
|
||||
BOOST_CHECK_EQUAL( candidate.GetSecondaryDrillEndLayer(),
|
||||
via.GetSecondaryDrillEndLayer() );
|
||||
BOOST_CHECK( candidate.GetBackPostMachining().has_value() );
|
||||
BOOST_CHECK_EQUAL( static_cast<int>( candidate.GetBackPostMachining().value() ),
|
||||
static_cast<int>( via.GetBackPostMachining().value() ) );
|
||||
};
|
||||
|
||||
checkVia( viaCopy );
|
||||
checkVia( *viaClone );
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user