From 215533f31afafcf4b3e6d5a4e265a769ab706265 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@gmail.com>
Date: Fri, 27 Sep 2024 09:08:31 +0100
Subject: [PATCH] Unify flip direction handling

There was a gentle mish-mash of booleans, some with
true being left/right and some up/down, and some functions that
can flip in both axes (which is never actually done, and doesn't
really make geometric sense).

Replace all this with the FLIP_DIRECTION enum class, which makes
the intention completely unambiguous.

This also then allows a small scattering of simplifications,
because everything takes the same type and you don't have to
fiddle booleans to fit.
---
 3d-viewer/dialogs/panel_preview_3d_model.cpp  |   2 +-
 common/bitmap_base.cpp                        |  11 +-
 common/eda_shape.cpp                          |  61 +++-------
 common/widgets/footprint_diff_widget.cpp      |   4 +-
 eeschema/sch_bitmap.cpp                       |   4 +-
 gerbview/export_to_pcbnew.cpp                 |   4 +-
 gerbview/gerber_draw_item.cpp                 |   2 +-
 include/bitmap_base.h                         |  14 +--
 include/board_item.h                          |  15 +--
 include/core/mirror.h                         |  29 ++++-
 include/eda_shape.h                           |  10 +-
 include/settings/app_settings.h               |   9 --
 libs/kimath/include/geometry/shape_arc.h      |   8 +-
 .../include/geometry/shape_line_chain.h       |   5 +-
 libs/kimath/include/geometry/shape_poly_set.h |   6 +-
 libs/kimath/src/geometry/shape_arc.cpp        |   7 +-
 libs/kimath/src/geometry/shape_line_chain.cpp |   9 +-
 libs/kimath/src/geometry/shape_poly_set.cpp   |   4 +-
 pcbnew/board_item.cpp                         |   2 +-
 .../dialogs/dialog_footprint_properties.cpp   |   5 +-
 pcbnew/dialogs/dialog_pad_properties.cpp      |   4 +-
 pcbnew/dialogs/panel_edit_options.cpp         |   7 +-
 .../drc/drc_test_provider_library_parity.cpp  |   2 +-
 pcbnew/exporters/export_gencad_writer.cpp     |   4 +-
 pcbnew/footprint.cpp                          |  19 ++--
 pcbnew/footprint.h                            |   2 +-
 pcbnew/footprint_editor_settings.cpp          |   3 +-
 pcbnew/footprint_viewer_frame.cpp             |   2 +-
 pcbnew/generators/pcb_tuning_pattern.cpp      |  13 +--
 pcbnew/load_select_footprint.cpp              |   5 +-
 pcbnew/pad.cpp                                |  26 ++---
 pcbnew/pad.h                                  |  14 ++-
 pcbnew/pcb_base_frame.cpp                     |   2 +-
 pcbnew/pcb_dimension.cpp                      |  36 ++----
 pcbnew/pcb_dimension.h                        |   8 +-
 pcbnew/pcb_edit_frame.cpp                     |   2 +-
 pcbnew/pcb_generator.cpp                      |  13 +--
 pcbnew/pcb_generator.h                        |   2 +-
 pcbnew/pcb_group.cpp                          |   4 +-
 pcbnew/pcb_group.h                            |   2 +-
 .../cadstar/cadstar_pcb_archive_loader.cpp    |   6 +-
 pcbnew/pcb_io/eagle/pcb_io_eagle.cpp          |   2 +-
 .../easyedapro/pcb_io_easyedapro_parser.cpp   |  11 +-
 pcbnew/pcb_io/fabmaster/import_fabmaster.cpp  |  10 +-
 pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp      |   4 +-
 .../pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp |   4 +-
 pcbnew/pcb_io/odbpp/odb_eda_data.cpp          |   2 +-
 pcbnew/pcb_marker.cpp                         |   2 +-
 pcbnew/pcb_marker.h                           |   2 +-
 pcbnew/pcb_reference_image.cpp                |  14 +--
 pcbnew/pcb_reference_image.h                  |   2 +-
 pcbnew/pcb_shape.cpp                          |  35 ++----
 pcbnew/pcb_shape.h                            |  11 +-
 pcbnew/pcb_table.cpp                          |   4 +-
 pcbnew/pcb_table.h                            |   2 +-
 pcbnew/pcb_target.cpp                         |   4 +-
 pcbnew/pcb_target.h                           |   2 +-
 pcbnew/pcb_text.cpp                           |   8 +-
 pcbnew/pcb_text.h                             |   4 +-
 pcbnew/pcb_textbox.cpp                        |  12 +-
 pcbnew/pcb_textbox.h                          |   4 +-
 pcbnew/pcb_track.cpp                          |  43 +++-----
 pcbnew/pcb_track.h                            |  10 +-
 pcbnew/pcbnew_settings.cpp                    |   7 +-
 pcbnew/pcbnew_settings.h                      |   5 +-
 .../specctra_export.cpp                       |   4 +-
 .../specctra_import.cpp                       |   4 +-
 pcbnew/tools/board_editor_control.cpp         |   2 +-
 pcbnew/tools/edit_tool.cpp                    | 104 ++++--------------
 pcbnew/tools/edit_tool_move_fct.cpp           |   4 +-
 pcbnew/tools/pcb_tool_base.cpp                |   3 +-
 pcbnew/zone.cpp                               |  10 +-
 pcbnew/zone.h                                 |   7 +-
 qa/tests/common/test_bitmap_base.cpp          |   4 +-
 qa/tests/pcbnew/test_board_item.cpp           |   8 +-
 75 files changed, 293 insertions(+), 448 deletions(-)

diff --git a/3d-viewer/dialogs/panel_preview_3d_model.cpp b/3d-viewer/dialogs/panel_preview_3d_model.cpp
index 19f50289e1..c5f967023b 100644
--- a/3d-viewer/dialogs/panel_preview_3d_model.cpp
+++ b/3d-viewer/dialogs/panel_preview_3d_model.cpp
@@ -117,7 +117,7 @@ PANEL_PREVIEW_3D_MODEL::PANEL_PREVIEW_3D_MODEL( wxWindow* aParent, PCB_BASE_FRAM
     // Ensure the footprint is shown like in Fp editor: rot 0, not flipped
     // to avoid mistakes when setting the3D shape position/rotation
     if( m_dummyFootprint->IsFlipped() )
-        m_dummyFootprint->Flip( m_dummyFootprint->GetPosition(), false );
+        m_dummyFootprint->Flip( m_dummyFootprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
 
     m_dummyFootprint->SetOrientation( ANGLE_0 );
 
diff --git a/common/bitmap_base.cpp b/common/bitmap_base.cpp
index 7908b697da..d3f61f0734 100644
--- a/common/bitmap_base.cpp
+++ b/common/bitmap_base.cpp
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2017 jean-pierre.charras
- * Copyright (C) 2011-2023 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2011-2024 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
@@ -22,7 +22,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#include <bitmap_base.h>
+#include "bitmap_base.h"
+
 #include <gr_basic.h>
 #include <math/util.h>    // for KiROUND
 #include <memory>         // for make_unique, unique_ptr
@@ -408,7 +409,7 @@ VECTOR2I BITMAP_BASE::GetSize() const
 }
 
 
-void BITMAP_BASE::Mirror( bool aVertically )
+void BITMAP_BASE::Mirror( FLIP_DIRECTION aFlipDirection )
 {
     if( m_image )
     {
@@ -420,13 +421,13 @@ void BITMAP_BASE::Mirror( bool aVertically )
         int resY = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONY );
         int unit = m_image->GetOptionInt( wxIMAGE_OPTION_RESOLUTIONUNIT );
 
-        *m_image = m_image->Mirror( not aVertically );
+        *m_image = m_image->Mirror( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT );
 
         m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONUNIT , unit);
         m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONX, resX);
         m_image->SetOption( wxIMAGE_OPTION_RESOLUTIONY, resY);
 
-        if( aVertically )
+        if( aFlipDirection == FLIP_DIRECTION::TOP_BOTTOM )
             m_isMirroredY = !m_isMirroredY;
         else
             m_isMirroredX = !m_isMirroredX;
diff --git a/common/eda_shape.cpp b/common/eda_shape.cpp
index a4fe693eb9..0a2e5e68c2 100644
--- a/common/eda_shape.cpp
+++ b/common/eda_shape.cpp
@@ -428,73 +428,38 @@ void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void EDA_SHAPE::flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void EDA_SHAPE::flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
     switch ( m_shape )
     {
     case SHAPE_T::SEGMENT:
     case SHAPE_T::RECTANGLE:
-        if( aFlipLeftRight )
-        {
-            m_start.x = aCentre.x - ( m_start.x - aCentre.x );
-            m_end.x   = aCentre.x - ( m_end.x - aCentre.x );
-        }
-        else
-        {
-            m_start.y = aCentre.y - ( m_start.y - aCentre.y );
-            m_end.y   = aCentre.y - ( m_end.y - aCentre.y );
-        }
+        MIRROR( m_start, aCentre, aFlipDirection );
+        MIRROR( m_end, aCentre, aFlipDirection );
         break;
 
     case SHAPE_T::CIRCLE:
-        if( aFlipLeftRight )
-        {
-            m_start.x = aCentre.x - ( m_start.x - aCentre.x );
-            m_end.x   = aCentre.x - ( m_end.x - aCentre.x );
-        }
-        else
-        {
-            m_start.y = aCentre.y - ( m_start.y - aCentre.y );
-            m_end.y   = aCentre.y - ( m_end.y - aCentre.y );
-        }
+        MIRROR( m_start, aCentre, aFlipDirection );
+        MIRROR( m_end, aCentre, aFlipDirection );
         break;
 
     case SHAPE_T::ARC:
-        if( aFlipLeftRight )
-        {
-            m_start.x     = aCentre.x - ( m_start.x - aCentre.x );
-            m_end.x       = aCentre.x - ( m_end.x - aCentre.x );
-            m_arcCenter.x = aCentre.x - ( m_arcCenter.x - aCentre.x );
-        }
-        else
-        {
-            m_start.y     = aCentre.y - ( m_start.y - aCentre.y );
-            m_end.y       = aCentre.y - ( m_end.y - aCentre.y );
-            m_arcCenter.y = aCentre.y - ( m_arcCenter.y - aCentre.y );
-        }
+        MIRROR( m_start, aCentre, aFlipDirection );
+        MIRROR( m_end, aCentre, aFlipDirection );
+        MIRROR( m_arcCenter, aCentre, aFlipDirection );
 
         std::swap( m_start, m_end );
         break;
 
     case SHAPE_T::POLY:
-        m_poly.Mirror( aFlipLeftRight, !aFlipLeftRight, aCentre );
+        m_poly.Mirror( aCentre, aFlipDirection );
         break;
 
     case SHAPE_T::BEZIER:
-        if( aFlipLeftRight )
-        {
-            m_start.x    = aCentre.x - ( m_start.x - aCentre.x );
-            m_end.x      = aCentre.x - ( m_end.x - aCentre.x );
-            m_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x );
-            m_bezierC2.x = aCentre.x - ( m_bezierC2.x - aCentre.x );
-        }
-        else
-        {
-            m_start.y    = aCentre.y - ( m_start.y - aCentre.y );
-            m_end.y      = aCentre.y - ( m_end.y - aCentre.y );
-            m_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y );
-            m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
-        }
+        MIRROR( m_start, aCentre, aFlipDirection );
+        MIRROR( m_end, aCentre, aFlipDirection );
+        MIRROR( m_bezierC1, aCentre, aFlipDirection );
+        MIRROR( m_bezierC2, aCentre, aFlipDirection );
 
         RebuildBezierToSegmentsPointsList( m_stroke.GetWidth() / 2 );
         break;
