From 40abb013ec4bffb28e16ada170e3d6416b9f4744 Mon Sep 17 00:00:00 2001
From: Jeff Young <jeff@rokeby.ie>
Date: Tue, 20 Jun 2023 18:12:55 +0100
Subject: [PATCH] Don't consider a group to be on a copper layer.

Its members can be on copper layers, but the group itself isn't on any
layer.

Also fixes a bug where we were trying to clone TRIANGULATED_POLYGON::TRI
shapes as indexable sub-shapes.  (The TRI only has indexes into its
parent, so cloning it will only result in segfaults down the line.)

Also fixes a bug where we weren't including copper items inside groups
when checking footprint net ties.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/15021
---
 include/pcb_group.h                           |  6 ++++++
 libs/kimath/include/geometry/shape_compound.h |  6 +++---
 pcbnew/footprint.cpp                          | 10 ++++++++++
 3 files changed, 19 insertions(+), 3 deletions(-)

diff --git a/include/pcb_group.h b/include/pcb_group.h
index b306b0da84..59aeea3beb 100644
--- a/include/pcb_group.h
+++ b/include/pcb_group.h
@@ -124,6 +124,12 @@ public:
         wxFAIL_MSG( wxT( "groups don't support layer SetLayer" ) );
     }
 
+    bool IsOnCopperLayer() const override
+    {
+        // A group might have members on a copper layer, but isn't itself on any layer.
+        return false;
+    }
+
     /** Set layer for all items within the group.
      *
      * To avoid freezes with circular references, the maximum depth is 20 by default.
diff --git a/libs/kimath/include/geometry/shape_compound.h b/libs/kimath/include/geometry/shape_compound.h
index c4c333cb03..0e26bd70ed 100644
--- a/libs/kimath/include/geometry/shape_compound.h
+++ b/libs/kimath/include/geometry/shape_compound.h
@@ -81,7 +81,7 @@ public:
     void AddShape( SHAPE* aShape )
     {
         // Don't make clients deal with nested SHAPE_COMPOUNDs
-        if( aShape->HasIndexableSubshapes() )
+        if( dynamic_cast<SHAPE_COMPOUND*>( aShape ) )
         {
             std::vector<const SHAPE*> subshapes;
             aShape->GetIndexableSubshapes( subshapes );
@@ -102,7 +102,7 @@ public:
     void AddShape( std::shared_ptr<SHAPE> aShape )
     {
         // Don't make clients deal with nested SHAPE_COMPOUNDs
-        if( aShape->HasIndexableSubshapes() )
+        if( dynamic_cast<SHAPE_COMPOUND*>( aShape.get() ) )
         {
             std::vector<const SHAPE*> subshapes;
             aShape->GetIndexableSubshapes( subshapes );
@@ -125,7 +125,7 @@ public:
 
     int Size() const
     {
-        return m_shapes.size();
+        return (int) m_shapes.size();
     }
 
     void Rotate( const EDA_ANGLE& aAngle, const VECTOR2I& aCenter = { 0, 0 } ) override;
diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp
index e869ab3744..4c6a9fc636 100644
--- a/pcbnew/footprint.cpp
+++ b/pcbnew/footprint.cpp
@@ -2417,7 +2417,17 @@ void FOOTPRINT::CheckNetTies( const std::function<void( const BOARD_ITEM* aItem,
     for( BOARD_ITEM* item : m_drawings )
     {
         if( item->IsOnCopperLayer() )
+        {
             copperItems.push_back( item );
+        }
+        else if( PCB_GROUP* group = dynamic_cast<PCB_GROUP*>( item ) )
+        {
+            group->RunOnDescendants( [&]( BOARD_ITEM* descendent )
+                                     {
+                                         if( descendent->IsOnCopperLayer() )
+                                             copperItems.push_back( descendent );
+                                     } );
+        }
     }
 
     for( ZONE* zone : m_zones )