← Back to team overview

kicad-developers team mailing list archive

[FEATURE] Partial selection in pcbnew

 

I have attached a patch-set that implements "partial selection" of objects
when the selection box is dragged right-to-left.

L -> R = Objects must be completely enclosed to be selected
R -> L = Objects that intersect the selection rectangle will be selected.

To achieve this I had to fix a lot of the HitTest implementations as this
was broken for most shapes, under a variety of edge cases (some HitTest
code did not work at all).

There are two issues I see as outstanding, and am unsure how to proceed:

1. When editing a PCB, selecting part of a footprint (e.g. a line of the
courtyard) selects both that line and the entire footprint. This causes
some issues when the footprint is dragged around the PCB. I believe that
the line should not be selected separately, but the entire footprint should.

2. The inverse of 1. In the footprint editor, selecting a single graphical
item selects the entire footprint. Somehow I would like to filter the
selection such that individual items are selected but NOT the entire
footprint.

Feedback please! :)

I have fixed hit testing (both for wxPoint and EDA_RECT comparison) for:

- Pads (all shapes)
- Lines
- Circles
- Arcs
- Text items
- Zones
- Footprints

Cheers,
Oliver
From 688d986602189b24877dd3296f12d8adb2a5fd9f Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 21 Apr 2017 00:20:56 +1000
Subject: [PATCH 01/12] Alter selection mode based on drag direction

LEFT > RIGHT = Enclosed selection
RIGHT > LEFT = Touching selection
---
 include/preview_items/selection_area.h |  4 ++++
 pcbnew/tools/selection_tool.cpp        | 37 ++++++++++++++++++++++++++++++----
 2 files changed, 37 insertions(+), 4 deletions(-)

diff --git a/include/preview_items/selection_area.h b/include/preview_items/selection_area.h
index c860ee4..d45924b 100644
--- a/include/preview_items/selection_area.h
+++ b/include/preview_items/selection_area.h
@@ -76,6 +76,10 @@ public:
         return wxT( "SELECTION_AREA" );
     }
 
+    VECTOR2I GetOrigin() const { return m_origin; }
+
+    VECTOR2I GetEnd() const { return m_end; }
+
 private:
 
     /**
diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp
index 494fb1c..d31eee7 100644
--- a/pcbnew/tools/selection_tool.cpp
+++ b/pcbnew/tools/selection_tool.cpp
@@ -500,21 +500,50 @@ bool SELECTION_TOOL::selectMultiple()
 
             // Mark items within the selection box as selected
             std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> selectedItems;
+
+            // Filter the view items based on the selection box
             BOX2I selectionBox = area.ViewBBox();
             view->Query( selectionBox, selectedItems );         // Get the list of selected items
 
             std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR>::iterator it, it_end;
 
+            int width = area.GetEnd().x - area.GetOrigin().x;
+            int height = area.GetEnd().y - area.GetOrigin().y;
+
+            // Construct an EDA_RECT to determine BOARD_ITEM selection
+            EDA_RECT selectionRect( wxPoint( area.GetOrigin().x, area.GetOrigin().y ),
+                                    wxSize( width, height ) );
+
+            selectionRect.Normalize();
+
             for( it = selectedItems.begin(), it_end = selectedItems.end(); it != it_end; ++it )
             {
                 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it->first );
 
-                // Add only those items that are visible and fully within the selection box
-                if( !item->IsSelected() && selectable( item ) &&
-                        selectionBox.Contains( item->ViewBBox() ) )
+                /* Selection mode depends on direction of drag-selection:
+                 * Left > Right : Select objects that are fully enclosed by selection
+                 * Right > Left : Select objects that are crossed by selection
+                 */
+
+                // Add only those items that are visible
+                if( !item->IsSelected() && selectable( item ) )
                 {
-                    select( item );
+                    if( item->HitTest( selectionRect, width >= 0) )
+                    {
+                        select( item );
+                    }
+
                 }
+                /*
+                    // Selecting left->right requires full enclosure
+                    if ( xDelta >= 0 && selectionBox.Contains( item->ViewBBox() ) )
+                    {
+                        select( item );
+                    }
+
+                    // Selecting right->left requires only
+                    else if
+                */
             }
 
             if( m_selection.Size() == 1 )
-- 
2.7.4

From db7ac6f20c515c590b652284f4a59645f664beb1 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Fri, 21 Apr 2017 00:53:57 +1000
Subject: [PATCH 02/12] Fixed ::HitTest for Circle shape

- Testing against rectangle intersection now works correctly
- Previously tested against BoundingBox() not circle outline
---
 common/base_struct.cpp          | 60 +++++++++++++++++++++++++++++++++++++++++
 include/class_eda_rect.h        | 30 +++++++++++++++++++++
 pcbnew/class_drawsegment.cpp    | 15 ++++++++++-
 pcbnew/tools/selection_tool.cpp | 16 +++++------
 4 files changed, 110 insertions(+), 11 deletions(-)

diff --git a/common/base_struct.cpp b/common/base_struct.cpp
index 76f51fb..8839ad8 100644
--- a/common/base_struct.cpp
+++ b/common/base_struct.cpp
@@ -443,6 +443,66 @@ bool EDA_RECT::Intersects( const EDA_RECT& aRect ) const
     return rc;
 }
 