diff --git a/common/widgets/footprint_diff_widget.cpp b/common/widgets/footprint_diff_widget.cpp
index 6335273ccb..5d82ab7f20 100644
--- a/common/widgets/footprint_diff_widget.cpp
+++ b/common/widgets/footprint_diff_widget.cpp
@@ -74,10 +74,10 @@ void FOOTPRINT_DIFF_WIDGET::DisplayDiff( FOOTPRINT* aBoardFootprint,
     m_boardItemCopy->Move( -m_boardItemCopy->GetPosition() );
 
     if( m_boardItemCopy->IsFlipped() )
-        m_boardItemCopy->Flip( {0,0}, false );
+        m_boardItemCopy->Flip( { 0, 0 }, FLIP_DIRECTION::TOP_BOTTOM );
 
     if( m_boardItemCopy->GetOrientation() != ANGLE_0 )
-        m_boardItemCopy->Rotate( {0,0}, -m_boardItemCopy->GetOrientation() );
+        m_boardItemCopy->Rotate( { 0, 0 }, -m_boardItemCopy->GetOrientation() );
 
     m_libraryItem = aLibFootprint;
 
diff --git a/eeschema/sch_bitmap.cpp b/eeschema/sch_bitmap.cpp
index a713f5f202..0c1fa6df8e 100644
--- a/eeschema/sch_bitmap.cpp
+++ b/eeschema/sch_bitmap.cpp
@@ -152,14 +152,14 @@ VECTOR2I SCH_BITMAP::GetSize() const
 void SCH_BITMAP::MirrorVertically( int aCenter )
 {
     MIRROR( m_pos.y, aCenter );
-    m_bitmapBase->Mirror( true );
+    m_bitmapBase->Mirror( FLIP_DIRECTION::TOP_BOTTOM );
 }
 
 
 void SCH_BITMAP::MirrorHorizontally( int aCenter )
 {
     MIRROR( m_pos.x, aCenter );
-    m_bitmapBase->Mirror( false );
+    m_bitmapBase->Mirror( FLIP_DIRECTION::LEFT_RIGHT );
 }
 
 
diff --git a/gerbview/export_to_pcbnew.cpp b/gerbview/export_to_pcbnew.cpp
index 1da98cdac2..b5045ada3b 100644
--- a/gerbview/export_to_pcbnew.cpp
+++ b/gerbview/export_to_pcbnew.cpp
@@ -186,7 +186,7 @@ void GBR_TO_PCB_EXPORTER::export_non_copper_item( const GERBER_DRAW_ITEM* aGbrIt
         SHAPE_POLY_SET polyshape = d_codeDescr->m_Polygon;
 
         // Compensate the Y axis orientation ( writePcbPolygon invert the Y coordinate )
-        polyshape.Outline( 0 ).Mirror( false, true );
+        polyshape.Outline( 0 ).Mirror( { 0, 0 }, FLIP_DIRECTION::TOP_BOTTOM );
         writePcbPolygon( polyshape, aLayer, aGbrItem->GetABPosition( seg_start ) );
         }
         break;
@@ -470,7 +470,7 @@ void GBR_TO_PCB_EXPORTER::export_flashed_copper_item( const GERBER_DRAW_ITEM* aG
         macroShape = *macro->GetApertureMacroShape( aGbrItem, VECTOR2I( 0, 0 ) );
 
         // Compensate the Y axis orientation ( writePcbPolygon invert the Y coordinate )
-        macroShape.Outline( 0 ).Mirror( false, true );
+        macroShape.Outline( 0 ).Mirror( { 0, 0 }, FLIP_DIRECTION::TOP_BOTTOM );
 
         writePcbPolygon( macroShape, aLayer, offset );
     }
diff --git a/gerbview/gerber_draw_item.cpp b/gerbview/gerber_draw_item.cpp
index d0f91c7c32..a60df6d894 100644
--- a/gerbview/gerber_draw_item.cpp
+++ b/gerbview/gerber_draw_item.cpp
@@ -627,7 +627,7 @@ void GERBER_DRAW_ITEM::ConvertSegmentToPolygon( SHAPE_POLY_SET* aPolygon ) const
 
     // Create final polygon:
     if( change )
-        aPolygon->Mirror( false, true );
+        aPolygon->Mirror( { 0, 0 }, FLIP_DIRECTION::TOP_BOTTOM );
 
     aPolygon->Move( VECTOR2I( start ) );
 }
diff --git a/include/bitmap_base.h b/include/bitmap_base.h
index 562140691b..64ae3a170b 100644
--- a/include/bitmap_base.h
+++ b/include/bitmap_base.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2018 jean-pierre.charras jp.charras at wanadoo.fr
- * Copyright (C) 2013-2023 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2013-2024 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
@@ -22,11 +22,12 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#ifndef BITMAP_BASE_H
-#define BITMAP_BASE_H
+#pragma once
 
 #include <wx/bitmap.h>
 #include <wx/image.h>
+
+#include <core/mirror.h>
 #include <kiid.h>
 #include <math/box2.h>
 #include <gal/color4d.h>
@@ -194,9 +195,9 @@ public:
     /**
      * Mirror image vertically (i.e. relative to its horizontal X axis ) or horizontally (i.e
      * relative to its vertical Y axis).
-     * @param aVertically false to mirror horizontally or true to mirror vertically.
+     * @param aFlipDirection the direction to flip the image.
      */
-    void Mirror( bool aVertically );
+    void Mirror( FLIP_DIRECTION aFlipDirection );
 
     /**
      * Rotate image CW or CCW.
@@ -274,6 +275,3 @@ private:
     bool      m_isMirroredY;        // Used for OpenGL rendering only
     EDA_ANGLE m_rotation;           // Used for OpenGL rendering only
 };
-
-
-#endif    // BITMAP_BASE_H
diff --git a/include/board_item.h b/include/board_item.h
index 1fb0acbfc5..09c0e7cb7f 100644
--- a/include/board_item.h
+++ b/include/board_item.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wandadoo.fr
- * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2024 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
@@ -22,15 +22,15 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#ifndef BOARD_ITEM_STRUCT_H
-#define BOARD_ITEM_STRUCT_H
+#pragma once
 
 
+#include <core/mirror.h>
 #include <eda_item.h>
+#include <geometry/approximation.h>
 #include <layer_ids.h>
 #include <lseq.h>
 #include <lset.h>
-#include <geometry/approximation.h>
 #include <stroke_params.h>
 #include <geometry/eda_angle.h>
 
@@ -358,9 +358,9 @@ public:
      * Flip this object, i.e. change the board side for this object.
      *
      * @param aCentre the rotation point.
-     * @param aFlipLeftRight mirror across Y axis instead of X (the default).
+     * @param aFlipDirection the flip direction
      */
-    virtual void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight );
+    virtual void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection );
 
     /**
      * Perform any normalization required after a user rotate and/or flip.
@@ -489,6 +489,3 @@ public:
     void Show( int , std::ostream& ) const override {}
 #endif
 };
-
-
-#endif /* BOARD_ITEM_STRUCT_H */
diff --git a/include/core/mirror.h b/include/core/mirror.h
index 5d82ece414..4c040e41ed 100644
--- a/include/core/mirror.h
+++ b/include/core/mirror.h
@@ -1,7 +1,7 @@
 /*
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
- * Copyright (C) 2020 KiCad Developers, see AUTHORS.TXT for contributors.
+ * Copyright (C) 2024 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
@@ -21,14 +21,19 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#ifndef MIRROR_H
-#define MIRROR_H
+#pragma once
+
+enum class FLIP_DIRECTION
+{
+    LEFT_RIGHT, ///< Flip left to right (around the Y axis)
+    TOP_BOTTOM  ///< Flip top to bottom (around the X axis)
+};
 
 /**
  *  Returns the mirror of aPoint relative to the aMirrorRef.
  */
 template <typename T>
-T MIRRORVAL( T aPoint, T aMirrorRef )
+constexpr T MIRRORVAL( T aPoint, T aMirrorRef )
 {
     return -( aPoint - aMirrorRef ) + aMirrorRef;
 }
@@ -37,9 +42,21 @@ T MIRRORVAL( T aPoint, T aMirrorRef )
  *  Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
  */
 template <typename T>
-void MIRROR( T& aPoint, const T& aMirrorRef )
+constexpr void MIRROR( T& aPoint, const T& aMirrorRef )
 {
     aPoint = MIRRORVAL( aPoint, aMirrorRef );
 }
 
-#endif /* MIRROR_H */
+
+/**
+ * Updates aPoint with the mirror of aPoint relative to the aMirrorRef,
+ * in the specified direction.
+ */
+template <typename VT>
+constexpr void MIRROR( VT& aPoint, const VT& aMirrorRef, FLIP_DIRECTION aFlipDirection )
+{
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
+        MIRROR( aPoint.x, aMirrorRef.x );
+    else
+        MIRROR( aPoint.y, aMirrorRef.y );
+}
diff --git a/include/eda_shape.h b/include/eda_shape.h
index e41ebb65ce..87467675a6 100644
--- a/include/eda_shape.h
+++ b/include/eda_shape.h
@@ -22,14 +22,14 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#ifndef EDA_SHAPE_H
-#define EDA_SHAPE_H
+#pragma once
 
-#include <trigo.h>
+#include <core/mirror.h>
 #include <geometry/shape_poly_set.h>
 #include <geometry/approximation.h>
 #include <properties/property.h>
 #include <stroke_params.h>
+#include <trigo.h>
 
 class LINE_READER;
 class EDA_DRAW_FRAME;
@@ -375,7 +375,7 @@ protected:
 
     void move( const VECTOR2I& aMoveVector );
     void rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle );
-    void flip( const VECTOR2I& aCentre, bool aFlipLeftRight );
+    void flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection );
     void scale( double aScale );
 
     const BOX2I getBoundingBox() const;
@@ -443,5 +443,3 @@ protected:
 DECLARE_ENUM_TO_WXANY( SHAPE_T );
 DECLARE_ENUM_TO_WXANY( LINE_STYLE );
 #endif
-
-#endif  // EDA_SHAPE_H
diff --git a/include/settings/app_settings.h b/include/settings/app_settings.h
index 879c0c62d9..5637c4991d 100644
--- a/include/settings/app_settings.h
+++ b/include/settings/app_settings.h
@@ -54,15 +54,6 @@ enum class ARC_EDIT_MODE
     KEEP_ENDPOINTS_OR_START_DIRECTION
 };
 
-/**
-* Settings for board items flip. Used by pcbnew
-*/
-enum class FLIP_DIRECTION
-{
-    LEFT_RIGHT,
-    TOP_BOTTOM
-};
-
 /**
 * Stores the window positioning/state
 */
diff --git a/libs/kimath/include/geometry/shape_arc.h b/libs/kimath/include/geometry/shape_arc.h
index 4638484437..6056188580 100644
--- a/libs/kimath/include/geometry/shape_arc.h
+++ b/libs/kimath/include/geometry/shape_arc.h
@@ -23,9 +23,9 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#ifndef __SHAPE_ARC_H
-#define __SHAPE_ARC_H
+#pragma once
 
+#include <core/mirror.h>     // for FLIP_DIRECTION
 #include <geometry/shape.h>
 #include <base_units.h>
 #include <math/vector2d.h>   // for VECTOR2I
@@ -185,7 +185,7 @@ public:
      */
     void Rotate( const EDA_ANGLE& aAngle, const VECTOR2I& aCenter ) override;
 
-    void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aVector = { 0, 0 } );
+    void Mirror( const VECTOR2I& aRef, FLIP_DIRECTION aFlipDirection );
 
     void Mirror( const SEG& axis );
 
@@ -290,5 +290,3 @@ private:
 
 // Required for Boost Test BOOST_CHECK_EQUAL:
 std::ostream& operator<<( std::ostream& aStream, const SHAPE_ARC& aArc );
-
-#endif
diff --git a/libs/kimath/include/geometry/shape_line_chain.h b/libs/kimath/include/geometry/shape_line_chain.h
index 0624b4589d..d665a76513 100644
--- a/libs/kimath/include/geometry/shape_line_chain.h
+++ b/libs/kimath/include/geometry/shape_line_chain.h
@@ -758,11 +758,10 @@ public:
     /**
      * Mirror the line points about y or x (or both).
      *
-     * @param aX If true, mirror about the y axis (flip X coordinate).
-     * @param aY If true, mirror about the x axis (flip Y coordinate).
      * @param aRef sets the reference point about which to mirror.
+     * @param aFlipDirection is the direction to mirror.
      */
-    void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aRef = { 0, 0 } );
+    void Mirror( const VECTOR2I& aRef, FLIP_DIRECTION aFlipDirection );
 
     /**
      * Mirror the line points using an given axis.
diff --git a/libs/kimath/include/geometry/shape_poly_set.h b/libs/kimath/include/geometry/shape_poly_set.h
index b4a2b867ad..0222f144ed 100644
--- a/libs/kimath/include/geometry/shape_poly_set.h
+++ b/libs/kimath/include/geometry/shape_poly_set.h
@@ -41,6 +41,7 @@
 
 #include <clipper.hpp>                  // for ClipType, PolyTree (ptr only)
 #include <clipper2/clipper.h>
+#include <core/mirror.h>                // for FLIP_DIRECTION
 #include <geometry/corner_strategy.h>
 #include <geometry/seg.h>               // for SEG
 #include <geometry/shape.h>
@@ -1140,11 +1141,10 @@ public:
     /**
      * Mirror the line points about y or x (or both)
      *
-     * @param aX If true, mirror about the y axis (flip x coordinate)
-     * @param aY If true, mirror about the x axis
      * @param aRef sets the reference point about which to mirror
+     * @param aFlipDirection is the direction to mirror the points.
      */
