Commit 78a8be0e authored by jean-pierre charras's avatar jean-pierre charras
Browse files

Pcbnew: fix a very old bug in DRC, pad to pad drc tests: sometimes a test...

Pcbnew: fix a very old bug in DRC, pad to pad drc tests: sometimes a test between a rectangular pad and an oval pad was skipped.
Segment Hit Test: enhanced detection near segment ends (noticeable for instance when clicking on short tracks in Pcbnew.
Remove duplicate code about segment hit test.
remove unused file.
parent 5d5a50dd
Loading
Loading
Loading
Loading
+123 −140
Original line number Diff line number Diff line
/**
 * @file trigo.cpp
 * @brief Trigonometric functions.
 * @brief Trigonometric and geometric basic functions.
 */

#include <fctsys.h>
#include <macros.h>
#include <trigo.h>
#include <common.h>

static bool DistanceTest( int seuil, int dx, int dy, int spot_cX, int spot_cY );

bool TestSegmentHit( wxPoint aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist )
{
    return DistanceTest( aDist, aEnd.x - aStart.x, aEnd.y - aStart.y,
                         aRefPoint.x - aStart.x, aRefPoint.y - aStart.y );
}


bool DistanceTest( int seuil, int dx, int dy, int spot_cX, int spot_cY )
{
    /*  We can have 4 cases::
     *      horizontal segment
     *      vertical segment
     *      45 degrees segment
     *      other slopes
#include <math_for_graphics.h>

/* Function TestSegmentHit
 * test for hit on line segment
 * i.e. a reference point is within a given distance from segment
 * aRefPoint = reference point to test
 * aStart, aEnd are coordinates of end points segment
 * aDist = maximum distance for hit
 * Note: for calculation time reasons, the distance between the ref point
 * and the segment is not always exactly calculated
 * (we only know if the actual dist is < aDist, not exactly know this dist.
 * Because many times we have horizontal or vertical segments,
 * a special calcultaion is made for them
 * Note: sometimes we need to calculate the distande between 2 points
 * A square root should be calculated.
 * However, because we just compare 2 distnaces, to avoid calculating square root,
 * the square of distances are compared.
*/
    int cXrot, cYrot, segX, segY;
    int pointX, pointY;

    segX   = dx;
    segY   = dy;
    pointX = spot_cX;
    pointY = spot_cY;

    /* Recalculating coord for the segment is in 1st quadrant (coord >= 0) */
    if( segX < 0 )   /* set > 0 by symmetry about the Y axis */
static inline double square( int x )    // helper function to calculate x*x
{
        segX   = -segX;
        pointX = -pointX;
    return (double) x * x;
}

    if( segY < 0 )   /* set > 0 by symmetry about the X axis */
bool TestSegmentHit( wxPoint aRefPoint, wxPoint aStart, wxPoint aEnd, int aDist )
{
        segY   = -segY;
        pointY = -pointY;
    }
    // test for vertical or horizontal segment
    if( aEnd.x == aStart.x )
    {
        // vertical segment
        int ll = abs( aRefPoint.x - aStart.x );

        if( ll >= aDist )
            return false;

    if( segY == 0 ) /* horizontal */
    {
        if( abs( pointY ) <= seuil )
        {
            if( ( pointX >= 0 ) && ( pointX <= segX ) )
                return 1;
        // To have only one case to examine, ensure aEnd.y > aStart.y
        if( aEnd.y < aStart.y )
            EXCHG( aStart.y, aEnd.y );

            if( ( pointX < 0 ) && ( pointX >= -seuil ) )
            {
                if( ( ( pointX * pointX ) + ( pointY * pointY ) ) <= ( seuil * seuil ) )
                    return true;
            }
            if( ( pointX > segX ) && ( pointX <= ( segX + seuil ) ) )
            {
                if( ( ( ( pointX - segX ) * ( pointX - segX ) )
                     + ( pointY * pointY ) ) <= ( seuil * seuil ) )
                    return true;
            }
        }
    }
    else if( segX == 0 ) /* vertical */
    {
        if( abs( pointX ) <= seuil )
        {
            if( ( pointY >= 0 ) && ( pointY <= segY ) )
        if( aRefPoint.y < aEnd.y && aRefPoint.y > aStart.y )
            return true;

            if( ( pointY < 0 ) && ( pointY >= -seuil ) )
        // there is a special case: x,y near an end point (distance < dist )
        // the distance should be carefully calculated
        if( (aStart.y - aRefPoint.y) < aDist )
        {
                if( ( ( pointY * pointY ) + ( pointX * pointX ) ) <= ( seuil * seuil ) )
            double dd = square( aRefPoint.x - aStart.x) +
                 square( aRefPoint.y - aStart.y );
            if( dd < square( aDist ) )
                return true;
        }

            if( ( pointY > segY ) && ( pointY <= ( segY + seuil ) ) )
        if( (aRefPoint.y - aEnd.y) < aDist )
        {
                if( ( ( ( pointY - segY ) * ( pointY - segY ) )
                     + ( pointX * pointX ) ) <= ( seuil * seuil ) )
            double dd = square( aRefPoint.x - aEnd.x ) +
                 square( aRefPoint.y - aEnd.y );
            if( dd < square( aDist ) )
                return true;
        }
    }
    }
    else if( segX == segY )    /* 45 degrees */
    else if( aEnd.y == aStart.y )
    {
        /* Rotate axes of 45 degrees. mouse was then
         * Coord: x1 = x * y * cos45 + sin45
         * y1 = y * cos45 - sin45 x *
         * And the segment of track is horizontal.
         * Coord recalculation of the mouse (sin45 = cos45 = .707 = 7 / 10
         * Note: sin or cos45 = .707, and when recalculating coord
         * dx45 and dy45, lect coeff .707 is neglected, dx and dy are
         * actually 0707 times
         * Too big. (security hole too small)
         * Spot_cX, Y * must be by .707 * .707 = 0.5
         */

        cXrot = (pointX + pointY) >> 1;
        cYrot = (pointY - pointX) >> 1;
        // horizontal segment
        int ll = abs( aRefPoint.y - aStart.y );

        /* Recalculating coord of segment extremity, which will be vertical
         * following the orientation of axes on the screen: dx45 = pointx
         * (or pointy) and 1.414 is actually greater, and dy45 = 0
         */
        if( ll >= aDist )
            return false;

        // * Threshold should be .707 to reflect the change in coeff dx, dy
        seuil *= 7;
        seuil /= 10;
        // To have only one case to examine, ensure xf > xi
        if( aEnd.x < aStart.x )
            EXCHG( aStart.x, aEnd.x );

        if( abs( cYrot ) <= seuil ) /* ok on vertical axis */
        {
            if( ( cXrot >= 0 ) && ( cXrot <= segX ) )
        if( aRefPoint.x < aEnd.x && aRefPoint.x > aStart.x )
            return true;

            /* Check extremes using the radius of a circle. */
            if( ( cXrot < 0 ) && ( cXrot >= -seuil ) )
        // there is a special case: x,y near an end point (distance < dist )
        // the distance should be carefully calculated
        if( (aStart.x - aRefPoint.x) < aDist )
        {
                if( ( ( cXrot * cXrot ) + ( cYrot * cYrot ) ) <= ( seuil * seuil ) )
            double dd = square( aRefPoint.x - aStart.x) +
                        square( aRefPoint.y - aStart.y );
            if( dd < square( aDist ) )
                return true;
        }
            if( ( cXrot > segX ) && ( cXrot <= ( segX + seuil ) ) )

        if( (aRefPoint.x - aEnd.x) < aDist )
        {
                if( ( ( ( cXrot - segX ) * ( cXrot - segX ) )
                     + ( cYrot * cYrot ) ) <= ( seuil * seuil ) )
            double dd = square(aRefPoint.x - aEnd.x) +
                        square( aRefPoint.y - aEnd.y);
            if( dd < square( aDist ) )
                return true;
        }
    }
    }
    else    /* any orientation */
    else
    {
        /* There is a change of axis (rotation), so that the segment
         * track is horizontal in the new reference
         */
        int angle;

        angle = KiROUND( ( atan2( (double) segY, (double) segX ) * 1800.0 / M_PI ) );
        cXrot = pointX;
        cYrot = pointY;

        RotatePoint( &cXrot, &cYrot, angle );   /* Rotate the point to be tested */
        RotatePoint( &segX, &segY, angle );     /* Rotate the segment */
        // oblique segment:
        // First, we need to calculate the distance between the point
        // and the line defined by aStart and aEnd
        // this dist should be < dist
        //
        // find a,slope such that aStart and aEnd lie on y = a + slope*x
        double  slope   = (double) (aEnd.y - aStart.y) / (aEnd.x - aStart.x);
        double  a   = (double) aStart.y - slope * aStart.x;
        // find c,orthoslope such that (x,y) lies on y = c + orthoslope*x,
        // where orthoslope=(-1/slope)
        // to calculate xp, yp = near point from aRefPoint
        // which is on the line defined by aStart, aEnd
        double  orthoslope   = -1.0 / slope;
        double  c   = (double) aRefPoint.y - orthoslope * aRefPoint.x;
        // find nearest point to (x,y) on line defined by aStart, aEnd
        double  xp  = (a - c) / (orthoslope - slope);
        double  yp  = a + slope * xp;
        // find distance to line, in fact the square of dist,
        // because we just know if it is > or < aDist
        double dd = square( aRefPoint.x - xp ) + square( aRefPoint.y - yp );
        double dist = square( aDist );

        if( dd >= dist )    // this reference point is not a good candiadte.
            return false;

        /* The track is horizontal, following the amendments to coordinate
         * axis and, therefore segX = length of segment
         */
        if( abs( cYrot ) <= seuil ) /* vertical axis */
        // dd is < dist, therefore we should make a fine test
        if( fabs( slope ) > 0.7 )
        {
            if( ( cXrot >= 0 ) && ( cXrot <= segX ) )
            // line segment more vertical than horizontal
            if( (aEnd.y > aStart.y && yp < aEnd.y && yp > aStart.y) ||
                (aEnd.y < aStart.y && yp > aEnd.y && yp < aStart.y) )
                return true;

            if( ( cXrot < 0 ) && ( cXrot >= -seuil ) )
        }
        else
        {
                if( ( ( cXrot * cXrot ) + ( cYrot * cYrot ) ) <= ( seuil * seuil ) )
            // line segment more horizontal than vertical
            if( (aEnd.x > aStart.x && xp < aEnd.x && xp > aStart.x) ||
                (aEnd.x < aStart.x && xp > aEnd.x && xp < aStart.x) )
                return true;
        }

            if( ( cXrot > segX ) && ( cXrot <= ( segX + seuil ) ) )
            {
                if( ( ( ( cXrot - segX ) * ( cXrot - segX ) )
                    + ( cYrot * cYrot ) ) <= ( seuil * seuil ) )
        // Here, the test point is still a good candidate,
        // however it is not "between" the end points of the segment.
        // It is "outside" the segment, but it could be near a segment end point
        // Therefore, we test the dist from the test point to each segment end point
        dd = square( aRefPoint.x - aEnd.x ) + square( aRefPoint.y - aEnd.y );
        if( dd < dist )
            return true;
        dd = square( aRefPoint.x - aStart.x ) + square( aRefPoint.y - aStart.y );
        if( dd < dist )
            return true;
            }
        }
    }

    return false;
    return false;    // no hit
}


+26 −2
Original line number Diff line number Diff line
@@ -2,6 +2,30 @@
 * @file trigo.h
 */

/*
 * This program source code file is part of KiCad, a free EDA CAD application.
 *
 * Copyright (C) 2013 KiCad Developers, see change_log.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
 */


#ifndef TRIGO_H
#define TRIGO_H

@@ -72,8 +96,8 @@ double CrossProduct( wxPoint vectorA, wxPoint vectorB );
/**
 * Function TestSegmentHit
 * test for hit on line segment
 * i.e. cursor within a given distance from segment
 * @param aRefPoint = cursor (point to test) coords
 * i.e. a reference point is within a given distance from segment
 * @param aRefPoint = reference point to test
 * @param aStart is the first end-point of the line segment
 * @param aEnd is the second end-point of the line segment
 * @param aDist = maximum distance for hit
+31 −18
Original line number Diff line number Diff line
@@ -136,16 +136,10 @@ bool trapezoid2pointDRC( wxPoint aTref[4], wxPoint aPcompare, int aDist )
        return false;
    }

    // Test distance between aPcompare and polygon edges:
    int    ii, jj;
    double dist = (double) aDist;

    for( ii = 0, jj = 3; ii < 4; jj = ii, ii++ )  // for all edges in polygon
    // Test distance between aPcompare and each segment of the polygon:
    for( int ii = 0, jj = 3; ii < 4; jj = ii, ii++ )  // for all edge in polygon
    {
        if( TestLineHit( aTref[ii].x, aTref[ii].y,
                         aTref[jj].x, aTref[jj].y,
                         aPcompare.x, aPcompare.y,
                         dist ) )
        if( TestSegmentHit( aTref[ii], aTref[jj], aPcompare, aDist ) )
            return false;
    }

@@ -602,12 +596,30 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
    bool swap_pads;
    swap_pads = false;

    if( aRefPad->GetShape() != PAD_CIRCLE  &&  aPad->GetShape() == PAD_CIRCLE )
    // swap pads to make comparisons easier
    // priority is aRefPad = ROUND then OVAL then RECT then other
    if( aRefPad->GetShape() != aPad->GetShape() && aRefPad->GetShape() != PAD_CIRCLE )
    {
        // pad ref shape is here oval, rect or trapezoid
        switch( aPad->GetShape() )
        {
            case PAD_CIRCLE:
                swap_pads = true;
    else if(  aRefPad->GetShape() != PAD_OVAL  &&  aPad->GetShape() == PAD_OVAL )
                break;

            case PAD_OVAL:
                swap_pads = true;
    else if(  aRefPad->GetShape() != PAD_RECT  &&  aPad->GetShape() == PAD_RECT )
                break;

            case PAD_RECT:
                if( aRefPad->GetShape() != PAD_OVAL )
                    swap_pads = true;
                break;

            default:
                break;
        }
    }

    if( swap_pads )
    {
@@ -639,7 +651,6 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
        break;

    case PAD_RECT:

        // pad_angle = pad orient relative to the aRefPad orient
        pad_angle = aRefPad->GetOrientation() + aPad->GetOrientation();
        NORMALIZE_ANGLE_POS( pad_angle );
@@ -707,7 +718,9 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
        {
            // Should not occur, because aPad and aRefPad are swapped
            // to have only aPad shape RECT or TRAP and aRefPad shape TRAP or RECT.
            wxLogDebug( wxT( "unexpected pad shape %d") , aPad->GetShape() );
            wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad ref RECT @ %d, %d to pad shape %d @ %d, %d"),
                aRefPad->GetPosition().x, aRefPad->GetPosition().y,
                aPad->GetShape(), aPad->GetPosition().x, aPad->GetPosition().y );
        }
        break;

@@ -775,7 +788,7 @@ bool DRC::checkClearancePadToPad( D_PAD* aRefPad, D_PAD* aPad )
        break;

    default:
        wxLogDebug( wxT( "unexpected pad shape" ) );
        wxLogDebug( wxT( "DRC::checkClearancePadToPad: unexpected pad shape" ) );
        break;
    }

+0 −176
Original line number Diff line number Diff line
/*************************************************************************/
/* Functions to create drill data used to create files and report  files */
/*************************************************************************/

#include <algorithm> // sort

#include <fctsys.h>
#include <common.h>
#include <plot_common.h>
#include <macros.h>

#include <class_board.h>
#include <class_module.h>
#include <class_track.h>

#include <pcbnew.h>
#include <pcbplot.h>
#include <gendrill.h>


// Local Functions

/* Compare function used for sorting holes  by increasing diameter value
 * and X value
 */
static bool CmpHoleDiameterValue( const HOLE_INFO& a, const HOLE_INFO& b )
{
    if( a.m_Hole_Diameter != b.m_Hole_Diameter )
        return a.m_Hole_Diameter < b.m_Hole_Diameter;

    if( a.m_Hole_Pos.x != b.m_Hole_Pos.x )
        return a.m_Hole_Pos.x < b.m_Hole_Pos.x;

    return a.m_Hole_Pos.y < b.m_Hole_Pos.y;
}


/*
 * Function BuildHolesList
 * Create the list of holes and tools for a given board
 * The list is sorted by increasing drill values
 * Only holes from aFirstLayer to aLastLayer copper layers  are listed (for vias, because pad holes are always through holes)
 * param aPcb : the given board
 * param aHoleListBuffer : the std::vector<HOLE_INFO> to fill with pcb holes info
 * param aToolListBuffer : the std::vector<DRILL_TOOL> to fill with tools to use
 * param aFirstLayer = first layer to consider. if < 0 aFirstLayer is ignored   (used to creates report file)
 * param aLastLayer = last layer to consider. if < 0 aLastLayer is ignored
 * param aExcludeThroughHoles : if true, exclude through holes ( pads and vias through )
 * param aGenerateNPTH_list :
 *       true to create NPTH only list (with no plated holes)
 *       false to created plated holes list (with no NPTH )
 */
void Build_Holes_List( BOARD* aPcb,
                       std::vector<HOLE_INFO>& aHoleListBuffer,
                       std::vector<DRILL_TOOL>& aToolListBuffer,
                       int aFirstLayer, int aLastLayer, bool aExcludeThroughHoles,
                       bool aGenerateNPTH_list )
{
    HOLE_INFO new_hole;
    int       hole_value;

    aHoleListBuffer.clear();
    aToolListBuffer.clear();

    if( (aFirstLayer >= 0) && (aLastLayer >= 0) )
    {
        if( aFirstLayer > aLastLayer )
            EXCHG( aFirstLayer, aLastLayer );
    }

    /* build hole list for vias
    */
    if( ! aGenerateNPTH_list )  // vias are always plated !
    {
        for( TRACK* track = aPcb->m_Track;  track;  track = track->Next() )
        {
            if( track->Type() != PCB_VIA_T )
                continue;

            SEGVIA* via = (SEGVIA*) track;
            hole_value = via->GetDrillValue();

            if( hole_value == 0 )
                continue;

            new_hole.m_Tool_Reference = -1;         // Flag value for Not initialized
            new_hole.m_Hole_Orient    = 0;
            new_hole.m_Hole_Diameter  = hole_value;
            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_Pos = via->m_Start;
            via->ReturnLayerPair( &new_hole.m_Hole_Top_Layer, &new_hole.m_Hole_Bottom_Layer );

            // ReturnLayerPair return params with m_Hole_Bottom_Layer < m_Hole_Top_Layer
            if( (new_hole.m_Hole_Bottom_Layer > aFirstLayer) && (aFirstLayer >= 0) )
                continue;

            if( (new_hole.m_Hole_Top_Layer < aLastLayer) && (aLastLayer >= 0) )
                continue;

            if( aExcludeThroughHoles  && (new_hole.m_Hole_Bottom_Layer == LAYER_N_BACK)
               && (new_hole.m_Hole_Top_Layer == LAYER_N_FRONT) )
                continue;

            aHoleListBuffer.push_back( new_hole );
        }
    }

    // build hole list for pads (assumed always through holes)
    if( !aExcludeThroughHoles || aGenerateNPTH_list )
    {
        for( MODULE* module = aPcb->m_Modules;  module;  module = module->Next() )
        {
            // Read and analyse pads
            for( D_PAD* pad = module->m_Pads;  pad;  pad = pad->Next() )
            {
                if( ! aGenerateNPTH_list && pad->GetAttribute() == PAD_HOLE_NOT_PLATED )
                    continue;

                if( aGenerateNPTH_list && pad->GetAttribute() != PAD_HOLE_NOT_PLATED )
                    continue;

                if( pad->GetDrillSize().x == 0 )
                    continue;

                new_hole.m_Hole_NotPlated = (pad->GetAttribute() == PAD_HOLE_NOT_PLATED);
                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;

                if( pad->GetDrillShape() != PAD_CIRCLE )
                    new_hole.m_Hole_Shape = 1; // oval flag set

                new_hole.m_Hole_Size = pad->GetDrillSize();
                new_hole.m_Hole_Pos = pad->GetPosition();               // hole position
                new_hole.m_Hole_Bottom_Layer = LAYER_N_BACK;
                new_hole.m_Hole_Top_Layer    = LAYER_N_FRONT;// pad holes are through holes
                aHoleListBuffer.push_back( new_hole );
            }
        }
    }

    // Sort holes per increasing diameter value
    sort( aHoleListBuffer.begin(), aHoleListBuffer.end(), CmpHoleDiameterValue );

    // build the tool list
    int        LastHole = -1; /* Set to not initialised (this is a value not used
                               * for aHoleListBuffer[ii].m_Hole_Diameter) */
    DRILL_TOOL new_tool( 0 );
    unsigned   jj;

    for( unsigned ii = 0; ii < aHoleListBuffer.size(); ii++ )
    {
        if( aHoleListBuffer[ii].m_Hole_Diameter != LastHole )
        {
            new_tool.m_Diameter = ( aHoleListBuffer[ii].m_Hole_Diameter );
            aToolListBuffer.push_back( new_tool );
            LastHole = new_tool.m_Diameter;
        }

        jj = aToolListBuffer.size();

        if( jj == 0 )
            continue;                                       // Should not occurs

        aHoleListBuffer[ii].m_Tool_Reference = jj;          // Tool value Initialized (value >= 1)

        aToolListBuffer.back().m_TotalCount++;

        if( aHoleListBuffer[ii].m_Hole_Shape )
            aToolListBuffer.back().m_OvalCount++;
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -715,7 +715,7 @@ void PCB_PARSER::parseLayers() throw( IO_ERROR, PARSE_ERROR )
        layer.SetFixedListIndex( layerIndex );
        m_board->SetLayer( layerIndex, layer );

        wxLogDebug( wxT( "Mapping layer %s to index %d" ),  GetChars( wname ), layerIndex );
//        wxLogDebug( wxT( "Mapping layer %s to index %d" ),  GetChars( wname ), layerIndex );

        if( layerType != LT_UNDEFINED )
            copperLayerCount++;
Loading