+const wxPoint EDA_RECT::ClosestPointTo( const wxPoint& aPoint ) const
+{
+    EDA_RECT me(*this);
+
+    me.Normalize();         // ensure size is >= 0
+
+    // Determine closest point to the circle centre within this rect
+    int nx = std::max( me.GetLeft(), std::min( aPoint.x, me.GetRight() ) );
+    int ny = std::max( me.GetTop(), std::min( aPoint.y, me.GetBottom() ) );
+
+    return wxPoint( nx, ny );
+}
+
+const wxPoint EDA_RECT::FarthestPointTo( const wxPoint& aPoint ) const
+{
+    EDA_RECT me(*this);
+
+    me.Normalize();         // ensure size is >= 0
+
+    int fx = std::max( std::abs( aPoint.x - me.GetLeft() ), std::abs( aPoint.x - me.GetRight() ) );
+    int fy = std::max( std::abs( aPoint.y - me.GetTop() ), std::abs( aPoint.y - me.GetBottom() ) );
+
+    return wxPoint( fx, fy );
+}
+
+/* IntersectsCircle
+ * test for common area between this rect and a circle
+ */
+bool EDA_RECT::IntersectsCircle( const wxPoint& aCenter, const int aRadius ) const
+{
+    wxPoint closest = ClosestPointTo( aCenter );
+
+    double dx = aCenter.x - closest.x;
+    double dy = aCenter.y - closest.y;
+
+    double r = (double) aRadius;
+
+    return ( dx * dx + dy * dy ) <= ( r * r );
+}
+
+bool EDA_RECT::IntersectsCircleEdge( const wxPoint& aCenter, const int aRadius, const int aWidth ) const
+{
+    EDA_RECT me(*this);
+    me.Normalize();         // ensure size is >= 0
+
+    // Test if the circle intersects at all
+    if( !IntersectsCircle( aCenter, aRadius + aWidth / 2 ) )
+    {
+        return false;
+    }
+
+    wxPoint far = FarthestPointTo( aCenter );
+    // Farthest point must be further than the inside of the line
+    double fx = (double) far.x;
+    double fy = (double) far.y;
+
+    double r = (double) aRadius - (double) aWidth / 2;
+
+    return ( fx * fx + fy * fy ) > ( r * r );
+}
 
 EDA_RECT& EDA_RECT::Inflate( int aDelta )
 {
diff --git a/include/class_eda_rect.h b/include/class_eda_rect.h
index 8849942..d9a67c1 100644
--- a/include/class_eda_rect.h
+++ b/include/class_eda_rect.h
@@ -169,6 +169,36 @@ public:
     bool Intersects( const wxPoint& aPoint1, const wxPoint& aPoint2 ) const;
 
     /**
+     * Return the point in this rect that is closest to the provided point
+     */
+    const wxPoint ClosestPointTo( const wxPoint& aPoint ) const;
+
+    /**
+     * Return the point in this rect that is farthest from the provided point
+     */
+    const wxPoint FarthestPointTo( const wxPoint& aPoint ) const;
+
+    /**
+     * Function IntersectsCircle
+     * tests for a common area between a circle and this rectangle
+     *
+     * @param aCenter center of the circle
+     * @param aRadius radius of the circle
+     */
+    bool IntersectsCircle( const wxPoint& aCenter, const int aRadius ) const;
+
+
+    /**
+     * IntersectsCircleEdge
+     * Tests for intersection between this rect and the edge (radius) of a circle
+     *
+     * @param aCenter center of the circle
+     * @param aRadius radius of the circle
+     * @param aWidth width of the circle edge
+     */
+    bool IntersectsCircleEdge( const wxPoint& aCenter, const int aRadius, const int aWidth ) const;
+
+    /**
      * Function operator(wxRect)
      * overloads the cast operator to return a wxRect
      * wxRect does not accept negative values for size, so ensure the
diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp
index 568c8be..89947ae 100644
--- a/pcbnew/class_drawsegment.cpp
+++ b/pcbnew/class_drawsegment.cpp
@@ -522,6 +522,8 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
     EDA_RECT arect = aRect;
     arect.Inflate( aAccuracy );
 
+    EDA_RECT arcRect;
+
     switch( m_Shape )
     {
     case S_CIRCLE:
@@ -529,7 +531,18 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
         if( aContained )
             return arect.Contains( GetBoundingBox() );
         else
-            return arect.Intersects( GetBoundingBox() );
+        {
+            // If the rectangle does not intersect the bounding box, this is a much quicker test
+            if( !aRect.Intersects( GetBoundingBox() ) )
+            {
+                return false;
+            }
+            else
+            {
+                return arect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
+            }
+
+        }
         break;
 
     case S_ARC:
diff --git a/pcbnew/tools/selection_tool.cpp b/pcbnew/tools/selection_tool.cpp
index d31eee7..0ea9529 100644
--- a/pcbnew/tools/selection_tool.cpp
+++ b/pcbnew/tools/selection_tool.cpp
@@ -525,25 +525,21 @@ bool SELECTION_TOOL::selectMultiple()
                  * Right > Left : Select objects that are crossed by selection
                  */
 
-                // Add only those items that are visible
-                if( !item->IsSelected() && selectable( item ) )
+                if( width >= 0 )
                 {
-                    if( item->HitTest( selectionRect, width >= 0) )
+                    if( selectionBox.Contains( item->ViewBBox() ) )
                     {
                         select( item );
                     }
-
                 }
-                /*
-                    // Selecting left->right requires full enclosure
-                    if ( xDelta >= 0 && selectionBox.Contains( item->ViewBBox() ) )
+                else
+                {
+                    if( item->HitTest( selectionRect, false ) )
                     {
                         select( item );
                     }
 
-                    // Selecting right->left requires only
-                    else if
-                */
+                }
             }
 
             if( m_selection.Size() == 1 )
-- 
2.7.4

From 58b8a4be78972b2d2890b4e3ab41024108d160eb Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Sat, 22 Apr 2017 15:03:06 +1000
Subject: [PATCH 03/12] Fixed HitTest for Arc segment

---
 pcbnew/class_drawsegment.cpp | 33 +++++++++++++++++----------------
 1 file changed, 17 insertions(+), 16 deletions(-)

diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp
index 89947ae..c3fab97 100644
--- a/pcbnew/class_drawsegment.cpp
+++ b/pcbnew/class_drawsegment.cpp
@@ -516,9 +516,6 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition ) const
 
 bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
 {
-    wxPoint p1, p2;
-    int radius;
-    float theta;
     EDA_RECT arect = aRect;
     arect.Inflate( aAccuracy );
 
@@ -546,24 +543,28 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
         break;
 
     case S_ARC:
-        radius = hypot( (double)( GetEnd().x - GetStart().x ),
-                        (double)( GetEnd().y - GetStart().y ) );
-        theta  = std::atan2( (double)( GetEnd().y - GetStart().y ),
-                             (double)( GetEnd().x - GetStart().x ) );
 
-        //Approximate the arc with two lines. This should be accurate enough for selection.
-        p1.x   = radius * std::cos( theta + M_PI/4 ) + GetStart().x;
-        p1.y   = radius * std::sin( theta + M_PI/4 ) + GetStart().y;
-        p2.x   = radius * std::cos( theta + M_PI/2 ) + GetStart().x;
-        p2.y   = radius * std::sin( theta + M_PI/2 ) + GetStart().y;
+        computeArcBBox( arcRect );
 
+        // Test for full containment of this arc in the rect
         if( aContained )
-            return arect.Contains( GetEnd() ) && aRect.Contains( p1 ) && aRect.Contains( p2 );
+        {
+            return arect.Contains( arcRect );
+        }
+        // Test if the rect crosses the arc
         else
-            return arect.Intersects( GetEnd(), p1 ) || aRect.Intersects( p1, p2 );
-
+        {
+            arcRect = arcRect.Common( arect );
+            //arcRect.Inflate( GetWidth() );
+
+            /* All following tests must pass:
+             * 1. Rectangle must intersect arc BoundingBox
+             * 2. Rectangle must cross the outside of the arc
+             */
+            return arcRect.Intersects( arect ) &&
+                   arcRect.IntersectsCircleEdge( GetCenter(), GetRadius(), GetWidth() );
+        }
         break;
-
     case S_SEGMENT:
         if( aContained )
             return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
-- 
2.7.4

From 06ac9516a685e8727085a26e5efd560630a62fbe Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Sat, 22 Apr 2017 16:49:15 +1000
Subject: [PATCH 04/12] HitTest for pads

- Circular pads
---
 pcbnew/class_drawsegment.cpp |  1 +
 pcbnew/class_pad.cpp         | 30 ++++++++++++++++++++++++++++++
 pcbnew/class_pad.h           |  2 ++
 3 files changed, 33 insertions(+)

diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp
index c3fab97..53293ff 100644
--- a/pcbnew/class_drawsegment.cpp
+++ b/pcbnew/class_drawsegment.cpp
@@ -517,6 +517,7 @@ bool DRAWSEGMENT::HitTest( const wxPoint& aPosition ) const
 bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
 {
     EDA_RECT arect = aRect;
+    arect.Normalize();
     arect.Inflate( aAccuracy );
 
     EDA_RECT arcRect;
diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp
index 1d2b81a..ba17744 100644
--- a/pcbnew/class_pad.cpp
+++ b/pcbnew/class_pad.cpp
@@ -776,6 +776,36 @@ bool D_PAD::HitTest( const wxPoint& aPosition ) const
     return false;
 }
 
+bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
+{
+    EDA_RECT arect = aRect;
+    arect.Normalize();
+    arect.Inflate( aAccuracy );
+
+    if( !arect.Intersects( GetBoundingBox() ) )
+        return false;
+
+    if( aContained )
+        return arect.Contains( GetBoundingBox() );
+
+    switch( GetShape() )
+    {
+    case PAD_SHAPE_CIRCLE:
+        return arect.IntersectsCircle( GetPosition(), GetBoundingRadius() );
+    case PAD_SHAPE_RECT:
+        break;
+    case PAD_SHAPE_OVAL:
+        break;
+    case PAD_SHAPE_TRAPEZOID:
+        break;
+    case PAD_SHAPE_ROUNDRECT:
+        break;
+    default:
+        break;
+    }
+
+    return false;
+}
 
 int D_PAD::Compare( const D_PAD* padref, const D_PAD* padcmp )
 {
diff --git a/pcbnew/class_pad.h b/pcbnew/class_pad.h
index 4dd2233..53900dd 100644
--- a/pcbnew/class_pad.h
+++ b/pcbnew/class_pad.h
@@ -483,6 +483,8 @@ public:
 
     bool HitTest( const wxPoint& aPosition ) const override;
 
+    bool HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy = 0 ) const override;
+
     wxString GetClass() const override
     {
         return wxT( "PAD" );
-- 
2.7.4

From c47454c352a6f7f74383b7816bd7f2b46a81175c Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Sun, 23 Apr 2017 10:06:56 +1000
Subject: [PATCH 05/12] HitTest for Rectangular pads

HitTest for Rectangular pads

- Works at any rotation, even with Shape Offset
- Fixed bugs in D_PAD BoundingBox calculation
---
 common/base_struct.cpp       | 104 +++++++++++++++++++++++++++++++++++++++++++
 include/class_eda_rect.h     |   9 ++++
 pcbnew/class_drawsegment.cpp |   7 ++-
 pcbnew/class_pad.cpp         |  30 +++++++++----
 4 files changed, 141 insertions(+), 9 deletions(-)

diff --git a/common/base_struct.cpp b/common/base_struct.cpp
index 8839ad8..e44d496 100644
--- a/common/base_struct.cpp
+++ b/common/base_struct.cpp
@@ -443,6 +443,110 @@ bool EDA_RECT::Intersects( const EDA_RECT& aRect ) const
     return rc;
 }
 
+bool EDA_RECT::Intersects( const EDA_RECT& aRect, double aRot ) const
+{
+    /* Most rectangles will be axis aligned.
+     * It is quicker to check for this case and pass the rect
+     * to the simpler intersection test
+     */
+
+    // Prevent floating point comparison errors
+    static const double ROT_EPS = 0.000000001;
+
+    static const double ROT_PARALLEL[] = { -3600, -1800, 0, 1800, 3600 };
+    static const double ROT_PERPENDICULAR[] = { -2700, -900, 0, 900, 2700 };
+
+    NORMALIZE_ANGLE_POS<double>( aRot );
+
+    // Test for non-rotated rectangle
+    for( int ii=0; ii<5; ii++ )
+    {
+        if( std::fabs( aRot - ROT_PARALLEL[ii] ) < ROT_EPS )
+        {
+            return Intersects( aRect );
+        }
+    }
+
+    // Test for rectangle rotated by multiple of 90 degrees
+    for( int jj=0; jj<4; jj++ )
+    {
+        if( std::fabs( aRot - ROT_PERPENDICULAR[jj] ) < ROT_EPS )
+        {
+            EDA_RECT rotRect;
+
+            // Rotate the supplied rect by 90 degrees
+            rotRect.SetOrigin( aRect.Centre() );
+            rotRect.Inflate( aRect.GetHeight(), aRect.GetWidth() );
+            return Intersects( rotRect );
+        }
+    }
+
+    /* There is some non-orthogonal rotation.
+     * There are three cases to test:
+     * A) One point of this rect is inside the rotated rect
+     * B) One point of the rotated rect is inside this rect
+     * C) One of the sides of the rotated rect intersect this
+     */
+
+    wxPoint corners[4];
+
+    /* Test A : Any corners exist in rotated rect? */
+
+    corners[0] = m_Pos;
+    corners[1] = m_Pos + wxPoint( m_Size.x, 0 );
+    corners[2] = m_Pos + wxPoint( m_Size.x, m_Size.y );
+    corners[3] = m_Pos + wxPoint( 0, m_Size.y );
+
+    wxPoint rCentre = aRect.Centre();
+
+    for( int i=0; i<4; i++ )
+    {
+        wxPoint delta = corners[i] - rCentre;
+        RotatePoint( &delta, -aRot );
+        delta += rCentre;
+
+        if( aRect.Contains( delta ) )
+        {
+            return true;
+        }
+    }
+
+    /* Test B : Any corners of rotated rect exist in this one? */
+    int w = aRect.GetWidth() / 2;
+    int h = aRect.GetHeight() / 2;
+
+    // Construct corners around center of shape
+    corners[0] = wxPoint( -w, -h );
+    corners[1] = wxPoint(  w, -h );
+    corners[2] = wxPoint(  w,  h );
+    corners[3] = wxPoint( -w,  h );
+
+    // Rotate and test each corner
+    for( int j=0; j<4; j++ )
+    {
+        RotatePoint( &corners[j], aRot );
+        corners[j] += rCentre;
+
+        if( Contains( corners[j] ) )
+        {
+            return true;
+        }
+    }
+
+    /* Test C : Any sides of rotated rect intersect this */
+
+    if( Intersects( corners[0], corners[1] ) ||
+        Intersects( corners[1], corners[2] ) ||
+        Intersects( corners[2], corners[3] ) ||
+        Intersects( corners[3], corners[0] ) )
+    {
+        return true;
+    }
+
+
+    return false;
+}
+
 const wxPoint EDA_RECT::ClosestPointTo( const wxPoint& aPoint ) const
 {
     EDA_RECT me(*this);
diff --git a/include/class_eda_rect.h b/include/class_eda_rect.h
index d9a67c1..c14c3b1 100644
--- a/include/class_eda_rect.h
+++ b/include/class_eda_rect.h
@@ -158,6 +158,15 @@ public:
     bool Intersects( const EDA_RECT& aRect ) const;
 
     /**
+     * Tests for a common area between this rectangle,
+     * and a rectangle with arbitrary rotation
+     *
+     * @param aRect a rectangle to test intersection with
+     * @param aRot rectangle rotation (in 1/10 degrees)
+     */
+    bool Intersects( const EDA_RECT& aRect, double aRot ) const;
+
+    /**
      * Function Intersects
      * tests for a common area between a segment and this rectangle.
      *
diff --git a/pcbnew/class_drawsegment.cpp b/pcbnew/class_drawsegment.cpp
index 53293ff..30f234a 100644
--- a/pcbnew/class_drawsegment.cpp
+++ b/pcbnew/class_drawsegment.cpp
@@ -556,7 +556,6 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
         else
         {
             arcRect = arcRect.Common( arect );
-            //arcRect.Inflate( GetWidth() );
 
             /* All following tests must pass:
              * 1. Rectangle must intersect arc BoundingBox
@@ -568,9 +567,15 @@ bool DRAWSEGMENT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy
         break;
     case S_SEGMENT:
         if( aContained )
+        {
             return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
+        }
         else
+        {
+            // Account for the width of the line
+            arect.Inflate( GetWidth() / 2 );
             return arect.Intersects( GetStart(), GetEnd() );
+        }
 
         break;
 
diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp
index ba17744..64d42c4 100644
--- a/pcbnew/class_pad.cpp
+++ b/pcbnew/class_pad.cpp
@@ -178,6 +178,9 @@ int D_PAD::GetRoundRectCornerRadius( const wxSize& aSize ) const
 }
 
 
+/**
+ * Return the BoundingBox for a D_PAD
+ */
 const EDA_RECT D_PAD::GetBoundingBox() const
 {
     EDA_RECT area;
@@ -192,7 +195,7 @@ const EDA_RECT D_PAD::GetBoundingBox() const
         break;
 
     case PAD_SHAPE_OVAL:
-        // Calculate the position of each rounded ent
+        // Calculate the position of each rounded end
         quadrant1.x =  m_Size.x/2;
         quadrant1.y =  0;
         quadrant2.x =  0;
@@ -207,7 +210,7 @@ const EDA_RECT D_PAD::GetBoundingBox() const
         dy = std::max( std::abs( quadrant1.y ) , std::abs( quadrant2.y )  );
 
         // Set the bbox
-        area.SetOrigin( m_Pos );
+        area.SetOrigin( ShapePos() );
         area.Inflate( dx, dy );
         break;
 
@@ -226,7 +229,7 @@ const EDA_RECT D_PAD::GetBoundingBox() const
         dy = std::max( std::abs( quadrant1.y ) , std::abs( quadrant2.y )  );
 
         // Set the bbox
-        area.SetOrigin( m_Pos );
+        area.SetOrigin( ShapePos() );
         area.Inflate( dx, dy );
         break;
 
@@ -251,6 +254,7 @@ const EDA_RECT D_PAD::GetBoundingBox() const
         y  = std::min( quadrant1.y, std::min( quadrant2.y, std::min( quadrant3.y, quadrant4.y) ) );
         dx = std::max( quadrant1.x, std::max( quadrant2.x, std::max( quadrant3.x, quadrant4.x) ) );
         dy = std::max( quadrant1.y, std::max( quadrant2.y, std::max( quadrant3.y, quadrant4.y) ) );
+
         area.SetOrigin( m_Pos.x+x, m_Pos.y+y );
         area.SetSize( dx-x, dy-y );
         break;
@@ -782,18 +786,28 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con
     arect.Normalize();
     arect.Inflate( aAccuracy );
 
-    if( !arect.Intersects( GetBoundingBox() ) )
-        return false;
+    EDA_RECT shapeRect;
 
-    if( aContained )
-        return arect.Contains( GetBoundingBox() );
+    shapeRect.SetOrigin( ShapePos() );
+    shapeRect.Inflate( GetSize().x / 2, GetSize().y / 2 );
+
+    EDA_RECT bb = GetBoundingBox();
+
+    if( !arect.Intersects( bb ) )
+            return false;
+
+    int dist;
+
+    // This covers total containment for all test cases
+    if( arect.Contains( bb ) )
+        return true;
 
     switch( GetShape() )
     {
     case PAD_SHAPE_CIRCLE:
         return arect.IntersectsCircle( GetPosition(), GetBoundingRadius() );
     case PAD_SHAPE_RECT:
-        break;
+        return arect.Intersects( shapeRect, m_Orient );
     case PAD_SHAPE_OVAL:
         break;
     case PAD_SHAPE_TRAPEZOID:
-- 
2.7.4

From 3cdd41b9de5f12b29ac203621f6b3aa5d466d52d Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Mon, 24 Apr 2017 21:54:53 +1000
Subject: [PATCH 06/12] HitTest for Oval pads

HitTest for Oval pads

Required fix for GetBoundingBox method for Oval Pad shape
---
 pcbnew/class_pad.cpp | 137 +++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 116 insertions(+), 21 deletions(-)

diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp
index 64d42c4..958e969 100644
--- a/pcbnew/class_pad.cpp
+++ b/pcbnew/class_pad.cpp
@@ -185,33 +185,79 @@ const EDA_RECT D_PAD::GetBoundingBox() const
 {
     EDA_RECT area;
     wxPoint quadrant1, quadrant2, quadrant3, quadrant4;
-    int x, y, dx, dy;
+    int x, y, r, dx, dy;
+
+    wxPoint center = ShapePos();
+    wxPoint endPoint;
+
+    EDA_RECT endRect;
 
     switch( GetShape() )
     {
     case PAD_SHAPE_CIRCLE:
-        area.SetOrigin( m_Pos );
+        area.SetOrigin( center );
         area.Inflate( m_Size.x / 2 );
         break;
 
     case PAD_SHAPE_OVAL:
-        // Calculate the position of each rounded end
-        quadrant1.x =  m_Size.x/2;
-        quadrant1.y =  0;
-        quadrant2.x =  0;
-        quadrant2.y =  m_Size.y/2;
+        /* To get the BoundingBox of an oval pad:
+         * a) If the pad is ROUND, see method for PAD_SHAPE_CIRCLE above
+         * OTHERWISE:
+         * b) Construct EDA_RECT for portion between circular ends
+         * c) Rotate that EDA_RECT
+         * d) Add the circular ends to the EDA_RECT
+         */
+
+        // Test if the shape is circular
+        if( m_Size.x == m_Size.y )
+        {
+            area.SetOrigin( center );
+            area.Inflate( m_Size.x / 2 );
+            break;
+        }
 
-        RotatePoint( &quadrant1, m_Orient );
-        RotatePoint( &quadrant2, m_Orient );
+        if( m_Size.x > m_Size.y )
+        {
+            // Pad is horizontal
+            dx = ( m_Size.x - m_Size.y ) / 2;
+            dy = m_Size.y / 2;
+
+            // Location of end-points
+            x = dx;
+            y = 0;
+            r = dy;
+        }
+        else
+        {
+            // Pad is vertical
+            dx = m_Size.x / 2;
+            dy = ( m_Size.y - m_Size.x ) / 2;
 
-        // Calculate the max position of each end, relative to the pad position
-        // (the min position is symetrical)
-        dx = std::max( std::abs( quadrant1.x ) , std::abs( quadrant2.x )  );
-        dy = std::max( std::abs( quadrant1.y ) , std::abs( quadrant2.y )  );
+            x = 0;
+            y = dy;
+            r = dx;
+        }
 
-        // Set the bbox
-        area.SetOrigin( ShapePos() );
+        // Construct the center rectangle and rotate
+        area.SetOrigin( center );
         area.Inflate( dx, dy );
+        area = area.GetBoundingBoxRotated( center, m_Orient );
+
+        endPoint = wxPoint( x, y );
+        RotatePoint( &endPoint, m_Orient );
+
+        // Add points at each quadrant of circular regions
+        endRect.SetOrigin( center + endPoint );
+        endRect.Inflate( r );
+
+        area.Merge( endRect );
+
+        endRect.SetSize( 0, 0 );
+        endRect.SetOrigin( center - endPoint );
+        endRect.Inflate( r );
+
+        area.Merge( endRect );
+
         break;
 
     case PAD_SHAPE_RECT:
@@ -786,17 +832,17 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con
     arect.Normalize();
     arect.Inflate( aAccuracy );
 
-    EDA_RECT shapeRect;
+    wxPoint shapePos = ShapePos();
 
-    shapeRect.SetOrigin( ShapePos() );
-    shapeRect.Inflate( GetSize().x / 2, GetSize().y / 2 );
+    EDA_RECT shapeRect;
 
     EDA_RECT bb = GetBoundingBox();
 
-    if( !arect.Intersects( bb ) )
-            return false;
+    wxPoint endCenter;
+    int radius;
 
-    int dist;
+    if( !arect.Intersects( bb ) )
+        return false;
 
     // This covers total containment for all test cases
     if( arect.Contains( bb ) )
@@ -807,8 +853,57 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con
     case PAD_SHAPE_CIRCLE:
         return arect.IntersectsCircle( GetPosition(), GetBoundingRadius() );
     case PAD_SHAPE_RECT:
+        shapeRect.SetOrigin( shapePos );
+        shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 );
         return arect.Intersects( shapeRect, m_Orient );
     case PAD_SHAPE_OVAL:
+
+        // Circlular test if dimensions are equal
+        if( m_Size.x == m_Size.y )
+            return arect.IntersectsCircle( shapePos, GetBoundingRadius() );
+
+        shapeRect.SetOrigin( shapePos );
+
+        // Horizontal dimension is greater
+        if( m_Size.x > m_Size.y )
+        {
+            radius = m_Size.y / 2;
+
+            shapeRect.Inflate( m_Size.x / 2 - radius, radius );
+
+            endCenter = wxPoint( m_Size.x / 2 - radius, 0 );
+            RotatePoint( &endCenter, m_Orient );
+
+            // Test circular ends
+            if( arect.IntersectsCircle( shapePos + endCenter, radius ) ||
+                arect.IntersectsCircle( shapePos - endCenter, radius ) )
+            {
+                return true;
+            }
+        }
+        else
+        {
+            radius = m_Size.x / 2;
+
+            shapeRect.Inflate( radius, m_Size.y / 2 - radius );
+
+            endCenter = wxPoint( 0, m_Size.y / 2 - radius );
+            RotatePoint( &endCenter, m_Orient );
+
+            // Test circular ends
+            if( arect.IntersectsCircle( shapePos + endCenter, radius ) ||
+                arect.IntersectsCircle( shapePos - endCenter, radius ) )
+            {
+                return true;
+            }
+        }
+
+        // Test rectangular portion between rounded ends
+        if( arect.Intersects( shapeRect, m_Orient ) )
+        {
+            return true;
+        }
+
         break;
     case PAD_SHAPE_TRAPEZOID:
         break;
-- 
2.7.4

From 428f0f4a2c5a62f0c3dc5ccec7c2ba517daf55da Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Mon, 24 Apr 2017 22:54:44 +1000
Subject: [PATCH 07/12] HitTest for RoundRect pad

---
 pcbnew/class_pad.cpp | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 59 insertions(+), 1 deletion(-)

diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp
index 958e969..096d22f 100644
--- a/pcbnew/class_pad.cpp
+++ b/pcbnew/class_pad.cpp
@@ -749,7 +749,7 @@ void D_PAD::GetOblongDrillGeometry( wxPoint& aStartPoint,
 
 bool D_PAD::HitTest( const wxPoint& aPosition ) const
 {
-    int     dx, dy;
+    int dx, dy;
 
     wxPoint shape_pos = ShapePos();
 
@@ -826,6 +826,9 @@ bool D_PAD::HitTest( const wxPoint& aPosition ) const
     return false;
 }
 
+/**
+ * Test if an x,y axis-aligned rectangle hits this pad
+ */
 bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
 {
     EDA_RECT arect = aRect;
@@ -836,6 +839,8 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con
 
     EDA_RECT shapeRect;
 
+    int r;
+
     EDA_RECT bb = GetBoundingBox();
 
     wxPoint endCenter;
@@ -908,6 +913,59 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con
     case PAD_SHAPE_TRAPEZOID:
         break;
     case PAD_SHAPE_ROUNDRECT:
+        /* RoundRect intersection can be broken up into simple tests:
+         * a) Test intersection of horizontal rect
+         * b) Test intersection of vertical rect
+         * c) Test intersection of each corner
+         */
+
+
+        r = GetRoundRectCornerRadius();
+
+        /* Test A - intersection of horizontal rect */
+        shapeRect.SetSize( 0, 0 );
+        shapeRect.SetOrigin( shapePos );
+        shapeRect.Inflate( m_Size.x / 2, m_Size.y / 2 - r );
+
+        // Short-circuit test for zero width or height
+        if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 &&
+            arect.Intersects( shapeRect, m_Orient ) )
+        {
+            return true;
+        }
+
+        /* Test B - intersection of vertical rect */
+        shapeRect.SetSize( 0, 0 );
+        shapeRect.SetOrigin( shapePos );
+        shapeRect.Inflate( m_Size.x / 2 - r, m_Size.y / 2 );
+
+        // Short-circuit test for zero width or height
+        if( shapeRect.GetWidth() > 0 && shapeRect.GetHeight() > 0 &&
+            arect.Intersects( shapeRect, m_Orient ) )
+        {
+            return true;
+        }
+
+        /* Test C - intersection of each corner */
+
+        endCenter = wxPoint( m_Size.x / 2 - r, m_Size.y / 2 - r );
+        RotatePoint( &endCenter, m_Orient );
+
+        if( arect.IntersectsCircle( shapePos + endCenter, r ) ||
+            arect.IntersectsCircle( shapePos - endCenter, r ) )
+        {
+            return true;
+        }
+
+        endCenter = wxPoint( m_Size.x / 2 - r, -m_Size.y / 2 + r );
+        RotatePoint( &endCenter, m_Orient );
+
+        if( arect.IntersectsCircle( shapePos + endCenter, r ) ||
+            arect.IntersectsCircle( shapePos - endCenter, r ) )
+        {
+            return true;
+        }
+
         break;
     default:
         break;
-- 
2.7.4

From f6cd8ef81450ce1572982275197f3ecae3572751 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Wed, 26 Apr 2017 19:05:44 +1000
Subject: [PATCH 08/12] HitTest for trapezoid pad

---
 pcbnew/class_pad.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 51 insertions(+), 3 deletions(-)

diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp
index 096d22f..300e642 100644
--- a/pcbnew/class_pad.cpp
+++ b/pcbnew/class_pad.cpp
@@ -280,14 +280,18 @@ const EDA_RECT D_PAD::GetBoundingBox() const
         break;
 
     case PAD_SHAPE_TRAPEZOID:
-        //Use the four corners and track their rotation
+        // Use the four corners and track their rotation
         // (Trapezoids will not be symmetric)
+
         quadrant1.x =  (m_Size.x + m_DeltaSize.y)/2;
         quadrant1.y =  (m_Size.y - m_DeltaSize.x)/2;
+
         quadrant2.x = -(m_Size.x + m_DeltaSize.y)/2;
         quadrant2.y =  (m_Size.y + m_DeltaSize.x)/2;
+
         quadrant3.x = -(m_Size.x - m_DeltaSize.y)/2;
         quadrant3.y = -(m_Size.y + m_DeltaSize.x)/2;
+
         quadrant4.x =  (m_Size.x - m_DeltaSize.y)/2;
         quadrant4.y = -(m_Size.y - m_DeltaSize.x)/2;
 
@@ -301,7 +305,7 @@ const EDA_RECT D_PAD::GetBoundingBox() const
         dx = std::max( quadrant1.x, std::max( quadrant2.x, std::max( quadrant3.x, quadrant4.x) ) );
         dy = std::max( quadrant1.y, std::max( quadrant2.y, std::max( quadrant3.y, quadrant4.y) ) );
 
-        area.SetOrigin( m_Pos.x+x, m_Pos.y+y );
+        area.SetOrigin( ShapePos().x + x, ShapePos().y + y );
         area.SetSize( dx-x, dy-y );
         break;
 
@@ -777,6 +781,7 @@ bool D_PAD::HitTest( const wxPoint& aPosition ) const
         wxPoint poly[4];
         BuildPadPolygon( poly, wxSize(0,0), 0 );
         RotatePoint( &delta, -m_Orient );
+
         return TestPointInsidePolygon( poly, 4, delta );
     }
 
@@ -911,7 +916,50 @@ bool D_PAD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) con
 
         break;
     case PAD_SHAPE_TRAPEZOID:
-        break;
+        /* Trapezoid intersection tests:
+         * A) Any points of rect inside trapezoid
+         * B) Any points of trapezoid inside rect
+         * C) Any sides of trapezoid cross rect
+         */
+        {
+
+        wxPoint poly[4];
+        BuildPadPolygon( poly, wxSize( 0, 0 ), 0 );
+
+        wxPoint corners[4];
+
+        corners[0] = wxPoint( arect.GetLeft(),  arect.GetTop() );
+        corners[1] = wxPoint( arect.GetRight(), arect.GetTop() );
+        corners[2] = wxPoint( arect.GetRight(), arect.GetBottom() );
+        corners[3] = wxPoint( arect.GetLeft(),  arect.GetBottom() );
+
+        for( int i=0; i<4; i++ )
+        {
+            RotatePoint( &poly[i], m_Orient );
+            poly[i] += shapePos;
+        }
+
+        for( int ii=0; ii<4; ii++ )
+        {
+            if( TestPointInsidePolygon( poly, 4, corners[ii] ) )
+            {
+                return true;
+            }
+
+            if( arect.Contains( poly[ii] ) )
+            {
+                return true;
+            }
+
+            if( arect.Intersects( poly[ii], poly[(ii+1) % 4] ) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+
+        }
     case PAD_SHAPE_ROUNDRECT:
         /* RoundRect intersection can be broken up into simple tests:
          * a) Test intersection of horizontal rect
-- 
2.7.4

From cb1b21a9e33f9bf85ff2131a0cd05d5fc8c118c4 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Wed, 26 Apr 2017 22:38:41 +1000
Subject: [PATCH 09/12] Updated HitTest for PCB_TEXT and TEXT_MOD

---
 pcbnew/class_pcb_text.h   |  4 ++--
 pcbnew/class_text_mod.cpp | 19 -------------------
 pcbnew/class_text_mod.h   | 10 +++++++++-
 3 files changed, 11 insertions(+), 22 deletions(-)

diff --git a/pcbnew/class_pcb_text.h b/pcbnew/class_pcb_text.h
index 3a2e0e5..ae54a6a 100644
--- a/pcbnew/class_pcb_text.h
+++ b/pcbnew/class_pcb_text.h
@@ -81,7 +81,7 @@ public:
 
     void GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) override;
 
-    bool HitTest( const wxPoint& aPosition ) const override
+    virtual bool HitTest( const wxPoint& aPosition ) const override
     {
         return TextHitTest( aPosition );
     }
@@ -89,7 +89,7 @@ public:
     /** @copydoc BOARD_ITEM::HitTest(const EDA_RECT& aRect,
      *                               bool aContained = true, int aAccuracy ) const
      */
-    bool HitTest( const EDA_RECT& aRect, bool aContained = true, int aAccuracy = 0 ) const override
+    virtual bool HitTest( const EDA_RECT& aRect, bool aContained = true, int aAccuracy = 0 ) const override
     {
         return TextHitTest( aRect, aContained, aAccuracy );
     }
diff --git a/pcbnew/class_text_mod.cpp b/pcbnew/class_text_mod.cpp
index 0e6bbb7..8aec446 100644
--- a/pcbnew/class_text_mod.cpp
+++ b/pcbnew/class_text_mod.cpp
@@ -181,25 +181,6 @@ void TEXTE_MODULE::SetLocalCoord()
     }
 }
 
-
-bool TEXTE_MODULE::HitTest( const wxPoint& aPosition ) const
-{
-    wxPoint  rel_pos;
-    EDA_RECT area = GetTextBox( -1, -1 );
-
-    /* Rotate refPos to - angle to test if refPos is within area (which
-     * is relative to an horizontal text)
-     */
-    rel_pos = aPosition;
-    RotatePoint( &rel_pos, GetTextPos(), -GetDrawRotation() );
-
-    if( area.Contains( rel_pos ) )
-        return true;
-
-    return false;
-}
-
-
 const EDA_RECT TEXTE_MODULE::GetBoundingBox() const
 {
     double   angle = GetDrawRotation();
diff --git a/pcbnew/class_text_mod.h b/pcbnew/class_text_mod.h
index df07ac8..9c4c9ae 100644
--- a/pcbnew/class_text_mod.h
+++ b/pcbnew/class_text_mod.h
@@ -184,7 +184,15 @@ public:
 
     void GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) override;
 
-    bool HitTest( const wxPoint& aPosition ) const override;
+    virtual bool HitTest( const wxPoint& aPosition ) const override
+    {
+        return TextHitTest( aPosition );
+    }
+
+    virtual bool HitTest( const EDA_RECT& aRect, bool aContained = false, int aAccuracy = 0 ) const override
+    {
+        return TextHitTest( aRect, aContained, aAccuracy );
+    }
 
     wxString GetClass() const override
     {
-- 
2.7.4

From 81202eefe9330817e8e0e0cf6b1ac60b3720cfcc Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Mon, 1 May 2017 22:10:24 +1000
Subject: [PATCH 10/12] Fixed HitTest for ZONE_CONTAINER

---
 pcbnew/class_zone.cpp | 55 ++++++++++++++++++++++++++++++---------------------
 1 file changed, 32 insertions(+), 23 deletions(-)

diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp
index ccaa1db..48aa445 100644
--- a/pcbnew/class_zone.cpp
+++ b/pcbnew/class_zone.cpp
@@ -539,53 +539,62 @@ bool ZONE_CONTAINER::HitTestForEdge( const wxPoint& refPos ) const
 
 bool ZONE_CONTAINER::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
 {
-    // Convert to BOX2I
-    BOX2I aBox = aRect;
-    aBox.Inflate( aAccuracy );
-    BOX2I bbox = m_Poly->BBox();
+    // Calculate bounding box for zone
+    EDA_RECT bbox = GetBoundingBox();
     bbox.Normalize();
 
+    EDA_RECT arect = aRect;
+    arect.Normalize();
+    arect.Inflate( aAccuracy );
+
     if( aContained )
-         return aBox.Contains( bbox );
+    {
+         return arect.Contains( bbox );
+    }
     else    // Test for intersection between aBox and the polygon
             // For a polygon, using its bounding box has no sense here
     {
         // Fast test: if aBox is outside the polygon bounding box,
         // rectangles cannot intersect
-        if( ! bbox.Intersects( aBox ) )
+        if( !arect.Intersects( bbox ) )
             return false;
 
         // aBox is inside the polygon bounding box,
         // and can intersect the polygon: use a fine test.
         // aBox intersects the polygon if at least one aBox corner
         // is inside the polygon
-        wxPoint corner = static_cast<wxPoint>( aBox.GetOrigin() );
-
-        if( HitTestInsideZone( corner ) )
-            return true;
-
-        corner.x = aBox.GetEnd().x;
-
-        if( HitTestInsideZone( corner ) )
-            return true;
-
-        corner = static_cast<wxPoint>( aBox.GetEnd() );
-
-        if( HitTestInsideZone( corner ) )
-            return true;
+        wxPoint origin = arect.GetOrigin();
 
-        corner.x = aBox.GetOrigin().x;
+        int w = arect.GetWidth();
+        int h = arect.GetHeight();
 
-        if( HitTestInsideZone( corner ) )
+        if ( HitTestInsideZone( origin ) ||
+             HitTestInsideZone( origin + wxPoint( w, 0 ) ) ||
+             HitTestInsideZone( origin + wxPoint( w, h ) ) ||
+             HitTestInsideZone( origin + wxPoint( 0, h ) ) )
+        {
             return true;
+        }
 
         // No corner inside aBox, but outlines can intersect aBox
         // if one of outline corners is inside aBox
         int count = m_Poly->TotalVertices();
         for( int ii =0; ii < count; ii++ )
         {
-            if( aBox.Contains( m_Poly->Vertex( ii ) ) )
+            auto vertex = m_Poly->Vertex( ii );
+            auto vertexNext = m_Poly->Vertex( ( ii + 1 ) % count );
+
+            // Test if the point is within the rect
+            if( arect.Contains( ( wxPoint ) vertex ) )
+            {
                 return true;
+            }
+
+            // Test if this edge intersects the rect
+            if( arect.Intersects( ( wxPoint ) vertex, ( wxPoint ) vertexNext ) )
+            {
+                return true;
+            }
         }
 
         return false;
-- 
2.7.4

From a19355b9e87ed81de3e66297719bb52758da2208 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Tue, 2 May 2017 16:44:41 +1000
Subject: [PATCH 11/12] Fixed HitTest for text and modules

---
 include/eda_text.h        |  4 ++--
 pcbnew/class_module.cpp   | 22 +++++++++++++++++++++-
 pcbnew/class_text_mod.cpp | 29 +++++++++++++++++++++++++++++
 pcbnew/class_text_mod.h   |  4 ++++
 4 files changed, 56 insertions(+), 3 deletions(-)

diff --git a/include/eda_text.h b/include/eda_text.h
index a20aacc..e0bbf59 100644
--- a/include/eda_text.h
+++ b/include/eda_text.h
@@ -260,7 +260,7 @@ public:
      * @param aAccuracy - Amount to inflate the bounding box.
      * @return bool - true if a hit, else false
      */
-    bool TextHitTest( const wxPoint& aPoint, int aAccuracy = 0 ) const;
+    virtual bool TextHitTest( const wxPoint& aPoint, int aAccuracy = 0 ) const;
 
     /**
      * Function TextHitTest (overloaded)
@@ -271,7 +271,7 @@ public:
      * @param aAccuracy - Amount to inflate the bounding box.
      * @return bool - true if a hit, else false
      */
-    bool TextHitTest( const EDA_RECT& aRect, bool aContains = false, int aAccuracy = 0 ) const;
+    virtual bool TextHitTest( const EDA_RECT& aRect, bool aContains = false, int aAccuracy = 0 ) const;
 
     /**
      * Function LenSize
diff --git a/pcbnew/class_module.cpp b/pcbnew/class_module.cpp
index ecfcd9a..b79ae81 100644
--- a/pcbnew/class_module.cpp
+++ b/pcbnew/class_module.cpp
@@ -614,7 +614,27 @@ bool MODULE::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) co
     if( aContained )
         return arect.Contains( m_BoundaryBox );
     else
-        return m_BoundaryBox.Intersects( arect );
+    {
+        // If the rect does not intersect the bounding box, skip any tests
+        if( !aRect.Intersects( GetBoundingBox() ) )
+            return false;
+
+        // Determine if any elements in the MODULE intersect the rect
+        for( D_PAD* pad = m_Pads; pad; pad = pad->Next() )
+        {
+            if( pad->HitTest( arect, false, 0 ) )
+                return true;
+        }
+
+        for( BOARD_ITEM* item = m_Drawings; item; item = item->Next() )
+        {
+            if( item->HitTest( arect, false, 0 ) )
+                return true;
+        }
+
+        // No items were hit
+        return false;
+    }
 }
 
 
diff --git a/pcbnew/class_text_mod.cpp b/pcbnew/class_text_mod.cpp
index 8aec446..9d807b5 100644
--- a/pcbnew/class_text_mod.cpp
+++ b/pcbnew/class_text_mod.cpp
@@ -89,6 +89,35 @@ void TEXTE_MODULE::SetTextAngle( double aAngle )
     EDA_TEXT::SetTextAngle( NormalizeAngle360( aAngle ) );
 }
 
+bool TEXTE_MODULE::TextHitTest( const wxPoint& aPoint, int aAccuracy ) const
+{
+    EDA_RECT rect = GetTextBox( -1 );
+    wxPoint location = aPoint;
+
+    rect.Inflate( aAccuracy );
+
+    RotatePoint( &location, GetTextPos(), -GetDrawRotation() );
+
+    return rect.Contains( location );
+}
+
+bool TEXTE_MODULE::TextHitTest( const EDA_RECT& aRect, bool aContains, int aAccuracy ) const
+{
+    EDA_RECT rect = aRect;
+
+    rect.Inflate( aAccuracy );
+
+    if( aContains )
+    {
+        return rect.Contains( GetBoundingBox() );
+    }
+    else
+    {
+        return rect.Intersects( GetTextBox( -1 ), GetDrawRotation() );
+    }
+
+}
+
 
 void TEXTE_MODULE::Rotate( const wxPoint& aRotCentre, double aAngle )
 {
diff --git a/pcbnew/class_text_mod.h b/pcbnew/class_text_mod.h
index 9c4c9ae..80d24b7 100644
--- a/pcbnew/class_text_mod.h
+++ b/pcbnew/class_text_mod.h
@@ -184,6 +184,10 @@ public:
 
     void GetMsgPanelInfo( std::vector< MSG_PANEL_ITEM >& aList ) override;
 
+    virtual bool TextHitTest( const wxPoint& aPoint, int aAccuracy = 0 ) const override;
+
+    virtual bool TextHitTest( const EDA_RECT& aRect, bool aContains = false, int aAccuracy = 0 ) const override;
+
     virtual bool HitTest( const wxPoint& aPosition ) const override
     {
         return TextHitTest( aPosition );
-- 
2.7.4

From ad33940b358b4aaf07e98b2e2101d5eb3b184c9c Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@xxxxxxxxx>
Date: Tue, 2 May 2017 17:14:07 +1000
Subject: [PATCH 12/12] Adjusted selection for ZONE

---
 pcbnew/class_zone.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp
index 48aa445..0c4f4e3 100644
--- a/pcbnew/class_zone.cpp
+++ b/pcbnew/class_zone.cpp
@@ -563,11 +563,14 @@ bool ZONE_CONTAINER::HitTest( const EDA_RECT& aRect, bool aContained, int aAccur
         // and can intersect the polygon: use a fine test.
         // aBox intersects the polygon if at least one aBox corner
         // is inside the polygon
+
+        /*
         wxPoint origin = arect.GetOrigin();
 
         int w = arect.GetWidth();
         int h = arect.GetHeight();
 
+
         if ( HitTestInsideZone( origin ) ||
              HitTestInsideZone( origin + wxPoint( w, 0 ) ) ||
              HitTestInsideZone( origin + wxPoint( w, h ) ) ||
@@ -575,6 +578,7 @@ bool ZONE_CONTAINER::HitTest( const EDA_RECT& aRect, bool aContained, int aAccur
         {
             return true;
         }
+        */
 
         // No corner inside aBox, but outlines can intersect aBox
         // if one of outline corners is inside aBox
-- 
2.7.4


Follow ups