-    void Mirror( bool aX = true, bool aY = false, const VECTOR2I& aRef = { 0, 0 } );
+    void Mirror( const VECTOR2I& aRef, FLIP_DIRECTION aFlipDirection );
 
     /**
      * Rotate all vertices by a given angle.
diff --git a/libs/kimath/src/geometry/shape_arc.cpp b/libs/kimath/src/geometry/shape_arc.cpp
index dd079121e6..96794e2146 100644
--- a/libs/kimath/src/geometry/shape_arc.cpp
+++ b/libs/kimath/src/geometry/shape_arc.cpp
@@ -642,16 +642,15 @@ void SHAPE_ARC::Rotate( const EDA_ANGLE& aAngle, const VECTOR2I& aCenter )
 }
 
 
-void SHAPE_ARC::Mirror( bool aX, bool aY, const VECTOR2I& aVector )
+void SHAPE_ARC::Mirror( const VECTOR2I& aVector, FLIP_DIRECTION aFlipDirection )
 {
-    if( aX )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
     {
         m_start.x = -m_start.x + 2 * aVector.x;
         m_end.x = -m_end.x + 2 * aVector.x;
         m_mid.x = -m_mid.x + 2 * aVector.x;
     }
-
-    if( aY )
+    else
     {
         m_start.y = -m_start.y + 2 * aVector.y;
         m_end.y = -m_end.y + 2 * aVector.y;
diff --git a/libs/kimath/src/geometry/shape_line_chain.cpp b/libs/kimath/src/geometry/shape_line_chain.cpp
index 49caff1298..82950fce07 100644
--- a/libs/kimath/src/geometry/shape_line_chain.cpp
+++ b/libs/kimath/src/geometry/shape_line_chain.cpp
@@ -1018,19 +1018,18 @@ long long int SHAPE_LINE_CHAIN::Length() const
 }
 
 
-void SHAPE_LINE_CHAIN::Mirror( bool aX, bool aY, const VECTOR2I& aRef )
+void SHAPE_LINE_CHAIN::Mirror( const VECTOR2I& aRef, FLIP_DIRECTION aFlipDirection )
 {
     for( auto& pt : m_points )
     {
-        if( aX )
+        if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
             pt.x = -pt.x + 2 * aRef.x;
-
-        if( aY )
+        else
             pt.y = -pt.y + 2 * aRef.y;
     }
 
     for( auto& arc : m_arcs )
-        arc.Mirror( aX, aY, aRef );
+        arc.Mirror( aRef, aFlipDirection );
 }
 
 
diff --git a/libs/kimath/src/geometry/shape_poly_set.cpp b/libs/kimath/src/geometry/shape_poly_set.cpp
index a4da8fc415..572f53c6a0 100644
--- a/libs/kimath/src/geometry/shape_poly_set.cpp
+++ b/libs/kimath/src/geometry/shape_poly_set.cpp
@@ -2740,12 +2740,12 @@ void SHAPE_POLY_SET::Move( const VECTOR2I& aVector )
 }
 
 
-void SHAPE_POLY_SET::Mirror( bool aX, bool aY, const VECTOR2I& aRef )
+void SHAPE_POLY_SET::Mirror( const VECTOR2I& aRef, FLIP_DIRECTION aFlipDirection )
 {
     for( POLYGON& poly : m_polys )
     {
         for( SHAPE_LINE_CHAIN& path : poly )
-            path.Mirror( aX, aY, aRef );
+            path.Mirror( aRef, aFlipDirection );
     }
 
     if( m_triangulationValid )
diff --git a/pcbnew/board_item.cpp b/pcbnew/board_item.cpp
index 0432c12759..ebb4124fa2 100644
--- a/pcbnew/board_item.cpp
+++ b/pcbnew/board_item.cpp
@@ -359,7 +359,7 @@ void BOARD_ITEM::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void BOARD_ITEM::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void BOARD_ITEM::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
     wxMessageBox( wxT( "virtual BOARD_ITEM::Flip used, should not occur" ), GetClass() );
 }
diff --git a/pcbnew/dialogs/dialog_footprint_properties.cpp b/pcbnew/dialogs/dialog_footprint_properties.cpp
index f34c8db8e6..17cdbc1a15 100644
--- a/pcbnew/dialogs/dialog_footprint_properties.cpp
+++ b/pcbnew/dialogs/dialog_footprint_properties.cpp
@@ -611,7 +611,10 @@ bool DIALOG_FOOTPRINT_PROPERTIES::TransferDataFromWindow()
         change_layer = true;
 
     if( change_layer )
-        m_footprint->Flip( m_footprint->GetPosition(), m_frame->GetPcbNewSettings()->m_FlipLeftRight );
+    {
+        m_footprint->Flip( m_footprint->GetPosition(),
+                           m_frame->GetPcbNewSettings()->m_FlipDirection );
+    }
 
     // Copy the models from the panel to the footprint
     std::vector<FP_3DMODEL>& panelList = m_3dPanel->GetModelList();
diff --git a/pcbnew/dialogs/dialog_pad_properties.cpp b/pcbnew/dialogs/dialog_pad_properties.cpp
index 9ba6b7d17a..bba160c793 100644
--- a/pcbnew/dialogs/dialog_pad_properties.cpp
+++ b/pcbnew/dialogs/dialog_pad_properties.cpp
@@ -553,7 +553,7 @@ void DIALOG_PAD_PROPERTIES::initValues()
             if( footprint->IsFlipped() )
             {
                 // flip pad (up/down) around its position
-                m_previewPad->Flip( m_previewPad->GetPosition(), false );
+                m_previewPad->Flip( m_previewPad->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
                 relPos.y = - relPos.y;
             }
 
@@ -1489,7 +1489,7 @@ bool DIALOG_PAD_PROPERTIES::TransferDataFromWindow()
     if( m_currentPad->GetParentFootprint() && m_currentPad->GetParentFootprint()->IsFlipped() )
     {
         // flip pad (up/down) around its position
-        m_currentPad->Flip( m_currentPad->GetPosition(), false );
+        m_currentPad->Flip( m_currentPad->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
     }
 
     m_currentPad->SetPosition( m_masterPad->GetPosition() );
diff --git a/pcbnew/dialogs/panel_edit_options.cpp b/pcbnew/dialogs/panel_edit_options.cpp
index 0511d2b08f..7d2617d723 100644
--- a/pcbnew/dialogs/panel_edit_options.cpp
+++ b/pcbnew/dialogs/panel_edit_options.cpp
@@ -65,9 +65,7 @@ void PANEL_EDIT_OPTIONS::loadPCBSettings( PCBNEW_SETTINGS* aCfg )
     m_rotationAngle.SetAngleValue( aCfg->m_RotationAngle );
     m_arcEditMode->SetSelection( (int) aCfg->m_ArcEditMode );
     m_trackMouseDragCtrl->SetSelection( (int) aCfg->m_TrackDragAction );
-    FLIP_DIRECTION flipDirection =
-            aCfg->m_FlipLeftRight ? FLIP_DIRECTION::LEFT_RIGHT : FLIP_DIRECTION::TOP_BOTTOM;
-    m_boardItemsFlip->SetSelection( (int) flipDirection );
+    m_boardItemsFlip->SetSelection( static_cast<int>( aCfg->m_FlipDirection ) );
     m_allowFreePads->SetValue( aCfg->m_AllowFreePads );
     m_autoRefillZones->SetValue( aCfg->m_AutoRefillZones );
 
@@ -153,8 +151,7 @@ bool PANEL_EDIT_OPTIONS::TransferDataFromWindow()
         cfg->m_RotationAngle = m_rotationAngle.GetAngleValue();
         cfg->m_ArcEditMode = (ARC_EDIT_MODE) m_arcEditMode->GetSelection();
         cfg->m_TrackDragAction = (TRACK_DRAG_ACTION) m_trackMouseDragCtrl->GetSelection();
-        FLIP_DIRECTION flipDirection = (FLIP_DIRECTION) m_boardItemsFlip->GetSelection();
-        cfg->m_FlipLeftRight = ( flipDirection == FLIP_DIRECTION::LEFT_RIGHT );
+        cfg->m_FlipDirection = static_cast<FLIP_DIRECTION>( m_boardItemsFlip->GetSelection() );
         cfg->m_AllowFreePads = m_allowFreePads->GetValue();
         cfg->m_AutoRefillZones = m_autoRefillZones->GetValue();
 
diff --git a/pcbnew/drc/drc_test_provider_library_parity.cpp b/pcbnew/drc/drc_test_provider_library_parity.cpp
index 55bacb92bc..d27a36ecdc 100644
--- a/pcbnew/drc/drc_test_provider_library_parity.cpp
+++ b/pcbnew/drc/drc_test_provider_library_parity.cpp
@@ -553,7 +553,7 @@ bool FOOTPRINT::FootprintNeedsUpdate( const FOOTPRINT* aLibFP, int aCompareFlags
     temp->SetParent( GetBoard() );  // Needed to know the copper layer count;
 
     if( IsFlipped() != temp->IsFlipped() )
-        temp->Flip( { 0, 0 }, false );
+        temp->Flip( { 0, 0 }, FLIP_DIRECTION::TOP_BOTTOM );
 
     if( GetOrientation() != temp->GetOrientation() )
         temp->SetOrientation( GetOrientation() );
diff --git a/pcbnew/exporters/export_gencad_writer.cpp b/pcbnew/exporters/export_gencad_writer.cpp
index f06080b7cd..479d3bf5d9 100644
--- a/pcbnew/exporters/export_gencad_writer.cpp
+++ b/pcbnew/exporters/export_gencad_writer.cpp
@@ -183,7 +183,7 @@ bool GENCAD_EXPORTER::WriteFile( wxString& aFullFileName )
 
         if( footprint->GetLayer() == B_Cu )
         {
-            footprint->Flip( footprint->GetPosition(), false );
+            footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
             footprint->SetFlag( 1 );
         }
     }
@@ -217,7 +217,7 @@ bool GENCAD_EXPORTER::WriteFile( wxString& aFullFileName )
     {
         if( footprint->GetFlag() )
         {
-            footprint->Flip( footprint->GetPosition(), false );
+            footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
             footprint->SetFlag( 0 );
         }
     }
diff --git a/pcbnew/footprint.cpp b/pcbnew/footprint.cpp
index 2008d854d9..77ffd50c11 100644
--- a/pcbnew/footprint.cpp
+++ b/pcbnew/footprint.cpp
@@ -33,7 +33,6 @@
 #include <confirm.h>
 #include <convert_basic_shapes_to_polygon.h>
 #include <convert_shape_list_to_polygon.h>
-#include <core/mirror.h>
 #include <drc/drc_item.h>
 #include <embedded_files.h>
 #include <font/font.h>
@@ -1215,7 +1214,7 @@ BOX2I FOOTPRINT::GetFpPadsLocalBbox() const
     dummy.SetOrientation( ANGLE_0 );
 
     if( dummy.IsFlipped() )
-        dummy.Flip( VECTOR2I( 0, 0 ), false );
+        dummy.Flip( VECTOR2I( 0, 0 ), FLIP_DIRECTION::TOP_BOTTOM );
 
     for( PAD* pad : dummy.Pads() )
         bbox.Merge( pad->GetBoundingBox() );
@@ -2260,11 +2259,11 @@ void FOOTPRINT::SetLayerAndFlip( PCB_LAYER_ID aLayer )
     wxASSERT( aLayer == F_Cu || aLayer == B_Cu );
 
     if( aLayer != GetLayer() )
-        Flip( GetPosition(), true );
+        Flip( GetPosition(), FLIP_DIRECTION::LEFT_RIGHT );
 }
 
 
-void FOOTPRINT::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void FOOTPRINT::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
     // Move footprint to its final position:
     VECTOR2I finalPos = m_pos;
@@ -2292,31 +2291,31 @@ void FOOTPRINT::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
 
     // Mirror fields to other side of board.
     for( PCB_FIELD* field : m_fields )
-        field->Flip( m_pos, false );
+        field->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
 
     // Mirror pads to other side of board.
     for( PAD* pad : m_pads )
-        pad->Flip( m_pos, false );
+        pad->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
 
     // Now set the new orientation.
     m_orient = newOrientation;
 
     // Mirror zones to other side of board.
     for( ZONE* zone : m_zones )
-        zone->Flip( m_pos, false );
+        zone->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
 
     // Reverse mirror footprint graphics and texts.
     for( BOARD_ITEM* item : m_drawings )
-        item->Flip( m_pos, false );
+        item->Flip( m_pos, FLIP_DIRECTION::TOP_BOTTOM );
 
     // Now rotate 180 deg if required
-    if( aFlipLeftRight )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
         Rotate( aCentre, ANGLE_180 );
 
     m_boundingBoxCacheTimeStamp = 0;
     m_textExcludedBBoxCacheTimeStamp = 0;
 
-    m_cachedHull.Mirror( aFlipLeftRight, !aFlipLeftRight, m_pos );
+    m_cachedHull.Mirror( m_pos, aFlipDirection );
 
     std::swap( m_courtyard_cache_front, m_courtyard_cache_back );
 }
diff --git a/pcbnew/footprint.h b/pcbnew/footprint.h
index 780fe95767..ebcd5fd083 100644
--- a/pcbnew/footprint.h
+++ b/pcbnew/footprint.h
@@ -369,7 +369,7 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     /**
      * Move the reference point of the footprint.
diff --git a/pcbnew/footprint_editor_settings.cpp b/pcbnew/footprint_editor_settings.cpp
index dbfb2e987d..962d53c612 100644
--- a/pcbnew/footprint_editor_settings.cpp
+++ b/pcbnew/footprint_editor_settings.cpp
@@ -18,8 +18,9 @@
  * with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "footprint_editor_settings.h"
+
 #include <common.h>
-#include <footprint_editor_settings.h>
 #include <layer_ids.h>
 #include <lset.h>
 #include <pgm_base.h>
diff --git a/pcbnew/footprint_viewer_frame.cpp b/pcbnew/footprint_viewer_frame.cpp
index 0f0cb3cd36..8a3d2cfe29 100644
--- a/pcbnew/footprint_viewer_frame.cpp
+++ b/pcbnew/footprint_viewer_frame.cpp
@@ -779,7 +779,7 @@ void FOOTPRINT_VIEWER_FRAME::AddFootprintToPCB( wxCommandEvent& aEvent )
         // Put it on FRONT layer,
         // (Can be stored flipped if the lib is an archive built from a board)
         if( newFootprint->IsFlipped() )
-            newFootprint->Flip( newFootprint->GetPosition(), cfg->m_FlipLeftRight );
+            newFootprint->Flip( newFootprint->GetPosition(), cfg->m_FlipDirection );
 
         KIGFX::VIEW_CONTROLS* viewControls = pcbframe->GetCanvas()->GetViewControls();
         VECTOR2D              cursorPos = viewControls->GetCursorPosition();
diff --git a/pcbnew/generators/pcb_tuning_pattern.cpp b/pcbnew/generators/pcb_tuning_pattern.cpp
index 4ff1bfc4b4..6ca265e215 100644
--- a/pcbnew/generators/pcb_tuning_pattern.cpp
+++ b/pcbnew/generators/pcb_tuning_pattern.cpp
@@ -332,22 +332,19 @@ public:
         }
     }
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override
     {
         if( !this->HasFlag( IN_EDIT ) )
         {
-            PCB_GENERATOR::Flip( aCentre, aFlipLeftRight );
+            PCB_GENERATOR::Flip( aCentre, aFlipDirection );
 
-            if( aFlipLeftRight )
-                MIRROR( m_end.x, aCentre.x );
-            else
-                MIRROR( m_end.y, aCentre.y );
+            MIRROR( m_end, aCentre, aFlipDirection );
 
             if( m_baseLine )
-                m_baseLine->Mirror( aFlipLeftRight, !aFlipLeftRight, aCentre );
+                m_baseLine->Mirror( aCentre, aFlipDirection );
 
             if( m_baseLineCoupled )
-                m_baseLineCoupled->Mirror( aFlipLeftRight, !aFlipLeftRight, aCentre );
+                m_baseLineCoupled->Mirror( aCentre, aFlipDirection );
         }
     }
 
diff --git a/pcbnew/load_select_footprint.cpp b/pcbnew/load_select_footprint.cpp
index 51eb88572c..f7ae77ce28 100644
--- a/pcbnew/load_select_footprint.cpp
+++ b/pcbnew/load_select_footprint.cpp
@@ -146,7 +146,10 @@ bool FOOTPRINT_EDIT_FRAME::LoadFootprintFromBoard( FOOTPRINT* aFootprint )
     // Put it on FRONT layer,
     // because this is the default in Footprint Editor, and in libs
     if( newFootprint->GetLayer() != F_Cu )
-        newFootprint->Flip( newFootprint->GetPosition(), frame->GetPcbNewSettings()->m_FlipLeftRight );
+    {
+        newFootprint->Flip( newFootprint->GetPosition(),
+                            frame->GetPcbNewSettings()->m_FlipDirection );
+    }
 
     // Put it in orientation 0,
     // because this is the default orientation in Footprint Editor, and in libs
diff --git a/pcbnew/pad.cpp b/pcbnew/pad.cpp
index df1cdc00ea..250894b3b6 100644
--- a/pcbnew/pad.cpp
+++ b/pcbnew/pad.cpp
@@ -25,7 +25,6 @@
 
 #include <base_units.h>
 #include <bitmaps.h>
-#include <core/mirror.h>
 #include <math/util.h>      // for KiROUND
 #include <eda_draw_frame.h>
 #include <geometry/shape_circle.h>
@@ -829,20 +828,11 @@ EDA_ANGLE PAD::GetFPRelativeOrientation() const
 }
 
 
-void PAD::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PAD::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aFlipLeftRight )
-    {
-        MIRROR( m_pos.x, aCentre.x );
-        MIRROR( m_padStack.Offset().x, 0 );
-        MIRROR( m_padStack.TrapezoidDeltaSize().x, 0 );
-    }
-    else
-    {
-        MIRROR( m_pos.y, aCentre.y );
-        MIRROR( m_padStack.Offset().y, 0 );
-        MIRROR( m_padStack.TrapezoidDeltaSize().y, 0 );
-    }
+    MIRROR( m_pos, aCentre, aFlipDirection );
+    MIRROR( m_padStack.Offset(), VECTOR2I{ 0, 0 }, aFlipDirection );
+    MIRROR( m_padStack.TrapezoidDeltaSize(), VECTOR2I{ 0, 0 }, aFlipDirection );
 
     SetFPRelativeOrientation( -GetFPRelativeOrientation() );
 
@@ -861,7 +851,7 @@ void PAD::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
                                   aBitfield &= ~b;
                           };
 
-    if( aFlipLeftRight )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
     {
         mirrorBitFlags( m_padStack.ChamferPositions(), RECT_CHAMFER_TOP_LEFT,
                         RECT_CHAMFER_TOP_RIGHT );
@@ -884,16 +874,16 @@ void PAD::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
     SetLayerSet( layerSet.Flip() );
 
     // Flip the basic shapes, in custom pads
-    FlipPrimitives( aFlipLeftRight );
+    FlipPrimitives( aFlipDirection );
 
     SetDirty();
 }
 
 
-void PAD::FlipPrimitives( bool aFlipLeftRight )
+void PAD::FlipPrimitives( FLIP_DIRECTION aFlipDirection )
 {
     for( std::shared_ptr<PCB_SHAPE>& primitive : m_padStack.Primitives() )
-        primitive->Flip( VECTOR2I( 0, 0 ), aFlipLeftRight );
+        primitive->Flip( VECTOR2I( 0, 0 ), aFlipDirection );
 
     SetDirty();
 }
diff --git a/pcbnew/pad.h b/pcbnew/pad.h
index e4750171e5..00d08b88ae 100644
--- a/pcbnew/pad.h
+++ b/pcbnew/pad.h
@@ -27,15 +27,17 @@
 
 #include <mutex>
 #include <array>
-#include <zones.h>
+
 #include <board_connected_item.h>
+#include <core/arraydim.h>
+#include <core/mirror.h>
+#include <geometry/eda_angle.h>
+#include <geometry/geometry_utils.h>
 #include <geometry/shape_poly_set.h>
 #include <geometry/shape_compound.h>
 #include <lset.h>
 #include <padstack.h>
-#include <geometry/eda_angle.h>
-#include <geometry/geometry_utils.h>
-#include <core/arraydim.h>
+#include <zones.h>
 
 class PCB_SHAPE;
 class SHAPE;
@@ -320,13 +322,13 @@ public:
         return m_padStack.Primitives();
     }
 
-    void Flip( const VECTOR2I& VECTOR2I, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& VECTOR2I, FLIP_DIRECTION aFlipDirection ) override;
 
     /**
      * Flip (mirror) the primitives left to right or top to bottom, around the anchor position
      * in custom pads.
      */
-    void FlipPrimitives( bool aFlipLeftRight );
+    void FlipPrimitives( FLIP_DIRECTION aFlipDirection );
 
     /**
      * Clear the current custom shape primitives list and import a new list.  Copies the input,
diff --git a/pcbnew/pcb_base_frame.cpp b/pcbnew/pcb_base_frame.cpp
index 0f6ee73058..239472c1a4 100644
--- a/pcbnew/pcb_base_frame.cpp
+++ b/pcbnew/pcb_base_frame.cpp
@@ -235,7 +235,7 @@ void PCB_BASE_FRAME::AddFootprintToBoard( FOOTPRINT* aFootprint )
         // Put it on FRONT layer (note that it might be stored flipped if the lib is an archive
         // built from a board)
         if( aFootprint->IsFlipped() )
-            aFootprint->Flip( aFootprint->GetPosition(), GetPcbNewSettings()->m_FlipLeftRight );
+            aFootprint->Flip( aFootprint->GetPosition(), GetPcbNewSettings()->m_FlipDirection );
 
         // Place it in orientation 0 even if it is not saved with orientation 0 in lib (note that
         // it might be stored in another orientation if the lib is an archive built from a board)
diff --git a/pcbnew/pcb_dimension.cpp b/pcbnew/pcb_dimension.cpp
index 5ed78230ed..1312b69d16 100644
--- a/pcbnew/pcb_dimension.cpp
+++ b/pcbnew/pcb_dimension.cpp
@@ -31,7 +31,6 @@
 #include <convert_basic_shapes_to_polygon.h>
 #include <font/font.h>
 #include <board.h>
-#include <core/mirror.h>
 #include <pcb_dimension.h>
 #include <pcb_text.h>
 #include <geometry/shape_compound.h>
@@ -366,38 +365,27 @@ void PCB_DIMENSION_BASE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aA
 }
 
 
-void PCB_DIMENSION_BASE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_DIMENSION_BASE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    Mirror( aCentre );
+    Mirror( aCentre, aFlipDirection );
 
     SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
 }
 
 
-void PCB_DIMENSION_BASE::Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight )
+void PCB_DIMENSION_BASE::Mirror( const VECTOR2I& axis_pos, FLIP_DIRECTION aFlipDirection )
 {
     VECTOR2I newPos = GetTextPos();
 
-    if( aMirrorLeftRight )
-        MIRROR( newPos.x, axis_pos.x );
-    else
-        MIRROR( newPos.y, axis_pos.y );
+    MIRROR( newPos, axis_pos, aFlipDirection );
 
     SetTextPos( newPos );
 
     // invert angle
     SetTextAngle( -GetTextAngle() );
 
-    if( aMirrorLeftRight )
-    {
-        MIRROR( m_start.x, axis_pos.x );
-        MIRROR( m_end.x, axis_pos.x );
-    }
-    else
-    {
-        MIRROR( m_start.y, axis_pos.y );
-        MIRROR( m_end.y, axis_pos.y );
-    }
+    MIRROR( m_start, axis_pos, aFlipDirection );
+    MIRROR( m_end, axis_pos, aFlipDirection );
 
     if( IsSideSpecific() )
         SetMirrored( !IsMirrored() );
@@ -694,11 +682,11 @@ void PCB_DIM_ALIGNED::swapData( BOARD_ITEM* aImage )
 }
 
 
-void PCB_DIM_ALIGNED::Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight )
+void PCB_DIM_ALIGNED::Mirror( const VECTOR2I& axis_pos, FLIP_DIRECTION aFlipDirection )
 {
     m_height = -m_height;
     // Call this last for the Update()
-    PCB_DIMENSION_BASE::Mirror( axis_pos, aMirrorLeftRight );
+    PCB_DIMENSION_BASE::Mirror( axis_pos, aFlipDirection );
 }
 
 
@@ -886,16 +874,16 @@ void PCB_DIM_ORTHOGONAL::swapData( BOARD_ITEM* aImage )
 }
 
 
-void PCB_DIM_ORTHOGONAL::Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight )
+void PCB_DIM_ORTHOGONAL::Mirror( const VECTOR2I& axis_pos, FLIP_DIRECTION aFlipDirection )
 {
     // Only reverse the height if the height is aligned with the flip
-    if( m_orientation == DIR::HORIZONTAL && !aMirrorLeftRight )
+    if( m_orientation == DIR::HORIZONTAL && aFlipDirection == FLIP_DIRECTION::TOP_BOTTOM )
         m_height = -m_height;
-    else if( m_orientation == DIR::VERTICAL && aMirrorLeftRight )
+    else if( m_orientation == DIR::VERTICAL && aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
         m_height = -m_height;
 
     // Call this last, as we need the Update()
-    PCB_DIMENSION_BASE::Mirror( axis_pos, aMirrorLeftRight );
+    PCB_DIMENSION_BASE::Mirror( axis_pos, aFlipDirection );
 }
 
 
diff --git a/pcbnew/pcb_dimension.h b/pcbnew/pcb_dimension.h
index 3dce7a2102..95e2437880 100644
--- a/pcbnew/pcb_dimension.h
+++ b/pcbnew/pcb_dimension.h
@@ -254,7 +254,7 @@ public:
 
     void Move( const VECTOR2I& offset ) override;
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     /**
      * Mirror the dimension relative to a given horizontal axis.
@@ -264,7 +264,7 @@ public:
      *
      * @param axis_pos is the vertical axis position to mirror around.
      */
-    virtual void Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight = false );
+    virtual void Mirror( const VECTOR2I& axis_pos, FLIP_DIRECTION aFlipDirection );
 
     void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
 
@@ -399,7 +399,7 @@ public:
 
     EDA_ITEM* Clone() const override;
 
-    void Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight = false ) override;
+    void Mirror( const VECTOR2I& axis_pos, FLIP_DIRECTION aFlipDirection ) override;
 
     BITMAPS GetMenuImage() const override;
 
@@ -497,7 +497,7 @@ public:
 
     EDA_ITEM* Clone() const override;
 
-    void Mirror( const VECTOR2I& axis_pos, bool aMirrorLeftRight = false ) override;
+    void Mirror( const VECTOR2I& axis_pos, FLIP_DIRECTION aFlipDirection ) override;
 
     BITMAPS GetMenuImage() const override;
 
diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp
index 5299d826a1..90dd8ebfa3 100644
--- a/pcbnew/pcb_edit_frame.cpp
+++ b/pcbnew/pcb_edit_frame.cpp
@@ -2320,7 +2320,7 @@ void PCB_EDIT_FRAME::ExchangeFootprint( FOOTPRINT* aExisting, FOOTPRINT* aNew,
     aNew->SetPosition( aExisting->GetPosition() );
 
     if( aNew->GetLayer() != aExisting->GetLayer() )
-        aNew->Flip( aNew->GetPosition(), GetPcbNewSettings()->m_FlipLeftRight );
+        aNew->Flip( aNew->GetPosition(), GetPcbNewSettings()->m_FlipDirection );
 
     if( aNew->GetOrientation() != aExisting->GetOrientation() )
         aNew->SetOrientation( aExisting->GetOrientation() );
diff --git a/pcbnew/pcb_generator.cpp b/pcbnew/pcb_generator.cpp
index 3e753ebc8b..8e2b60c8e2 100644
--- a/pcbnew/pcb_generator.cpp
+++ b/pcbnew/pcb_generator.cpp
@@ -22,8 +22,8 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#include <pcb_generator.h>
-#include <core/mirror.h>
+#include "pcb_generator.h"
+
 #include <board.h>
 
 
@@ -135,16 +135,13 @@ void PCB_GENERATOR::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle
     PCB_GROUP::Rotate( aRotCentre, aAngle );
 }
 
-void PCB_GENERATOR::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_GENERATOR::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aFlipLeftRight )
-        MIRROR( m_origin.x, aCentre.x );
-    else
-        MIRROR( m_origin.y, aCentre.y );
+    MIRROR( m_origin, aCentre, aFlipDirection );
 
     SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
 
-    PCB_GROUP::Flip( aCentre, aFlipLeftRight );
+    PCB_GROUP::Flip( aCentre, aFlipDirection );
 }
 
 bool PCB_GENERATOR::AddItem( BOARD_ITEM* aItem )
diff --git a/pcbnew/pcb_generator.h b/pcbnew/pcb_generator.h
index 8fd86ee71f..fba185af11 100644
--- a/pcbnew/pcb_generator.h
+++ b/pcbnew/pcb_generator.h
@@ -86,7 +86,7 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     bool AddItem( BOARD_ITEM* aItem ) override;
 
diff --git a/pcbnew/pcb_group.cpp b/pcbnew/pcb_group.cpp
index a3d77a52bc..41727fdf14 100644
--- a/pcbnew/pcb_group.cpp
+++ b/pcbnew/pcb_group.cpp
@@ -359,10 +359,10 @@ void PCB_GROUP::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void PCB_GROUP::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_GROUP::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
     for( BOARD_ITEM* item : m_items )
-        item->Flip( aCentre, aFlipLeftRight );
+        item->Flip( aCentre, aFlipDirection );
 }
 
 
diff --git a/pcbnew/pcb_group.h b/pcbnew/pcb_group.h
index 6e5fdedae9..e4009211ae 100644
--- a/pcbnew/pcb_group.h
+++ b/pcbnew/pcb_group.h
@@ -184,7 +184,7 @@ public:
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
     /// @copydoc BOARD_ITEM::Flip
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     /// @copydoc EDA_ITEM::GetItemDescription
     wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const override;
diff --git a/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp b/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp
index ad290346c7..8fbd3d8fca 100644
--- a/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp
+++ b/pcbnew/pcb_io/cadstar/cadstar_pcb_archive_loader.cpp
@@ -1848,7 +1848,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadComponents()
             EDA_ANGLE mirroredAngle = - getAngle( comp.OrientAngle );
             mirroredAngle.Normalize180();
             footprint->SetOrientation( mirroredAngle );
-            footprint->Flip( getKiCadPoint( comp.Origin ), true );
+            footprint->Flip( getKiCadPoint( comp.Origin ), FLIP_DIRECTION::LEFT_RIGHT );
         }
 
         loadComponentAttributes( comp, footprint );
@@ -2709,7 +2709,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarText( const TEXT& aCadstarText,
     }
 
     if( aMirrorInvert )
-        txt->Flip( aTransformCentre, true );
+        txt->Flip( aTransformCentre, FLIP_DIRECTION::LEFT_RIGHT );
 
     //scale it after flipping:
     if( aScalingFactor != 1.0 )
@@ -2975,7 +2975,7 @@ PCB_SHAPE* CADSTAR_PCB_ARCHIVE_LOADER::getShapeFromVertex( const POINT& aCadstar
 
     //Apply transforms
     if( aMirrorInvert )
-        shape->Flip( aTransformCentre, true );
+        shape->Flip( aTransformCentre, FLIP_DIRECTION::LEFT_RIGHT );
 
     if( aScalingFactor != 1.0 )
     {
diff --git a/pcbnew/pcb_io/eagle/pcb_io_eagle.cpp b/pcbnew/pcb_io/eagle/pcb_io_eagle.cpp
index 536fe9bef7..5f39eec017 100644
--- a/pcbnew/pcb_io/eagle/pcb_io_eagle.cpp
+++ b/pcbnew/pcb_io/eagle/pcb_io_eagle.cpp
@@ -1665,7 +1665,7 @@ void PCB_IO_EAGLE::orientFootprintAndText( FOOTPRINT* aFootprint, const EELEMENT
         if( e.rot->mirror )
         {
             aFootprint->SetOrientation( EDA_ANGLE( e.rot->degrees + 180.0, DEGREES_T ) );
-            aFootprint->Flip( aFootprint->GetPosition(), false );
+            aFootprint->Flip( aFootprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
         }
         else
         {
diff --git a/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp b/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp
index fed575b7fc..db33e96892 100644
--- a/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp
+++ b/pcbnew/pcb_io/easyedapro/pcb_io_easyedapro_parser.cpp
@@ -1391,7 +1391,12 @@ void PCB_IO_EASYEDAPRO_PARSER::ParseBoard(
                 shape->Rotate( ScalePos( start ), EDA_ANGLE( angle, DEGREES_T ) );
 
                 if( IsBackLayer( klayer ) ^ !!mirror )
-                    shape->Mirror( ScalePos( start ), !IsBackLayer( klayer ) );
+                {
+                    FLIP_DIRECTION flipDirection = IsBackLayer( klayer )
+                                                           ? FLIP_DIRECTION::TOP_BOTTOM
+                                                           : FLIP_DIRECTION::LEFT_RIGHT;
+                    shape->Mirror( ScalePos( start ), flipDirection );
+                }
 
                 if( group )
                     group->AddItem( shape.get() );
@@ -1479,7 +1484,7 @@ void PCB_IO_EASYEDAPRO_PARSER::ParseBoard(
                         MIRROR( x, KiROUND( kstart.x ) );
                         bitmap->SetX( x );
 
-                        bitmap->MutableImage()->Mirror( false );
+                        bitmap->MutableImage()->Mirror( FLIP_DIRECTION::LEFT_RIGHT );
                     }
 
                     aBoard->Add( bitmap.release(), ADD_MODE::APPEND );
@@ -1653,7 +1658,7 @@ void PCB_IO_EASYEDAPRO_PARSER::ParseBoard(
                 //std::map<wxString, wxString> props = line.at( 7 );
 
                 if( klayer == B_Cu )
-                    footprint->Flip( footprint->GetPosition(), false );
+                    footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
 
                 footprint->SetOrientationDegrees( orient );
                 footprint->SetPosition( ScalePos( center ) );
diff --git a/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp b/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp
index 951f8f94c3..2be302f487 100644
--- a/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp
+++ b/pcbnew/pcb_io/fabmaster/import_fabmaster.cpp
@@ -2155,7 +2155,7 @@ bool FABMASTER::loadFootprints( BOARD* aBoard )
                             circle->SetWidth( ds.GetLineThickness( circle->GetLayer() ) );
 
                         if( src->mirror )
-                            circle->Flip( circle->GetCenter(), false );
+                            circle->Flip( circle->GetCenter(), FLIP_DIRECTION::TOP_BOTTOM );
 
                         fp->Add( circle, ADD_MODE::APPEND );
                         break;
@@ -2176,7 +2176,7 @@ bool FABMASTER::loadFootprints( BOARD* aBoard )
                             arc->SetStroke( defaultStroke );
 
                         if( src->mirror )
-                            arc->Flip( arc->GetCenter(), false );
+                            arc->Flip( arc->GetCenter(), FLIP_DIRECTION::TOP_BOTTOM );
 
                         fp->Add( arc, ADD_MODE::APPEND );
                         break;
@@ -2369,8 +2369,8 @@ bool FABMASTER::loadFootprints( BOARD* aBoard )
 
                                     if( src->mirror )
                                     {
-                                        poly_outline.Mirror( false, true,
-                                                             VECTOR2I( 0, ( pin->pin_y - src->y ) ) );
+                                        poly_outline.Mirror( VECTOR2I( 0, ( pin->pin_y - src->y ) ),
+                                                             FLIP_DIRECTION::TOP_BOTTOM );
                                         poly_outline.Rotate( EDA_ANGLE( src->rotate - pin->rotation,
                                                                         DEGREES_T ) );
                                     }
@@ -2458,7 +2458,7 @@ bool FABMASTER::loadFootprints( BOARD* aBoard )
             if( src->mirror )
             {
                 fp->SetOrientationDegrees( 180.0 - src->rotate );
-                fp->Flip( fp->GetPosition(), true );
+                fp->Flip( fp->GetPosition(), FLIP_DIRECTION::LEFT_RIGHT );
             }
 
             aBoard->Add( fp, ADD_MODE::APPEND );
diff --git a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp
index b9623bf65e..9a8534eb3c 100644
--- a/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp
+++ b/pcbnew/pcb_io/ipc2581/pcb_io_ipc2581.cpp
@@ -1246,7 +1246,7 @@ wxXmlNode* PCB_IO_IPC2581::generateBOMSection( wxXmlNode* aEcadNode )
         fp->SetPosition( {0, 0} );
 
         if( fp->GetLayer() != F_Cu )
-            fp->Flip( fp->GetPosition(), false );
+            fp->Flip( fp->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
 
         fp->SetOrientation( ANGLE_0 );
 
@@ -1949,7 +1949,7 @@ wxXmlNode* PCB_IO_IPC2581::addPackage( wxXmlNode* aContentNode, FOOTPRINT* aFp )
     fp->SetPosition( { 0, 0 } );
 
     if( fp->GetLayer() != F_Cu )
-        fp->Flip( fp->GetPosition(), false );
+        fp->Flip( fp->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
 
     fp->SetOrientation( ANGLE_0 );
 
diff --git a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp
index 21a7d600b6..b87fbfd486 100644
--- a/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp
+++ b/pcbnew/pcb_io/kicad_sexpr/pcb_io_kicad_sexpr.cpp
@@ -2917,9 +2917,9 @@ void PCB_IO_KICAD_SEXPR::FootprintSave( const wxString& aLibraryPath, const FOOT
         PCBNEW_SETTINGS* cfg = dynamic_cast<PCBNEW_SETTINGS*>( Kiface().KifaceSettings() );
 
         if( cfg )
-            footprint->Flip( footprint->GetPosition(), cfg->m_FlipLeftRight );
+            footprint->Flip( footprint->GetPosition(), cfg->m_FlipDirection );
         else
-            footprint->Flip( footprint->GetPosition(), false );
+            footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
     }
 
     // Detach it from the board and its group
diff --git a/pcbnew/pcb_io/odbpp/odb_eda_data.cpp b/pcbnew/pcb_io/odbpp/odb_eda_data.cpp
index 611eaa60fe..a269c0a850 100644
--- a/pcbnew/pcb_io/odbpp/odb_eda_data.cpp
+++ b/pcbnew/pcb_io/odbpp/odb_eda_data.cpp
@@ -195,7 +195,7 @@ void EDA_DATA::AddPackage( const FOOTPRINT* aFp )
     fp->SetPosition( { 0, 0 } );
 
     if( fp->GetLayer() != F_Cu )
-        fp->Flip( fp->GetPosition(), false );
+        fp->Flip( fp->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
 
     fp->SetOrientation( ANGLE_0 );
 
diff --git a/pcbnew/pcb_marker.cpp b/pcbnew/pcb_marker.cpp
index b7636fef6e..ff007f0416 100644
--- a/pcbnew/pcb_marker.cpp
+++ b/pcbnew/pcb_marker.cpp
@@ -251,7 +251,7 @@ void PCB_MARKER::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void PCB_MARKER::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_MARKER::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
     // Marker geometry isn't user-editable
 }
diff --git a/pcbnew/pcb_marker.h b/pcbnew/pcb_marker.h
index 485d907323..7d4ef87504 100644
--- a/pcbnew/pcb_marker.h
+++ b/pcbnew/pcb_marker.h
@@ -63,7 +63,7 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     VECTOR2I GetPosition() const override { return m_Pos; }
     void     SetPosition( const VECTOR2I& aPos ) override { m_Pos = aPos; }
diff --git a/pcbnew/pcb_reference_image.cpp b/pcbnew/pcb_reference_image.cpp
index 95115a95f8..d4a3585488 100644
--- a/pcbnew/pcb_reference_image.cpp
+++ b/pcbnew/pcb_reference_image.cpp
@@ -180,18 +180,10 @@ const VECTOR2I PCB_REFERENCE_IMAGE::GetSize() const
 }
 
 
-void PCB_REFERENCE_IMAGE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_REFERENCE_IMAGE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aFlipLeftRight )
-    {
-        MIRROR( m_pos.x, aCentre.x );
-        m_bitmapBase->Mirror( false );
-    }
-    else
-    {
-        MIRROR( m_pos.y, aCentre.y );
-        m_bitmapBase->Mirror( true );
-    }
+    MIRROR( m_pos, aCentre, aFlipDirection );
+    m_bitmapBase->Mirror( aFlipDirection );
 }
 
 void PCB_REFERENCE_IMAGE::Rotate( const VECTOR2I& aCenter, const EDA_ANGLE& aAngle )
diff --git a/pcbnew/pcb_reference_image.h b/pcbnew/pcb_reference_image.h
index 631797cceb..edad027953 100644
--- a/pcbnew/pcb_reference_image.h
+++ b/pcbnew/pcb_reference_image.h
@@ -117,7 +117,7 @@ public:
 
     void Move( const VECTOR2I& aMoveVector ) override { m_pos += aMoveVector; }
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
     void Rotate( const VECTOR2I& aCenter, const EDA_ANGLE& aAngle ) override;
 
     wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const override
diff --git a/pcbnew/pcb_shape.cpp b/pcbnew/pcb_shape.cpp
index b8d044f7aa..b5653f85ae 100644
--- a/pcbnew/pcb_shape.cpp
+++ b/pcbnew/pcb_shape.cpp
@@ -4,7 +4,7 @@
  * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  * Copyright (C) 2011 Wayne Stambaugh <stambaughw@gmail.com>
- * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2024 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
@@ -24,11 +24,12 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
+#include "pcb_shape.h"
+
 #include <google/protobuf/any.pb.h>
 #include <magic_enum.hpp>
 
 #include <bitmaps.h>
-#include <core/mirror.h>
 #include <macros.h>
 #include <pcb_edit_frame.h>
 #include <board_design_settings.h>
@@ -41,7 +42,6 @@
 #include <geometry/shape_compound.h>
 #include <geometry/point_types.h>
 #include <geometry/shape_utils.h>
-#include <pcb_shape.h>
 #include <pcb_painter.h>
 #include <api/board/board_types.pb.h>
 #include <api/api_enums.h>
@@ -557,15 +557,15 @@ void PCB_SHAPE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void PCB_SHAPE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_SHAPE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    flip( aCentre, aFlipLeftRight );
+    flip( aCentre, aFlipDirection );
 
     SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
 }
 
 
-void PCB_SHAPE::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
+void PCB_SHAPE::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
     // Mirror an edge of the footprint. the layer is not modified
     // This is a footprint shape modification.
@@ -577,22 +577,11 @@ void PCB_SHAPE::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
     case SHAPE_T::RECTANGLE:
     case SHAPE_T::CIRCLE:
     case SHAPE_T::BEZIER:
-        if( aMirrorAroundXAxis )
-        {
-            MIRROR( m_start.y, aCentre.y );
-            MIRROR( m_end.y, aCentre.y );
-            MIRROR( m_arcCenter.y, aCentre.y );
-            MIRROR( m_bezierC1.y, aCentre.y );
-            MIRROR( m_bezierC2.y, aCentre.y );
-        }
-        else
-        {
-            MIRROR( m_start.x, aCentre.x );
-            MIRROR( m_end.x, aCentre.x );
-            MIRROR( m_arcCenter.x, aCentre.x );
-            MIRROR( m_bezierC1.x, aCentre.x );
-            MIRROR( m_bezierC2.x, aCentre.x );
-        }
+        MIRROR( m_start, aCentre, aFlipDirection );
+        MIRROR( m_end, aCentre, aFlipDirection );
+        MIRROR( m_arcCenter, aCentre, aFlipDirection );
+        MIRROR( m_bezierC1, aCentre, aFlipDirection );
+        MIRROR( m_bezierC2, aCentre, aFlipDirection );
 
         if( GetShape() == SHAPE_T::ARC )
             std::swap( m_start, m_end );
@@ -603,7 +592,7 @@ void PCB_SHAPE::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
         break;
 
     case SHAPE_T::POLY:
-        m_poly.Mirror( !aMirrorAroundXAxis, aMirrorAroundXAxis, aCentre );
+        m_poly.Mirror( aCentre, aFlipDirection );
         break;
 
     default:
diff --git a/pcbnew/pcb_shape.h b/pcbnew/pcb_shape.h
index 95b767a7f4..4cfd93b82b 100644
--- a/pcbnew/pcb_shape.h
+++ b/pcbnew/pcb_shape.h
@@ -22,8 +22,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
  */
 
-#ifndef PCB_SHAPE_H
-#define PCB_SHAPE_H
+#pragma once
 
 #include <board_connected_item.h>
 #include <eda_shape.h>
@@ -40,7 +39,7 @@ class PCB_SHAPE : public BOARD_CONNECTED_ITEM, public EDA_SHAPE
 public:
     PCB_SHAPE( BOARD_ITEM* aParent, KICAD_T aItemType, SHAPE_T aShapeType );
 
-    PCB_SHAPE( BOARD_ITEM* aParent = NULL, SHAPE_T aShapeType = SHAPE_T::SEGMENT );
+    PCB_SHAPE( BOARD_ITEM* aParent = nullptr, SHAPE_T aShapeType = SHAPE_T::SEGMENT );
 
     // Do not create a copy constructor & operator=.
     // The ones generated by the compiler are adequate.
@@ -130,9 +129,9 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
-    virtual void Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis );
+    virtual void Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection );
 
     void Scale( double aScale );
 
@@ -180,5 +179,3 @@ protected:
         bool operator()( const BOARD_ITEM* aFirst, const BOARD_ITEM* aSecond ) const;
     };
 };
-
-#endif  // PCB_SHAPE_H
diff --git a/pcbnew/pcb_table.cpp b/pcbnew/pcb_table.cpp
index 29ed8107ba..58effa3ee2 100644
--- a/pcbnew/pcb_table.cpp
+++ b/pcbnew/pcb_table.cpp
@@ -211,10 +211,10 @@ void PCB_TABLE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void PCB_TABLE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_TABLE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
     for( PCB_TABLECELL* cell : m_cells )
-        cell->Flip( aCentre, aFlipLeftRight );
+        cell->Flip( aCentre, aFlipDirection );
 
     std::vector<PCB_TABLECELL*> oldCells = m_cells;
     int                         rowOffset = 0;
diff --git a/pcbnew/pcb_table.h b/pcbnew/pcb_table.h
index 970dcb462c..82f4eb231d 100644
--- a/pcbnew/pcb_table.h
+++ b/pcbnew/pcb_table.h
@@ -191,7 +191,7 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     const BOX2I GetBoundingBox() const override;
 
diff --git a/pcbnew/pcb_target.cpp b/pcbnew/pcb_target.cpp
index 2f92469bf3..a3d271bb9d 100644
--- a/pcbnew/pcb_target.cpp
+++ b/pcbnew/pcb_target.cpp
@@ -90,9 +90,9 @@ void PCB_TARGET::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void PCB_TARGET::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_TARGET::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aFlipLeftRight )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
         m_pos.x = aCentre.x - ( m_pos.x - aCentre.x );
     else
         m_pos.y = aCentre.y - ( m_pos.y - aCentre.y );
diff --git a/pcbnew/pcb_target.h b/pcbnew/pcb_target.h
index 5937ef11dd..5e752f4aa4 100644
--- a/pcbnew/pcb_target.h
+++ b/pcbnew/pcb_target.h
@@ -70,7 +70,7 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     wxString GetClass() const override
     {
diff --git a/pcbnew/pcb_text.cpp b/pcbnew/pcb_text.cpp
index 29c5a454bd..3c0b327d6c 100644
--- a/pcbnew/pcb_text.cpp
+++ b/pcbnew/pcb_text.cpp
@@ -436,11 +436,11 @@ void PCB_TEXT::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void PCB_TEXT::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
+void PCB_TEXT::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
     // the position and justification are mirrored, but not the text itself
 
-    if( aMirrorAroundXAxis )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
     {
         if( GetTextAngle() == ANGLE_VERTICAL )
             SetHorizJustify( (GR_TEXT_H_ALIGN_T) -GetHorizJustify() );
@@ -457,9 +457,9 @@ void PCB_TEXT::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
 }
 
 
-void PCB_TEXT::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_TEXT::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aFlipLeftRight )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
     {
         SetTextX( MIRRORVAL( GetTextPos().x, aCentre.x ) );
         SetTextAngle( -GetTextAngle() );
diff --git a/pcbnew/pcb_text.h b/pcbnew/pcb_text.h
index beca26c1c5..47fbfa503b 100644
--- a/pcbnew/pcb_text.h
+++ b/pcbnew/pcb_text.h
@@ -96,9 +96,9 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    void Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis );
+    void Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection );
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
 
diff --git a/pcbnew/pcb_textbox.cpp b/pcbnew/pcb_textbox.cpp
index 9a53831274..b793f36ee0 100644
--- a/pcbnew/pcb_textbox.cpp
+++ b/pcbnew/pcb_textbox.cpp
@@ -439,23 +439,23 @@ void PCB_TEXTBOX::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void PCB_TEXTBOX::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
+void PCB_TEXTBOX::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
     // the position and angle are mirrored, but not the text (or its justification)
-    PCB_SHAPE::Mirror( aCentre, aMirrorAroundXAxis );
+    PCB_SHAPE::Mirror( aCentre, aFlipDirection );
 
-    if( aMirrorAroundXAxis )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
         EDA_TEXT::SetTextAngle( ANGLE_180 - GetTextAngle() );
     else
         EDA_TEXT::SetTextAngle( -GetTextAngle() );
 }
 
 
-void PCB_TEXTBOX::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_TEXTBOX::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    PCB_SHAPE::Flip( aCentre, aFlipLeftRight );
+    PCB_SHAPE::Flip( aCentre, aFlipDirection );
 
-    if( aFlipLeftRight )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
         EDA_TEXT::SetTextAngle( -GetTextAngle() );
     else
         EDA_TEXT::SetTextAngle( ANGLE_180 - GetTextAngle() );
diff --git a/pcbnew/pcb_textbox.h b/pcbnew/pcb_textbox.h
index fcfeb61614..9011017cc8 100644
--- a/pcbnew/pcb_textbox.h
+++ b/pcbnew/pcb_textbox.h
@@ -105,9 +105,9 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    void Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis ) override;
+    void Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
 
diff --git a/pcbnew/pcb_track.cpp b/pcbnew/pcb_track.cpp
index c50b06c2cd..3745528e7b 100644
--- a/pcbnew/pcb_track.cpp
+++ b/pcbnew/pcb_track.cpp
@@ -662,41 +662,24 @@ void PCB_ARC::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void PCB_TRACK::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
+void PCB_TRACK::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aMirrorAroundXAxis )
-    {
-        MIRROR( m_Start.y, aCentre.y );
-        MIRROR( m_End.y, aCentre.y );
-    }
-    else
-    {
-        MIRROR( m_Start.x, aCentre.x );
-        MIRROR( m_End.x, aCentre.x );
-    }
+    MIRROR( m_Start, aCentre, aFlipDirection );
+    MIRROR( m_End, aCentre, aFlipDirection );
 }
 
 
-void PCB_ARC::Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis )
+void PCB_ARC::Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aMirrorAroundXAxis )
-    {
-        MIRROR( m_Start.y, aCentre.y );
-        MIRROR( m_End.y, aCentre.y );
-        MIRROR( m_Mid.y, aCentre.y );
-    }
-    else
-    {
-        MIRROR( m_Start.x, aCentre.x );
-        MIRROR( m_End.x, aCentre.x );
-        MIRROR( m_Mid.x, aCentre.x );
-    }
+    MIRROR( m_Start, aCentre, aFlipDirection );
+    MIRROR( m_End, aCentre, aFlipDirection );
+    MIRROR( m_Mid, aCentre, aFlipDirection );
 }
 
 
-void PCB_TRACK::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_TRACK::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aFlipLeftRight )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
     {
         m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
         m_End.x   = aCentre.x - ( m_End.x - aCentre.x );
@@ -711,9 +694,9 @@ void PCB_TRACK::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
 }
 
 
-void PCB_ARC::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_ARC::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aFlipLeftRight )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
     {
         m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
         m_End.x   = aCentre.x - ( m_End.x - aCentre.x );
@@ -740,9 +723,9 @@ bool PCB_ARC::IsCCW() const
 }
 
 
-void PCB_VIA::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void PCB_VIA::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    if( aFlipLeftRight )
+    if( aFlipDirection == FLIP_DIRECTION::LEFT_RIGHT )
     {
         m_Start.x = aCentre.x - ( m_Start.x - aCentre.x );
         m_End.x   = aCentre.x - ( m_End.x - aCentre.x );
diff --git a/pcbnew/pcb_track.h b/pcbnew/pcb_track.h
index a223cdf4bf..dbc649c7cf 100644
--- a/pcbnew/pcb_track.h
+++ b/pcbnew/pcb_track.h
@@ -104,9 +104,9 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    virtual void Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis );
+    virtual void Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection );
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     void SetPosition( const VECTOR2I& aPos ) override { m_Start = aPos; }
     VECTOR2I GetPosition() const override             { return m_Start; }
@@ -273,9 +273,9 @@ public:
 
     void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
 
-    void Mirror( const VECTOR2I& aCentre, bool aMirrorAroundXAxis ) override;
+    void Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     void SetMid( const VECTOR2I& aMid ) { m_Mid = aMid; }
     const VECTOR2I& GetMid() const      { return m_Mid; }
@@ -477,7 +477,7 @@ public:
 
     double ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const override;
 
-    void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
 #if defined (DEBUG)
     void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
diff --git a/pcbnew/pcbnew_settings.cpp b/pcbnew/pcbnew_settings.cpp
index 03a2386cd7..c5f700068a 100644
--- a/pcbnew/pcbnew_settings.cpp
+++ b/pcbnew/pcbnew_settings.cpp
@@ -71,7 +71,7 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS()
           m_ArcEditMode( ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS ),
           m_CtrlClickHighlight( false ),
           m_Use45DegreeLimit( false ),
-          m_FlipLeftRight( false ),
+          m_FlipDirection( FLIP_DIRECTION::TOP_BOTTOM ),
           m_ESCClearsNetHighlight( true ),
           m_PolarCoords( false ),
           m_RotationAngle( ANGLE_90 ),
@@ -154,7 +154,8 @@ PCBNEW_SETTINGS::PCBNEW_SETTINGS()
             &m_FootprintChooser.use_fp_filters, false ) );
 
     m_params.emplace_back( new PARAM<bool>( "editing.flip_left_right",
-            &m_FlipLeftRight, true ) );
+            reinterpret_cast<bool*>( &m_FlipDirection ),
+            static_cast<bool>( FLIP_DIRECTION::LEFT_RIGHT ) ) );
 
     m_params.emplace_back( new PARAM<bool>( "editing.esc_clears_net_highlight",
             &m_ESCClearsNetHighlight, true ) );
@@ -1003,7 +1004,7 @@ bool PCBNEW_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg )
 //{
 //    py::class_<PCBNEW_SETTINGS>( m, "settings" )
 //            .def_readwrite( "Use45DegreeGraphicSegments", &PCBNEW_SETTINGS::m_Use45DegreeGraphicSegments )
-//            .def_readwrite( "FlipLeftRight", &PCBNEW_SETTINGS::m_FlipLeftRight )
+//            .def_readwrite( "FlipLeftRight", &PCBNEW_SETTINGS::m_FlipDirection )
 //            .def_readwrite( "AddUnlockedPads", &PCBNEW_SETTINGS::m_AddUnlockedPads)
 //            .def_readwrite( "UsePolarCoords", &PCBNEW_SETTINGS::m_PolarCoords)
 //            .def_readwrite( "RotationAngle", &PCBNEW_SETTINGS::m_RotationAngle)
diff --git a/pcbnew/pcbnew_settings.h b/pcbnew/pcbnew_settings.h
index 570fb6ae1a..d8b865ddb6 100644
--- a/pcbnew/pcbnew_settings.h
+++ b/pcbnew/pcbnew_settings.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
- * Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2020-2024 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
@@ -21,6 +21,7 @@
 #ifndef PCBNEW_SETTINGS_H_
 #define PCBNEW_SETTINGS_H_
 
+#include <core/mirror.h> // for FLIP_DIRECTION
 #include <geometry/eda_angle.h>
 #include <settings/app_settings.h>
 #include <pcb_display_options.h>
@@ -426,7 +427,7 @@ public:
 
     bool m_Use45DegreeLimit;            // True to constrain tool actions to horizontal,
                                         // vertical and 45deg
-    bool m_FlipLeftRight;               // True: Flip footprints across Y axis
+    FLIP_DIRECTION m_FlipDirection;
 
     bool m_ESCClearsNetHighlight;
 
diff --git a/pcbnew/specctra_import_export/specctra_export.cpp b/pcbnew/specctra_import_export/specctra_export.cpp
index 5cae569f07..a588d8098b 100644
--- a/pcbnew/specctra_import_export/specctra_export.cpp
+++ b/pcbnew/specctra_import_export/specctra_export.cpp
@@ -1808,7 +1808,7 @@ void SPECCTRA_DB::FlipFOOTPRINTs( BOARD* aBoard )
 
         if( footprint->GetLayer() == B_Cu )
         {
-            footprint->Flip( footprint->GetPosition(), false );
+            footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
             footprint->SetFlag( 1 );
         }
     }
@@ -1829,7 +1829,7 @@ void SPECCTRA_DB::RevertFOOTPRINTs( BOARD* aBoard )
     {
         if( footprint->GetFlag() )
         {
-            footprint->Flip( footprint->GetPosition(), false );
+            footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
             footprint->SetFlag( 0 );
         }
     }
diff --git a/pcbnew/specctra_import_export/specctra_import.cpp b/pcbnew/specctra_import_export/specctra_import.cpp
index 225120dd61..f67bedf52c 100644
--- a/pcbnew/specctra_import_export/specctra_import.cpp
+++ b/pcbnew/specctra_import_export/specctra_import.cpp
@@ -409,7 +409,7 @@ void SPECCTRA_DB::FromSESSION( BOARD* aBoard )
                     if( footprint->GetLayer() != F_Cu )
                     {
                         // footprint is on copper layer (back)
-                        footprint->Flip( footprint->GetPosition(), false );
+                        footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
                     }
 
                     footprint->SetOrientation( orientation );
@@ -421,7 +421,7 @@ void SPECCTRA_DB::FromSESSION( BOARD* aBoard )
                     if( footprint->GetLayer() != B_Cu )
                     {
                         // footprint is on component layer (front)
-                        footprint->Flip( footprint->GetPosition(), false );
+                        footprint->Flip( footprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
                     }
 
                     footprint->SetOrientation( orientation );
diff --git a/pcbnew/tools/board_editor_control.cpp b/pcbnew/tools/board_editor_control.cpp
index 8aa4edcb77..36a63de0bd 100644
--- a/pcbnew/tools/board_editor_control.cpp
+++ b/pcbnew/tools/board_editor_control.cpp
@@ -1158,7 +1158,7 @@ int BOARD_EDITOR_CONTROL::PlaceFootprint( const TOOL_EVENT& aEvent )
                 // Put it on FRONT layer,
                 // (Can be stored flipped if the lib is an archive built from a board)
                 if( fp->IsFlipped() )
-                    fp->Flip( fp->GetPosition(), m_frame->GetPcbNewSettings()->m_FlipLeftRight );
+                    fp->Flip( fp->GetPosition(), m_frame->GetPcbNewSettings()->m_FlipDirection );
 
                 fp->SetOrientation( ANGLE_0 );
                 fp->SetPosition( cursorPos );
diff --git a/pcbnew/tools/edit_tool.cpp b/pcbnew/tools/edit_tool.cpp
index d482d402c3..6228604e3b 100644
--- a/pcbnew/tools/edit_tool.cpp
+++ b/pcbnew/tools/edit_tool.cpp
@@ -55,7 +55,7 @@
 #include <tools/pcb_grid_helper.h>
 #include <tools/pad_tool.h>
 #include <view/view_controls.h>
-#include <connectivity/connectivity_algo.h>
+#include <connectivity/connectivity_algo.h>`
 #include <core/kicad_algo.h>
 #include <fix_board_shape.h>
 #include <bitmaps.h>
@@ -2007,75 +2007,23 @@ int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
 
 
 /**
- * Mirror a point about the vertical axis passing through another point.
+ * Mirror a pad in the H/V axis passing through a point
  */
-static VECTOR2I mirrorPointX( const VECTOR2I& aPoint, const VECTOR2I& aMirrorPoint )
-{
-    VECTOR2I mirrored = aPoint;
-
-    mirrored.x -= aMirrorPoint.x;
-    mirrored.x = -mirrored.x;
-    mirrored.x += aMirrorPoint.x;
-
-    return mirrored;
-}
-
-
-/**
- * Mirror a point about the vertical axis passing through another point.
- */
-static VECTOR2I mirrorPointY( const VECTOR2I& aPoint, const VECTOR2I& aMirrorPoint )
-{
-    VECTOR2I mirrored = aPoint;
-
-    mirrored.y -= aMirrorPoint.y;
-    mirrored.y = -mirrored.y;
-    mirrored.y += aMirrorPoint.y;
-
-    return mirrored;
-}
-
-
-/**
- * Mirror a pad in the vertical axis passing through a point (mirror left to right).
- */
-static void mirrorPadX( PAD& aPad, const VECTOR2I& aMirrorPoint )
+static void mirrorPad( PAD& aPad, const VECTOR2I& aMirrorPoint, FLIP_DIRECTION aFlipDirection )
 {
     if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
-        aPad.FlipPrimitives( true );  // mirror primitives left to right
+        aPad.FlipPrimitives( aFlipDirection );
 
-    VECTOR2I tmpPt = mirrorPointX( aPad.GetPosition(), aMirrorPoint );
+    VECTOR2I tmpPt = aPad.GetPosition();
+    MIRROR( tmpPt, aMirrorPoint, aFlipDirection );
     aPad.SetPosition( tmpPt );
 
     tmpPt = aPad.GetOffset();
-    tmpPt.x = -tmpPt.x;
+    MIRROR( tmpPt, VECTOR2I{ 0, 0 }, aFlipDirection );
     aPad.SetOffset( tmpPt );
 
-    auto tmpz = aPad.GetDelta();
-    tmpz.x = -tmpz.x;
-    aPad.SetDelta( tmpz );
-
-    aPad.SetOrientation( -aPad.GetOrientation() );
-}
-
-
-/**
- * Mirror a pad in the vertical axis passing through a point (mirror left to right).
- */
-static void mirrorPadY( PAD& aPad, const VECTOR2I& aMirrorPoint )
-{
-    if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
-        aPad.FlipPrimitives( false );  // mirror primitives top to bottom
-
-    VECTOR2I tmpPt = mirrorPointY( aPad.GetPosition(), aMirrorPoint );
-    aPad.SetPosition( tmpPt );
-
-    tmpPt = aPad.GetOffset();
-    tmpPt.y = -tmpPt.y;
-    aPad.SetOffset( tmpPt );
-
-    auto tmpz = aPad.GetDelta();
-    tmpz.y = -tmpz.y;
+    VECTOR2I tmpz = aPad.GetDelta();
+    MIRROR( tmpz, VECTOR2I{ 0, 0 }, aFlipDirection );
     aPad.SetDelta( tmpz );
 
     aPad.SetOrientation( -aPad.GetOrientation() );
@@ -2123,17 +2071,9 @@ int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
     updateModificationPoint( selection );
     VECTOR2I mirrorPoint = selection.GetReferencePoint();
 
-    // Set the mirroring options.
-    // Unfortunately, the mirror function do not have the same parameter for all items
-    // So we need these 2 parameters to avoid mistakes
-    bool mirrorLeftRight = true;
-    bool mirrorAroundXaxis = false;
-
-    if( aEvent.IsAction( &PCB_ACTIONS::mirrorV ) )
-    {
-        mirrorLeftRight = false;
-        mirrorAroundXaxis = true;
-    }
+    FLIP_DIRECTION flipDirection = aEvent.IsAction( &PCB_ACTIONS::mirrorV )
+                                           ? FLIP_DIRECTION::TOP_BOTTOM
+                                           : FLIP_DIRECTION::LEFT_RIGHT;
 
     std::vector<EDA_ITEM*> items;
 
@@ -2165,20 +2105,20 @@ int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
         switch( item->Type() )
         {
         case PCB_SHAPE_T:
-            static_cast<PCB_SHAPE*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
+            static_cast<PCB_SHAPE*>( item )->Mirror( mirrorPoint, flipDirection );
             break;
 
         case PCB_ZONE_T:
-            static_cast<ZONE*>( item )->Mirror( mirrorPoint, mirrorLeftRight );
+            static_cast<ZONE*>( item )->Mirror( mirrorPoint, flipDirection );
             break;
 
         case PCB_FIELD_T:
         case PCB_TEXT_T:
-            static_cast<PCB_TEXT*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
+            static_cast<PCB_TEXT*>( item )->Mirror( mirrorPoint, flipDirection );
             break;
 
         case PCB_TEXTBOX_T:
-            static_cast<PCB_TEXTBOX*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
+            static_cast<PCB_TEXTBOX*>( item )->Mirror( mirrorPoint, flipDirection );
             break;
 
         case PCB_TABLE_T:
@@ -2186,17 +2126,13 @@ int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
             break;
 
         case PCB_PAD_T:
-            if( mirrorLeftRight )
-                mirrorPadX( *static_cast<PAD*>( item ), mirrorPoint );
-            else
-                mirrorPadY( *static_cast<PAD*>( item ), mirrorPoint );
-
+            mirrorPad( *static_cast<PAD*>( item ), mirrorPoint, flipDirection );
             break;
 
         case PCB_TRACE_T:
         case PCB_ARC_T:
         case PCB_VIA_T:
-            static_cast<PCB_TRACK*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
+            static_cast<PCB_TRACK*>( item )->Mirror( mirrorPoint, flipDirection );
             break;
 
         default:
@@ -2337,7 +2273,7 @@ int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
     if( selection.GetSize() == 1 )
         refPt = selection.GetReferencePoint();
 
-    bool leftRight = frame()->GetPcbNewSettings()->m_FlipLeftRight;
+    const FLIP_DIRECTION flipDirection = frame()->GetPcbNewSettings()->m_FlipDirection;
 
     for( EDA_ITEM* item : selection )
     {
@@ -2349,7 +2285,7 @@ int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
         if( !boardItem->IsNew() && !boardItem->IsMoving() )
             commit->Modify( boardItem );
 
-        boardItem->Flip( refPt, leftRight );
+        boardItem->Flip( refPt, flipDirection );
         boardItem->Normalize();
     }
 
diff --git a/pcbnew/tools/edit_tool_move_fct.cpp b/pcbnew/tools/edit_tool_move_fct.cpp
index 7949c5c43e..57ba9bf73f 100644
--- a/pcbnew/tools/edit_tool_move_fct.cpp
+++ b/pcbnew/tools/edit_tool_move_fct.cpp
@@ -126,8 +126,8 @@ int EDIT_TOOL::Swap( const TOOL_EVENT& aEvent )
             // Flip both if needed
             if( aFP->IsFlipped() != bFP->IsFlipped() )
             {
-                aFP->Flip( aPos, false );
-                bFP->Flip( bPos, false );
+                aFP->Flip( aPos, FLIP_DIRECTION::TOP_BOTTOM );
+                bFP->Flip( bPos, FLIP_DIRECTION::TOP_BOTTOM );
             }
 
             // Set orientation
diff --git a/pcbnew/tools/pcb_tool_base.cpp b/pcbnew/tools/pcb_tool_base.cpp
index 4a5c9fea08..7f4f05eec4 100644
--- a/pcbnew/tools/pcb_tool_base.cpp
+++ b/pcbnew/tools/pcb_tool_base.cpp
@@ -227,7 +227,8 @@ void PCB_TOOL_BASE::doInteractiveItemPlacement( const TOOL_EVENT&        aTool,
             }
             else if( evt->IsAction( &PCB_ACTIONS::flip ) && ( aOptions & IPO_FLIP ) )
             {
-                newItem->Flip( newItem->GetPosition(), frame()->GetPcbNewSettings()->m_FlipLeftRight );
+                newItem->Flip( newItem->GetPosition(),
+                               frame()->GetPcbNewSettings()->m_FlipDirection );
                 view()->Update( &preview );
             }
             else if( evt->IsAction( &PCB_ACTIONS::properties ) )
diff --git a/pcbnew/zone.cpp b/pcbnew/zone.cpp
index b2273273d9..bafd047406 100644
--- a/pcbnew/zone.cpp
+++ b/pcbnew/zone.cpp
@@ -740,9 +740,9 @@ void ZONE::Rotate( const VECTOR2I& aCentre, const EDA_ANGLE& aAngle )
 }
 
 
-void ZONE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
+void ZONE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
 {
-    Mirror( aCentre, aFlipLeftRight );
+    Mirror( aCentre, aFlipDirection );
 
     std::map<PCB_LAYER_ID, SHAPE_POLY_SET> fillsCopy;
 
@@ -761,14 +761,14 @@ void ZONE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
 }
 
 
-void ZONE::Mirror( const VECTOR2I& aMirrorRef, bool aMirrorLeftRight )
+void ZONE::Mirror( const VECTOR2I& aMirrorRef, FLIP_DIRECTION aFlipDirection )
 {
-    m_Poly->Mirror( aMirrorLeftRight, !aMirrorLeftRight, aMirrorRef );
+    m_Poly->Mirror( aMirrorRef, aFlipDirection );
 
     HatchBorder();
 
     for( std::pair<const PCB_LAYER_ID, std::shared_ptr<SHAPE_POLY_SET>>& pair : m_FilledPolysList )
-        pair.second->Mirror( aMirrorLeftRight, !aMirrorLeftRight, aMirrorRef );
+        pair.second->Mirror( aMirrorRef, aFlipDirection );
 }
 
 
diff --git a/pcbnew/zone.h b/pcbnew/zone.h
index 5f3d0e9366..a1d6e7496d 100644
--- a/pcbnew/zone.h
+++ b/pcbnew/zone.h
@@ -490,16 +490,17 @@ public:
      * (like Mirror() but changes layer).
      *
      * @param aCentre is the rotation point.
+     * @param aFlipDirection is the direction of the flip.
      */
-    virtual void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
+    virtual void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override;
 
     /**
      * Mirror the outlines relative to a given horizontal axis the layer is not changed.
      *
      * @param aMirrorRef is axis position
-     * @param aMirrorLeftRight mirror across Y axis (otherwise mirror across X)
+     * @param aFlipDirection is the direction of the flip.
      */
-    void Mirror( const VECTOR2I& aMirrorRef, bool aMirrorLeftRight );
+    void Mirror( const VECTOR2I& aMirrorRef, FLIP_DIRECTION aFlipDirection );
 
     /**
      * @return the class name.
diff --git a/qa/tests/common/test_bitmap_base.cpp b/qa/tests/common/test_bitmap_base.cpp
index 4873b7ed08..4330c90990 100644
--- a/qa/tests/common/test_bitmap_base.cpp
+++ b/qa/tests/common/test_bitmap_base.cpp
@@ -114,7 +114,7 @@ BOOST_AUTO_TEST_CASE( Empty )
 
     // can do this on an empty image
     empty.Rotate( true );
-    empty.Mirror( true );
+    empty.Mirror( FLIP_DIRECTION::TOP_BOTTOM );
 }
 
 
@@ -223,7 +223,7 @@ BOOST_AUTO_TEST_CASE( RotateImage )
  */
 BOOST_AUTO_TEST_CASE( MirrorImage )
 {
-    m_4tile.Mirror( true );
+    m_4tile.Mirror( FLIP_DIRECTION::TOP_BOTTOM );
 
     const wxImage* img_data = m_4tile.GetImageData();
     BOOST_REQUIRE_NE( img_data, nullptr );
diff --git a/qa/tests/pcbnew/test_board_item.cpp b/qa/tests/pcbnew/test_board_item.cpp
index d2a295f9dd..3da1a7e777 100644
--- a/qa/tests/pcbnew/test_board_item.cpp
+++ b/qa/tests/pcbnew/test_board_item.cpp
@@ -272,8 +272,8 @@ BOOST_AUTO_TEST_CASE( FlipLeftRight )
 
                         // Two equivalent flips are an identity.
 
-                        item->Flip( aRef, true );
-                        item->Flip( aRef, true );
+                        item->Flip( aRef, FLIP_DIRECTION::LEFT_RIGHT );
+                        item->Flip( aRef, FLIP_DIRECTION::LEFT_RIGHT );
 
                         CompareItems( item.get(), aOriginalItem );
                     } );
@@ -311,8 +311,8 @@ BOOST_AUTO_TEST_CASE( FlipUpDown )
 
                         // Two equivalent flips are an identity.
 
-                        item->Flip( aRef, false );
-                        item->Flip( aRef, false );
+                        item->Flip( aRef, FLIP_DIRECTION::TOP_BOTTOM );
+                        item->Flip( aRef, FLIP_DIRECTION::TOP_BOTTOM );
 
                         CompareItems( item.get(), aOriginalItem );
                     } );