← Back to team overview

kicad-developers team mailing list archive

[PATCH] GAL Microwave tools

 

Hi,

Here's a patchset to add the microwave tools to the Pcbnew GAL canvas.

First patch put some relevant functions in an isolated place, the
second one adds MICROWAVE_TOOL to Pcbnew.

Cheers,

John
From 6cffcd3ddc122ebff309896d2fc7b4851002651a Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Thu, 23 Feb 2017 14:23:17 +0800
Subject: [PATCH 2/2] Add GAL microwave tools

This adds the microwave tools to GAL in Pcbnew as a new tool:
MICROWAVE_TOOL.

Some new preview items are introduced to support this:

* TWO_POINT_GEOM_MANAGER for managing construction of two-point
  geometries, such as that used to construct the inductor.
* CENTRELINE_RECT_ITEM a preview item to draw ractangle with a given
  aspect ratio along a line (specified using a TWO_POINT_GEOM_MANAGER)

PCB_TOOL gets a generic event loops which should be useful for more
than just microwave tools:

* doInteractiveItemPlacement() - handles event loops that wait for a
  click, create an item on click and then allow moving it
  around/flipping, etc.

Fixes: lp:1531323
* https://bugs.launchpad.net/kicad/+bug/1531323
---
 common/CMakeLists.txt                          |   1 +
 common/preview_items/centreline_rect_item.cpp  | 100 +++++++
 include/preview_items/centreline_rect_item.h   |  78 +++++
 include/preview_items/two_point_geom_manager.h |  97 +++++++
 pcbnew/CMakeLists.txt                          |   2 +
 pcbnew/microwave/microwave_inductor.cpp        |  14 +-
 pcbnew/tools/microwave_tool.cpp                | 385 +++++++++++++++++++++++++
 pcbnew/tools/microwave_tool.h                  |  68 +++++
 pcbnew/tools/pcb_actions.cpp                   |  15 +
 pcbnew/tools/pcb_actions.h                     |  11 +
 pcbnew/tools/pcb_tool.cpp                      | 169 +++++++++++
 pcbnew/tools/pcb_tool.h                        |  27 ++
 pcbnew/tools/tools_common.cpp                  |   2 +
 13 files changed, 962 insertions(+), 7 deletions(-)
 create mode 100644 common/preview_items/centreline_rect_item.cpp
 create mode 100644 include/preview_items/centreline_rect_item.h
 create mode 100644 include/preview_items/two_point_geom_manager.h
 create mode 100644 pcbnew/tools/microwave_tool.cpp
 create mode 100644 pcbnew/tools/microwave_tool.h
 create mode 100644 pcbnew/tools/pcb_tool.cpp

diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index 2f9fd1d9e..0ad9dea74 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -187,6 +187,7 @@ set( COMMON_PAGE_LAYOUT_SRCS
 set( COMMON_PREVIEW_ITEMS_SRCS
     preview_items/arc_assistant.cpp
     preview_items/arc_geom_manager.cpp
+    preview_items/centreline_rect_item.cpp
     preview_items/preview_utils.cpp
     preview_items/ruler_item.cpp
     preview_items/simple_overlay_item.cpp
diff --git a/common/preview_items/centreline_rect_item.cpp b/common/preview_items/centreline_rect_item.cpp
new file mode 100644
index 000000000..b5c5cae39
--- /dev/null
+++ b/common/preview_items/centreline_rect_item.cpp
@@ -0,0 +1,100 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Kicad Developers, see change_log.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include <preview_items/centreline_rect_item.h>
+
+#include <preview_items/two_point_geom_manager.h>
+
+#include <gal/graphics_abstraction_layer.h>
+
+
+using namespace KIGFX::PREVIEW;
+
+static SHAPE_POLY_SET getRectangleAlongCentreLine(
+        const VECTOR2D& aClStart, const VECTOR2D& aClEnd, double aAspect )
+{
+    SHAPE_POLY_SET poly;
+    poly.NewOutline();
+
+    /*
+     * The point layout of the rectangle goes like this,
+     * but start/end don't have to be horz/vert
+     *
+     *  0 ---------------- 1 -----
+     *  |                  |     ^
+     *  s--------cl------->e   |cl|/aspect
+     *  |                  |     v
+     *  3----------------- 2 -----
+     */
+
+    // vector down the centre line of the rectangle
+    const VECTOR2D cl    = aClEnd - aClStart;
+
+    // the "side" of the rectangle is the centre line rotated by 90 deg
+    // and scaled by the aspect ratio
+    const VECTOR2D side = cl.Rotate( M_PI / 2.0 ) * aAspect;
+
+    VECTOR2D pt = aClStart + ( side / 2.0 );
+    poly.Append( pt );
+
+    pt += cl;
+    poly.Append( pt );
+
+    pt -= side;
+    poly.Append( pt );
+
+    pt -= cl;
+    poly.Append( pt );
+
+    return poly;
+}
+
+
+CENTRELINE_RECT_ITEM::CENTRELINE_RECT_ITEM(
+        const TWO_POINT_GEOMETRY_MANAGER& aGeomMgr,
+        double aAspect ):
+    m_geomMgr( aGeomMgr ),
+    m_aspect( aAspect )
+{
+}
+
+
+SHAPE_POLY_SET CENTRELINE_RECT_ITEM::getOutline() const
+{
+    return getRectangleAlongCentreLine( m_geomMgr.GetOrigin(),
+                                        m_geomMgr.GetEnd(),
+                                        m_aspect );
+}
+
+
+const BOX2I CENTRELINE_RECT_ITEM::ViewBBox() const
+{
+    return getOutline().BBox();
+}
+
+
+void CENTRELINE_RECT_ITEM::drawPreviewShape( KIGFX::GAL& aGal ) const
+{
+    aGal.DrawLine( m_geomMgr.GetOrigin(), m_geomMgr.GetEnd() );
+    aGal.DrawPolygon( getOutline() );
+}
diff --git a/include/preview_items/centreline_rect_item.h b/include/preview_items/centreline_rect_item.h
new file mode 100644
index 000000000..5c6bbf802
--- /dev/null
+++ b/include/preview_items/centreline_rect_item.h
@@ -0,0 +1,78 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef PREVIEW_ITEMS_CENTERLINE_RECT_ITEM_H
+#define PREVIEW_ITEMS_CENTERLINE_RECT_ITEM_H
+
+#include <preview_items/simple_overlay_item.h>
+
+#include <geometry/shape_poly_set.h>
+
+#include <math/vector2d.h>
+
+namespace KIGFX
+{
+class GAL;
+
+namespace PREVIEW
+{
+class TWO_POINT_GEOMETRY_MANAGER;
+
+/**
+ * Class CENTRELINE_RECT_ITEM
+ *
+ * Represents an area drawn by drawing a rectangle of a given aspect
+ * along a vector, with the midpoiunt of one side on the start point
+ * and the mid point of the opposite side on the end.
+ *
+ * The centre line does not have to horizontal or vertical, it
+ * can be at any angle.
+ */
+class CENTRELINE_RECT_ITEM : public SIMPLE_OVERLAY_ITEM
+{
+public:
+
+    CENTRELINE_RECT_ITEM( const TWO_POINT_GEOMETRY_MANAGER& aGeomMgr,
+                          double aAspect );
+
+    ///> Gets the bounding box of the rectangle
+    virtual const BOX2I ViewBBox() const override;
+
+private:
+
+    ///> Get the rectangular outline
+    SHAPE_POLY_SET getOutline() const;
+
+    ///> Draw rectangle and centre line onto GAL
+    void drawPreviewShape( KIGFX::GAL& aGal ) const override;
+
+    const TWO_POINT_GEOMETRY_MANAGER& m_geomMgr;
+
+    ///> The aspect ratio of the rectangle to draw
+    double m_aspect;
+};
+
+} // PREVIEW
+} // KIGFX
+
+#endif // PREVIEW_ITEMS_CENTERLINE_RECT_ITEM_H
diff --git a/include/preview_items/two_point_geom_manager.h b/include/preview_items/two_point_geom_manager.h
new file mode 100644
index 000000000..4d218cb02
--- /dev/null
+++ b/include/preview_items/two_point_geom_manager.h
@@ -0,0 +1,97 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef PREVIEW_ITEMS_TWO_POINT_GEOMETRY_MANAGER_H
+#define PREVIEW_ITEMS_TWO_POINT_GEOMETRY_MANAGER_H
+
+#include <math/vector2d.h>
+#include <common.h>
+
+namespace KIGFX
+{
+class GAL;
+
+namespace PREVIEW
+{
+
+/**
+ * Class TWO_POINT_GEOMETRY_MANAGER
+ *
+ * Represents a very simple geometry manager for items that have
+ * a start and end point.
+ */
+class TWO_POINT_GEOMETRY_MANAGER
+{
+public:
+
+    ///> Set the origin of the ruler (the fixed end)
+    void SetOrigin( const VECTOR2I& aOrigin )
+    {
+        m_origin = aOrigin;
+    }
+
+    VECTOR2I GetOrigin() const
+    {
+        return m_origin;
+    }
+
+    /**
+     * Set the current end of the rectangle (the end that moves
+     * with the cursor.
+     */
+    void SetEnd( const VECTOR2I& aEnd )
+    {
+        if( m_angleSnap )
+        {
+            const auto vec = aEnd - m_origin;
+            const auto len = vec.EuclideanNorm();
+            const auto angle = KiROUND( vec.Angle() / M_PI_4 ) * M_PI_4;
+
+            m_end = m_origin + VECTOR2I( len, 0 ).Rotate( angle );
+        }
+        else
+        {
+            m_end = aEnd;
+        }
+    }
+
+    VECTOR2I GetEnd() const
+    {
+        return m_end;
+    }
+
+    void SetAngleSnap( bool aSnap )
+    {
+        m_angleSnap = aSnap;
+    }
+
+private:
+
+    VECTOR2I m_origin, m_end;
+    bool m_angleSnap = false;
+};
+
+} // PREVIEW
+} // KIGFX
+
+#endif // PREVIEW_ITEMS_TWO_POINT_GEOMETRY_MANAGER_H
diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index e0ed2bf24..5f190315f 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -295,6 +295,7 @@ set( PCBNEW_CLASS_SRCS
     class_footprint_wizard.cpp
     class_action_plugin.cpp
 
+    tools/pcb_tool.cpp
     tools/selection_tool.cpp
     tools/pcb_selection_conditions.cpp
     tools/pcb_bright_box.cpp
@@ -305,6 +306,7 @@ set( PCBNEW_CLASS_SRCS
     tools/edit_tool.cpp
     tools/pcbnew_control.cpp
     tools/pcb_editor_control.cpp
+    tools/microwave_tool.cpp
     tools/module_editor_tools.cpp
     tools/placement_tool.cpp
     tools/pcb_actions.cpp
diff --git a/pcbnew/microwave/microwave_inductor.cpp b/pcbnew/microwave/microwave_inductor.cpp
index f7a875a79..b0394a2ec 100644
--- a/pcbnew/microwave/microwave_inductor.cpp
+++ b/pcbnew/microwave/microwave_inductor.cpp
@@ -307,10 +307,10 @@ MODULE* MWAVE::CreateMicrowaveInductor( INDUCTOR_PATTERN& inductorPattern,
 
     // Enter the desired length.
     msg = StringFromValue( g_UserUnit, inductorPattern.m_length );
-    wxTextEntryDialog dlg( NULL, wxEmptyString, _( "Length of Trace:" ), msg );
+    wxTextEntryDialog dlg( nullptr, wxEmptyString, _( "Length of Trace:" ), msg );
 
     if( dlg.ShowModal() != wxID_OK )
-        return NULL; // canceled by user
+        return nullptr; // canceled by user
 
     msg = dlg.GetValue();
     inductorPattern.m_length = ValueFromString( g_UserUnit, msg );
@@ -319,7 +319,7 @@ MODULE* MWAVE::CreateMicrowaveInductor( INDUCTOR_PATTERN& inductorPattern,
     if( inductorPattern.m_length < min_len )
     {
         aErrorMessage = _( "Requested length < minimum length" );
-        return NULL;
+        return nullptr;
     }
 
     // Calculate the elements.
@@ -331,16 +331,16 @@ MODULE* MWAVE::CreateMicrowaveInductor( INDUCTOR_PATTERN& inductorPattern,
     if( !ll )
     {
         aErrorMessage = _( "Requested length too large" );
-        return NULL;
+        return nullptr;
     }
 
     // Generate footprint. the value is also used as footprint name.
-    msg.Empty();
-    wxTextEntryDialog cmpdlg( NULL, wxEmptyString, _( "Component Value:" ), msg );
+    msg = "L";
+    wxTextEntryDialog cmpdlg( nullptr, wxEmptyString, _( "Component Value:" ), msg );
     cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );
 
     if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
-        return NULL;    //  Aborted by user
+        return nullptr;    //  Aborted by user
 
     MODULE* module = aPcbFrame->CreateNewModule( msg );
 
diff --git a/pcbnew/tools/microwave_tool.cpp b/pcbnew/tools/microwave_tool.cpp
new file mode 100644
index 000000000..a3b1b856c
--- /dev/null
+++ b/pcbnew/tools/microwave_tool.cpp
@@ -0,0 +1,385 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Kicad Developers, see change_log.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include "microwave_tool.h"
+
+#include <gal/graphics_abstraction_layer.h>
+#include <class_draw_panel_gal.h>
+#include <view/view_controls.h>
+#include <view/view.h>
+#include <tool/tool_manager.h>
+#include <board_commit.h>
+#include <confirm.h>
+#include <preview_items/two_point_geom_manager.h>
+#include <preview_items/centreline_rect_item.h>
+
+// For frame ToolID values
+#include <pcbnew_id.h>
+
+// For action icons
+#include <bitmaps.h>
+
+#include <class_board_item.h>
+#include <class_module.h>
+
+#include <microwave/microwave_inductor.h>
+
+#include "pcb_actions.h"
+#include "selection_tool.h"
+#include "tool_event_utils.h"
+
+
+///> Type of items that are "simple" - just get placed on
+///> the board directly, without a graphical interactive setup stage
+enum MWAVE_TOOL_SIMPLE_ID
+{
+    GAP,
+    STUB,
+    STUB_ARC,
+    FUNCTION_SHAPE,
+};
+
+TOOL_ACTION PCB_ACTIONS::microwaveCreateGap(
+        "pcbnew.MicrowaveTool.createGap",
+        AS_GLOBAL, 0,
+        _( "Add Gap" ), _( "Create gap of specified length for microwave applications" ),
+        mw_add_gap_xpm, AF_ACTIVATE, (void*) MWAVE_TOOL_SIMPLE_ID::GAP );
+
+TOOL_ACTION PCB_ACTIONS::microwaveCreateStub(
+        "pcbnew.MicrowaveTool.createStub",
+        AS_GLOBAL, 0,
+        _( "Add Stub" ), _( "Create stub of specified length for microwave applications" ),
+        mw_add_stub_xpm, AF_ACTIVATE, (void*) MWAVE_TOOL_SIMPLE_ID::STUB );
+
+TOOL_ACTION PCB_ACTIONS::microwaveCreateStubArc(
+        "pcbnew.MicrowaveTool.createStubArc",
+        AS_GLOBAL, 0,
+        _( "Add Arc Stub" ), _( "Create stub (arc) of specified length for microwave applications" ),
+        mw_add_stub_arc_xpm, AF_ACTIVATE, (void*) MWAVE_TOOL_SIMPLE_ID::STUB_ARC );
+
+TOOL_ACTION PCB_ACTIONS::microwaveCreateFunctionShape(
+        "pcbnew.MicrowaveTool.createFunctionShape",
+        AS_GLOBAL, 0,
+        _( "Add Polynomial Shape" ), _( "Create polynomial shape for microwave applications" ),
+        mw_add_gap_xpm, AF_ACTIVATE, (void*) MWAVE_TOOL_SIMPLE_ID::FUNCTION_SHAPE );
+
+TOOL_ACTION PCB_ACTIONS::microwaveCreateLine(
+        "pcbnew.MicrowaveTool.createLine",
+        AS_GLOBAL, 0,
+        _( "Add Microwave Line" ), _( "Create line of specified length for microwave applications" ),
+        mw_add_line_xpm, AF_ACTIVATE );
+
+
+MICROWAVE_TOOL::MICROWAVE_TOOL() :
+        PCB_TOOL( "pcbnew.MicrowaveTool" ),
+        m_menu( *this )
+{
+}
+
+
+MICROWAVE_TOOL::~MICROWAVE_TOOL()
+{}
+
+
+void MICROWAVE_TOOL::Reset( RESET_REASON aReason )
+{
+}
+
+
+bool MICROWAVE_TOOL::Init()
+{
+    auto activeToolFunctor = [ this ] ( const SELECTION& aSel ) {
+        return true;
+    };
+
+    auto& ctxMenu = m_menu.GetMenu();
+
+    // cancel current goes in main context menu at the top if present
+    ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1000 );
+    ctxMenu.AddSeparator( activeToolFunctor, 1000 );
+
+    m_menu.AddStandardSubMenus( *getEditFrame<PCB_BASE_FRAME>() );
+
+    return true;
+}
+
+
+struct MICROWAVE_TOOL_INFO
+{
+    using MOD_CREATOR = std::function<std::unique_ptr<MODULE>()>;
+
+    wxString  name;
+    int       toolId;
+    MOD_CREATOR creatorFunc;
+};
+
+
+MICROWAVE_TOOL_INFO getMicrowaveItemCreator( PCB_EDIT_FRAME& frame, int aParam )
+{
+    MICROWAVE_TOOL_INFO info;
+
+    switch( aParam )
+    {
+    case MWAVE_TOOL_SIMPLE_ID::GAP:
+        info.name =  _( "Add Gap" );
+        info.toolId = ID_PCB_MUWAVE_TOOL_GAP_CMD;
+        info.creatorFunc = [&frame] () {
+            return std::unique_ptr<MODULE>( frame.Create_MuWaveComponent( 0 ) );
+        };
+        break;
+
+    case MWAVE_TOOL_SIMPLE_ID::STUB:
+        info.name =  _( "Add Stub" );
+        info.toolId = ID_PCB_MUWAVE_TOOL_STUB_CMD;
+        info.creatorFunc = [&frame] () {
+            return std::unique_ptr<MODULE>( frame.Create_MuWaveComponent( 1 ) );
+        };
+        break;
+
+    case MWAVE_TOOL_SIMPLE_ID::STUB_ARC:
+        info.name =  _( "Add Stub (Arc)" );
+        info.toolId = ID_PCB_MUWAVE_TOOL_STUB_ARC_CMD;
+        info.creatorFunc = [&frame] () {
+            return std::unique_ptr<MODULE>( frame.Create_MuWaveComponent( 2 ) );
+        };
+        break;
+
+    case MWAVE_TOOL_SIMPLE_ID::FUNCTION_SHAPE:
+        info.name =  _( "Add Polynomial Shape" );
+        info.toolId = ID_PCB_MUWAVE_TOOL_FUNCTION_SHAPE_CMD;
+        info.creatorFunc = [&frame] () {
+            return std::unique_ptr<MODULE>( frame.Create_MuWavePolygonShape() );
+        };
+        break;
+    };
+
+    return info;
+}
+
+
+int MICROWAVE_TOOL::addMicrowaveFootprint( const TOOL_EVENT& aEvent )
+{
+    auto& frame = *getEditFrame<PCB_EDIT_FRAME>();
+
+    const int param = aEvent.Parameter<intptr_t>();
+
+    MICROWAVE_TOOL_INFO info = getMicrowaveItemCreator( frame, param );
+
+    // failed to find suitable item info - shouldn't be possible
+    // if all the id's are handled
+    if( !info.name )
+    {
+        wxASSERT_MSG( 0, "Failed to find suitable microwave tool info" );
+        return 0;
+    }
+
+    frame.SetToolID( info.toolId, wxCURSOR_PENCIL, info.name );
+
+    ITEM_CREATOR moduleCreator = [this, &info] ( const TOOL_EVENT& aAddingEvent ) {
+            auto module = info.creatorFunc();
+
+            // Module has been added in the legacy backend,
+            // so we have to remove it before committing the change
+            // @todo LEGACY
+            if( module )
+            {
+                board()->Remove( module.get() );
+            }
+
+            return module;
+    };
+
+    doInteractiveItemPlacement( moduleCreator,  _( "Place microwave feature" ) );
+
+    frame.SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+    return 0;
+}
+
+
+void MICROWAVE_TOOL::createInductorBetween( const VECTOR2I& aStart, const VECTOR2I& aEnd )
+{
+    auto& frame = *getEditFrame<PCB_EDIT_FRAME>();
+
+    MWAVE::INDUCTOR_PATTERN pattern;
+
+    pattern.m_Width = board()->GetDesignSettings().GetCurrentTrackWidth();
+
+    pattern.m_Start = { aStart.x, aStart.y };
+    pattern.m_End = { aEnd.x, aEnd.y };
+
+    wxString errorMessage;
+
+    auto inductorModule = std::unique_ptr<MODULE>(
+            CreateMicrowaveInductor( pattern, &frame, errorMessage )
+    );
+
+    if( inductorModule )
+    {
+        // legacy mode tools add to the board
+        // so remove it and add it back with the commit object
+        // this has to happen, even if we don't end up storing the module
+        // @todo LEGACY
+        board()->Remove( inductorModule.get() );
+    }
+
+    // on any error, report if we can
+    if ( !inductorModule || !errorMessage.IsEmpty() )
+    {
+        if ( !errorMessage.IsEmpty() )
+        {
+            DisplayError( &frame, errorMessage );
+        }
+    }
+    else
+    {
+        // at this point, we can save the module
+        frame.SetCurItem( inductorModule.get() );
+
+        BOARD_COMMIT commit( this );
+        commit.Add( inductorModule.release() );
+        commit.Push( _("Add microwave inductor" ) );
+    }
+}
+
+
+static const COLOR4D inductorAreaFill( 0.3, 0.3, 0.5, 0.3 );
+static const COLOR4D inductorAreaStroke( 0.4, 1.0, 1.0, 1.0 );
+static const double  inductorAreaStrokeWidth = 1.0;
+
+///> Aspect of the preview rectangle - this is hardcoded in the
+///> microwave backend for now
+static const double  inductorAreaAspect = 0.5;
+
+
+int MICROWAVE_TOOL::drawMicrowaveInductor( const TOOL_EVENT& aEvent )
+{
+    using namespace KIGFX::PREVIEW;
+
+    KIGFX::VIEW& view = *getView();
+    KIGFX::VIEW_CONTROLS& controls = *getViewControls();
+    auto& frame = *getEditFrame<PCB_EDIT_FRAME>();
+
+    frame.SetToolID( ID_PCB_MUWAVE_TOOL_SELF_CMD, wxCURSOR_PENCIL, _( "Add Microwave Inductor" ) );
+
+    Activate();
+
+    TWO_POINT_GEOMETRY_MANAGER tpGeomMgr;
+
+    CENTRELINE_RECT_ITEM previewRect( tpGeomMgr, inductorAreaAspect );
+
+    previewRect.SetFillColor( inductorAreaFill );
+    previewRect.SetStrokeColor( inductorAreaStroke );
+    previewRect.SetLineWidth( inductorAreaStrokeWidth );
+
+    bool originSet = false;
+
+    controls.ShowCursor( true );
+    controls.SetSnapping( true );
+
+    view.Add( &previewRect );
+
+    while( auto evt = Wait() )
+    {
+        VECTOR2I cursorPos = controls.GetCursorPosition();
+
+        if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
+        {
+            // overriding action, or we're cancelling without
+            // an in-progress preview area
+            if ( evt->IsActivate() || !originSet )
+            {
+                break;
+            }
+
+            // had an in-progress area, so start again but don't
+            // cancel the tool
+            originSet = false;
+            view.SetVisible( &previewRect, false );
+            view.Update( &previewRect, KIGFX::GEOMETRY );
+        }
+
+        // A click or drag starts
+        else if( !originSet &&
+                ( evt->IsClick( BUT_LEFT ) || evt->IsDrag( BUT_LEFT ) ) )
+        {
+            tpGeomMgr.SetOrigin( cursorPos );
+            tpGeomMgr.SetEnd( cursorPos );
+
+            originSet = true;
+
+            controls.CaptureCursor( true );
+            controls.SetAutoPan( true );
+        }
+
+        // another click after origin set is the end
+        // left up is also the end, as you'll only get that after a drag
+        else if( originSet &&
+                ( evt->IsClick( BUT_LEFT ) || evt->IsMouseUp( BUT_LEFT ) ) )
+        {
+            // second click, we're done:
+            // delegate to the point-to-point inductor creator function
+            createInductorBetween( tpGeomMgr.GetOrigin(), tpGeomMgr.GetEnd() );
+
+            // start again if needed
+            originSet = false;
+
+            view.SetVisible( &previewRect, false );
+            view.Update( &previewRect, KIGFX::GEOMETRY );
+        }
+
+        // any move or drag once the origin was set updates
+        // the end point
+        else if( originSet &&
+                ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
+        {
+            tpGeomMgr.SetAngleSnap( evt->Modifier( MD_CTRL ) );
+            tpGeomMgr.SetEnd( cursorPos );
+
+            view.SetVisible( &previewRect, true );
+            view.Update( &previewRect, KIGFX::GEOMETRY );
+        }
+
+        else if( evt->IsClick( BUT_RIGHT ) )
+        {
+            m_menu.ShowContextMenu();
+        }
+    }
+
+    view.Remove( &previewRect );
+
+    frame.SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+
+    return 0;
+}
+
+
+void MICROWAVE_TOOL::SetTransitions()
+{
+    Go( &MICROWAVE_TOOL::addMicrowaveFootprint, PCB_ACTIONS::microwaveCreateGap.MakeEvent() );
+    Go( &MICROWAVE_TOOL::addMicrowaveFootprint, PCB_ACTIONS::microwaveCreateStub.MakeEvent() );
+    Go( &MICROWAVE_TOOL::addMicrowaveFootprint, PCB_ACTIONS::microwaveCreateStubArc.MakeEvent() );
+    Go( &MICROWAVE_TOOL::addMicrowaveFootprint, PCB_ACTIONS::microwaveCreateFunctionShape.MakeEvent() );
+
+    Go( &MICROWAVE_TOOL::drawMicrowaveInductor, PCB_ACTIONS::microwaveCreateLine.MakeEvent() );
+}
diff --git a/pcbnew/tools/microwave_tool.h b/pcbnew/tools/microwave_tool.h
new file mode 100644
index 000000000..adad7ad7a
--- /dev/null
+++ b/pcbnew/tools/microwave_tool.h
@@ -0,0 +1,68 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 Kicad Developers, see change_log.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef TOOLS_MICROWAVE_TOOL_H
+#define TOOLS_MICROWAVE_TOOL_H
+
+#include <tools/pcb_tool.h>
+
+#include <tool/tool_menu.h>
+
+
+/**
+ * Class MICROWAVE_TOOL
+ *
+ * Tool responsible for adding microwave features to PCBs
+ */
+class MICROWAVE_TOOL : public PCB_TOOL
+{
+public:
+    MICROWAVE_TOOL();
+    ~MICROWAVE_TOOL();
+
+    ///> React to model/view changes
+    void Reset( RESET_REASON aReason ) override;
+
+    ///> Basic initalization
+    bool Init() override;
+
+    ///> Bind handlers to corresponding TOOL_ACTIONs
+    void SetTransitions() override;
+
+private:
+
+    ///> Main interactive tool
+    int addMicrowaveFootprint( const TOOL_EVENT& aEvent );
+
+    ///> Create an inductor between the two points
+    void createInductorBetween( const VECTOR2I& aStart, const VECTOR2I& aEnd );
+
+    ///> Draw a microwave inductor interactively
+    int drawMicrowaveInductor( const TOOL_EVENT& aEvent );
+
+    /// Menu model displayed by the tool.
+    TOOL_MENU m_menu;
+};
+
+
+#endif // TOOLS_MICROWAVE_TOOL_H
diff --git a/pcbnew/tools/pcb_actions.cpp b/pcbnew/tools/pcb_actions.cpp
index 486450f9f..be83c3704 100644
--- a/pcbnew/tools/pcb_actions.cpp
+++ b/pcbnew/tools/pcb_actions.cpp
@@ -163,6 +163,21 @@ boost::optional<TOOL_EVENT> PCB_ACTIONS::TranslateLegacyId( int aId )
 
     case ID_PCB_SHOW_1_RATSNEST_BUTT:
         return PCB_ACTIONS::toBeDone.MakeEvent();
+
+    case ID_PCB_MUWAVE_TOOL_GAP_CMD:
+        return PCB_ACTIONS::microwaveCreateGap.MakeEvent();
+
+    case ID_PCB_MUWAVE_TOOL_STUB_CMD:
+        return PCB_ACTIONS::microwaveCreateStub.MakeEvent();
+
+    case ID_PCB_MUWAVE_TOOL_STUB_ARC_CMD:
+        return PCB_ACTIONS::microwaveCreateStubArc.MakeEvent();
+
+    case ID_PCB_MUWAVE_TOOL_FUNCTION_SHAPE_CMD:
+        return PCB_ACTIONS::microwaveCreateFunctionShape.MakeEvent();
+
+    case ID_PCB_MUWAVE_TOOL_SELF_CMD:
+        return PCB_ACTIONS::microwaveCreateLine.MakeEvent();
     }
 
     return boost::optional<TOOL_EVENT>();
diff --git a/pcbnew/tools/pcb_actions.h b/pcbnew/tools/pcb_actions.h
index 21615c7ff..1ebfa7bbb 100644
--- a/pcbnew/tools/pcb_actions.h
+++ b/pcbnew/tools/pcb_actions.h
@@ -294,6 +294,17 @@ public:
     /// Copy the current pad's settings to other pads in the module or on the board
     static TOOL_ACTION pushPadSettings;
 
+    // Microwave tools
+    static TOOL_ACTION microwaveCreateGap;
+
+    static TOOL_ACTION microwaveCreateStub;
+
+    static TOOL_ACTION microwaveCreateStubArc;
+
+    static TOOL_ACTION microwaveCreateFunctionShape;
+
+    static TOOL_ACTION microwaveCreateLine;
+
     /// Cursor control with keyboard
     static TOOL_ACTION cursorUp;
     static TOOL_ACTION cursorDown;
diff --git a/pcbnew/tools/pcb_tool.cpp b/pcbnew/tools/pcb_tool.cpp
new file mode 100644
index 000000000..9f921b4b7
--- /dev/null
+++ b/pcbnew/tools/pcb_tool.cpp
@@ -0,0 +1,169 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+
+#include "pcb_tool.h"
+
+#include <view/view_controls.h>
+#include <view/view.h>
+#include <tool/tool_manager.h>
+#include <board_commit.h>
+
+#include <class_module.h>
+
+#include "selection_tool.h"
+#include "pcb_actions.h"
+#include "tool_event_utils.h"
+
+
+void PCB_TOOL::doInteractiveItemPlacement( ITEM_CREATOR aItemCreator,
+                                           const wxString& aCommitMessage )
+{
+    using namespace std::placeholders;
+
+    KIGFX::VIEW& view = *getView();
+    KIGFX::VIEW_CONTROLS& controls = *getViewControls();
+    auto& frame = *getEditFrame<PCB_EDIT_FRAME>();
+
+    std::unique_ptr<BOARD_ITEM> newItem;
+
+    Activate();
+
+    BOARD_COMMIT commit( &frame );
+
+    GetManager()->RunAction( PCB_ACTIONS::selectionClear, true );
+
+    // do not capture or auto-pan until we start placing an item
+    controls.ShowCursor( true );
+    controls.SetSnapping( true );
+
+    // Add a VIEW_GROUP that serves as a preview for the new item
+    SELECTION preview;
+    view.Add( &preview );
+
+    // Main loop: keep receiving events
+    while( OPT_TOOL_EVENT evt = Wait() )
+    {
+        VECTOR2I cursorPos = controls.GetCursorPosition();
+
+        if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
+        {
+            if( newItem )
+            {
+                // Delete the old item and have another try
+                newItem = nullptr;
+
+                preview.Clear();
+
+                controls.SetAutoPan( false );
+                controls.CaptureCursor( false );
+                controls.ShowCursor( true );
+            }
+            else
+            {
+                break;
+            }
+
+            if( evt->IsActivate() )  // now finish unconditionally
+                break;
+        }
+
+        else if( evt->IsClick( BUT_LEFT ) )
+        {
+            if( !newItem )
+            {
+                // create the item if possible
+                newItem = aItemCreator( *evt );
+
+                // no item created, so wait for another click
+                if( !newItem  )
+                    continue;
+
+                controls.CaptureCursor( true );
+                controls.SetAutoPan( true );
+
+                newItem->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+
+                preview.Add( newItem.get() );
+
+                if( newItem->Type() == PCB_MODULE_T )
+                {
+                    auto module = dyn_cast<MODULE*>( newItem.get() );
+
+                    // modules have more drawable parts
+                    module->RunOnChildren( std::bind( &KIGFX::VIEW_GROUP::Add, &preview, _1 ) );
+                }
+            }
+            else
+            {
+                newItem->ClearFlags();
+                preview.Remove( newItem.get() );
+
+                if( newItem->Type() == PCB_MODULE_T )
+                {
+                    auto module = dyn_cast<MODULE*>( newItem.get() );
+                    module->RunOnChildren( std::bind( &KIGFX::VIEW_GROUP::Remove, &preview, _1 ) );
+                }
+
+                commit.Add( newItem.release() );
+                commit.Push( aCommitMessage );
+
+                controls.CaptureCursor( false );
+                controls.SetAutoPan( false );
+                controls.ShowCursor( true );
+            }
+        }
+
+        else if( newItem && evt->Category() == TC_COMMAND )
+        {
+            /*
+             * Handle any events that can affect the item as we move
+             * it around, eg rotate and flip
+             */
+
+            if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) )
+            {
+                const auto rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle(
+                        frame, *evt );
+                newItem->Rotate( newItem->GetPosition(), rotationAngle );
+                view.Update( &preview );
+            }
+            else if( evt->IsAction( &PCB_ACTIONS::flip ) )
+            {
+                newItem->Flip( newItem->GetPosition() );
+                view.Update( &preview );
+            }
+        }
+
+        else if( newItem && evt->IsMotion() )
+        {
+            // track the cursor
+            newItem->SetPosition( wxPoint( cursorPos.x, cursorPos.y ) );
+
+            // Show a preview of the item
+            view.Update( &preview );
+        }
+    }
+
+    view.Remove( &preview );
+}
diff --git a/pcbnew/tools/pcb_tool.h b/pcbnew/tools/pcb_tool.h
index accc8f0d3..cac03fc42 100644
--- a/pcbnew/tools/pcb_tool.h
+++ b/pcbnew/tools/pcb_tool.h
@@ -33,6 +33,9 @@
 #include <class_board.h>
 #include <view/view_group.h>
 
+#include <functional>
+
+
 /**
  * Class PCB_TOOL
  *
@@ -78,6 +81,30 @@ public:
     }
 
 protected:
+
+    /**
+     * Callable that returns a new board item.
+     *
+     * The event that triggered it is provided, so you can check modifier
+     * keys, position, etc, if required
+     */
+    using ITEM_CREATOR = std::function< std::unique_ptr< BOARD_ITEM >( const TOOL_EVENT& aEvt ) >;
+
+    /**
+     * Helper function for performing a common interactive idiom:
+     * wait for a left click, place an item there (perhaps with a
+     * dialog or other user interaction), then have it move with
+     * the mouse and respond to rotate/flip, etc
+     *
+     * More complex interactive processes are not supported here, you
+     * should implement a customised event loop for those.
+     *
+     * @param aItemCreator the callable that will attempt to create the item
+     * @param aCommitMessage the message used on a successful commit
+     */
+    void doInteractiveItemPlacement( ITEM_CREATOR aItemCreator,
+                                     const wxString& aCommitMessage );
+
     KIGFX::VIEW* view() const { return getView(); }
     PCB_EDIT_FRAME* frame() const { return getEditFrame<PCB_EDIT_FRAME>(); }
     BOARD* board() const { return getModel<BOARD>(); }
diff --git a/pcbnew/tools/tools_common.cpp b/pcbnew/tools/tools_common.cpp
index d06efedec..a653ebf24 100644
--- a/pcbnew/tools/tools_common.cpp
+++ b/pcbnew/tools/tools_common.cpp
@@ -38,6 +38,7 @@
 #include <tools/pcb_editor_control.h>
 #include <tools/placement_tool.h>
 #include <tools/pad_tool.h>
+#include <tools/microwave_tool.h>
 #include <tools/pcb_actions.h>
 
 #include <router/router_tool.h>
@@ -58,4 +59,5 @@ void PCB_ACTIONS::RegisterAllTools( TOOL_MANAGER* aToolManager )
     aToolManager->RegisterTool( new PCBNEW_CONTROL );
     aToolManager->RegisterTool( new PCB_EDITOR_CONTROL );
     aToolManager->RegisterTool( new PLACEMENT_TOOL );
+    aToolManager->RegisterTool( new MICROWAVE_TOOL );
 }
-- 
2.12.0

From 1db4cc4b8e57f7b2eb5a6527756591f085184fe9 Mon Sep 17 00:00:00 2001
From: John Beard <john.j.beard@xxxxxxxxx>
Date: Thu, 23 Feb 2017 16:00:37 +0800
Subject: [PATCH 1/2] Allow calling of MW inductor function externally

This makes it possible to access the creation functions
for the microwave inductor externally, so that it can be
used from GAL.

Unlike the Gap/Stub tools, the inductor tool is more interactive, and
doesn't just return a MODULE*.

Introduces a new directory in pcbnew called 'microwave' to better
isolate generic (i.e. canvas-agnostic) functions, and a namespace MWAVE
to avoid polluting the global namespace.
---
 pcbnew/CMakeLists.txt                   |  12 +-
 pcbnew/microwave/microwave_inductor.cpp | 405 +++++++++++++++++++++++++++++++
 pcbnew/microwave/microwave_inductor.h   |  55 +++++
 pcbnew/muonde.cpp                       | 418 ++------------------------------
 4 files changed, 484 insertions(+), 406 deletions(-)
 create mode 100644 pcbnew/microwave/microwave_inductor.cpp
 create mode 100644 pcbnew/microwave/microwave_inductor.h

diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index b68610ad9..e0ed2bf24 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -194,6 +194,10 @@ set( PCBNEW_AUTOROUTER_SRCS
     autorouter/work.cpp
     )
 
+set( PCBNEW_MICROWAVE_SRCS
+    microwave/microwave_inductor.cpp
+    )
+
 set( PCBNEW_CLASS_SRCS
     board_commit.cpp
     tool_modview.cpp
@@ -317,8 +321,12 @@ set( PCBNEW_CLASS_SRCS
     footprint_preview_panel.cpp
     )
 
-set( PCBNEW_SRCS ${PCBNEW_AUTOROUTER_SRCS} ${PCBNEW_CLASS_SRCS} ${PCBNEW_DIALOGS} )
-
+set( PCBNEW_SRCS
+    ${PCBNEW_AUTOROUTER_SRCS}
+    ${PCBNEW_MICROWAVE_SRCS}
+    ${PCBNEW_CLASS_SRCS}
+    ${PCBNEW_DIALOGS}
+    )
 
 # extra sources from common
 set( PCBNEW_COMMON_SRCS
diff --git a/pcbnew/microwave/microwave_inductor.cpp b/pcbnew/microwave/microwave_inductor.cpp
new file mode 100644
index 000000000..f7a875a79
--- /dev/null
+++ b/pcbnew/microwave/microwave_inductor.cpp
@@ -0,0 +1,405 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include "microwave_inductor.h"
+
+#include <wx/wx.h>
+
+#include <base_units.h>
+#include <validators.h>
+#include <wxPcbStruct.h>
+
+#include <class_pad.h>
+#include <class_edge_mod.h>
+#include <class_module.h>
+
+
+using namespace MWAVE;
+
+/**
+ * Function  gen_arc
+ * generates an arc using arc approximation by lines:
+ * Center aCenter
+ * Angle "angle" (in 0.1 deg)
+ * @param  aBuffer = a buffer to store points.
+ * @param  aStartPoint = starting point of arc.
+ * @param  aCenter = arc centre.
+ * @param  a_ArcAngle = arc length in 0.1 degrees.
+ */
+static void gen_arc( std::vector <wxPoint>& aBuffer,
+                     const wxPoint&         aStartPoint,
+                     const wxPoint&         aCenter,
+                     int                     a_ArcAngle )
+{
+    const int SEGM_COUNT_PER_360DEG = 16;
+    auto    first_point = aStartPoint - aCenter;
+    int     seg_count   = ( ( abs( a_ArcAngle ) ) * SEGM_COUNT_PER_360DEG ) / 3600;
+
+    if( seg_count == 0 )
+        seg_count = 1;
+
+    double increment_angle = (double) a_ArcAngle * M_PI / 1800 / seg_count;
+
+    // Creates nb_seg point to approximate arc by segments:
+    for( int ii = 1; ii <= seg_count; ii++ )
+    {
+        double  rot_angle = increment_angle * ii;
+        double  fcos = cos( rot_angle );
+        double  fsin = sin( rot_angle );
+        wxPoint currpt;
+
+        // Rotate current point:
+        currpt.x = KiROUND( ( first_point.x * fcos + first_point.y * fsin ) );
+        currpt.y = KiROUND( ( first_point.y * fcos - first_point.x * fsin ) );
+
+        auto corner = aCenter + currpt;
+        aBuffer.push_back( corner );
+    }
+}
+
+
+/**
+ * Function BuildCornersList_S_Shape
+ * Create a path like a S-shaped coil
+ * @param  aBuffer =  a buffer where to store points (ends of segments)
+ * @param  aStartPoint = starting point of the path
+ * @param  aEndPoint = ending point of the path
+ * @param  aLength = full length of the path
+ * @param  aWidth = segment width
+ */
+static int BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer,
+                              const wxPoint& aStartPoint,
+                              const wxPoint& aEndPoint,
+                              int aLength, int aWidth )
+{
+/* We must determine:
+ * segm_count = number of segments perpendicular to the direction
+ * segm_len = length of a strand
+ * radius = radius of rounded parts of the coil
+ * stubs_len = length of the 2 stubs( segments parallel to the direction)
+ *         connecting the start point to the start point of the S shape
+ *         and the ending point to the end point of the S shape
+ * The equations are (assuming the area size of the entire shape is Size:
+ * Size.x = 2 * radius + segm_len
+ * Size.y = (segm_count + 2 ) * 2 * radius + 2 * stubs_len
+ * inductorPattern.m_length = 2 * delta // connections to the coil
+ *             + (segm_count-2) * segm_len      // length of the strands except 1st and last
+ *             + (segm_count) * (PI * radius)   // length of rounded
+ * segm_len + / 2 - radius * 2)                 // length of 1st and last bit
+ *
+ * The constraints are:
+ * segm_count >= 2
+ * radius < m_Size.x
+ * Size.y = (radius * 4) + (2 * stubs_len)
+ * segm_len > radius * 2
+ *
+ * The calculation is conducted in the following way:
+ * first:
+ * segm_count = 2
+ * radius = 4 * Size.x (arbitrarily fixed value)
+ * Then:
+ * Increasing the number of segments to the desired length
+ * (radius decreases if necessary)
+ */
+    wxPoint size;
+
+    // This scale factor adjusts the arc length to handle
+    // the arc to segment approximation.
+    // because we use SEGM_COUNT_PER_360DEG segment to approximate a circle,
+    // the trace len must be corrected when calculated using arcs
+    // this factor adjust calculations and must be changed if SEGM_COUNT_PER_360DEG is modified
+    // because trace using segment is shorter the corresponding arc
+    // ADJUST_SIZE is the ratio between tline len and the arc len for an arc
+    // of 360/ADJUST_SIZE angle
+    #define ADJUST_SIZE 0.988
+
+    auto    pt       = aEndPoint - aStartPoint;
+    double  angle    = -ArcTangente( pt.y, pt.x );
+    int     min_len  = KiROUND( EuclideanNorm( pt ) );
+    int     segm_len = 0;           // length of segments
+    int     full_len;               // full len of shape (sum of length of all segments + arcs)
+
+
+    /* Note: calculations are made for a vertical coil (more easy calculations)
+     * and after points are rotated to their actual position
+     * So the main direction is the Y axis.
+     * the 2 stubs are on the Y axis
+     * the others segments are parallel to the X axis.
+     */
+
+    // Calculate the size of area (for a vertical shape)
+    size.x = min_len / 2;
+    size.y = min_len;
+
+    // Choose a reasonable starting value for the radius of the arcs.
+    int radius = std::min( aWidth * 5, size.x / 4 );
+
+    int segm_count;     // number of full len segments
+                        // the half size segments (first and last segment) are not counted here
+    int stubs_len = 0;  // length of first or last segment (half size of others segments)
+
+    for( segm_count = 0; ; segm_count++ )
+    {
+        stubs_len = ( size.y - ( radius * 2 * (segm_count + 2 ) ) ) / 2;
+
+        if( stubs_len < size.y / 10 ) // Reduce radius.
+        {
+            stubs_len = size.y / 10;
+            radius    = ( size.y - (2 * stubs_len) ) / ( 2 * (segm_count + 2) );
+
+            if( radius < aWidth ) // Radius too small.
+            {
+                // Unable to create line: Requested length value is too large for room
+                return 0;
+            }
+        }
+
+        segm_len  = size.x - ( radius * 2 );
+        full_len  = 2 * stubs_len;               // Length of coil connections.
+        full_len += segm_len * segm_count;       // Length of full length segments.
+        full_len += KiROUND( ( segm_count + 2 ) * M_PI * ADJUST_SIZE * radius );    // Ard arcs len
+        full_len += segm_len - (2 * radius);     // Length of first and last segments
+                                                 // (half size segments len = segm_len/2 - radius).
+
+        if( full_len >= aLength )
+            break;
+    }
+
+    // Adjust len by adjusting segm_len:
+    int delta_size = full_len - aLength;
+
+    // reduce len of the segm_count segments + 2 half size segments (= 1 full size segment)
+    segm_len -= delta_size / (segm_count + 1);
+
+    // Generate first line (the first stub) and first arc (90 deg arc)
+    pt = aStartPoint;
+    aBuffer.push_back( pt );
+    pt.y += stubs_len;
+    aBuffer.push_back( pt );
+
+    auto centre = pt;
+    centre.x -= radius;
+    gen_arc( aBuffer, pt, centre, -900 );
+    pt = aBuffer.back();
+
+    int half_size_seg_len = segm_len / 2 - radius;
+
+    if( half_size_seg_len )
+    {
+        pt.x -= half_size_seg_len;
+        aBuffer.push_back( pt );
+    }
+
+    // Create shape.
+    int ii;
+    int sign = 1;
+    segm_count += 1;    // increase segm_count to create the last half_size segment
+
+    for( ii = 0; ii < segm_count; ii++ )
+    {
+        int arc_angle;
+
+        if( ii & 1 ) // odd order arcs are greater than 0
+            sign = -1;
+        else
+            sign = 1;
+
+        arc_angle = 1800 * sign;
+        centre    = pt;
+        centre.y += radius;
+        gen_arc( aBuffer, pt, centre, arc_angle );
+        pt    = aBuffer.back();
+        pt.x += segm_len * sign;
+        aBuffer.push_back( pt );
+    }
+
+    // The last point is false:
+    // it is the end of a full size segment, but must be
+    // the end of the second half_size segment. Change it.
+    sign *= -1;
+    aBuffer.back().x = aStartPoint.x + radius * sign;
+
+    // create last arc
+    pt        = aBuffer.back();
+    centre    = pt;
+    centre.y += radius;
+    gen_arc( aBuffer, pt, centre, 900 * sign );
+    aBuffer.back();
+
+    // Rotate point
+    angle += 900;
+
+    for( unsigned jj = 0; jj < aBuffer.size(); jj++ )
+    {
+        RotatePoint( &aBuffer[jj].x, &aBuffer[jj].y, aStartPoint.x, aStartPoint.y, angle );
+    }
+
+    // push last point (end point)
+    aBuffer.push_back( aEndPoint );
+
+    return 1;
+}
+
+
+MODULE* MWAVE::CreateMicrowaveInductor( INDUCTOR_PATTERN& inductorPattern,
+                    PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage )
+{
+    /* Build a microwave inductor footprint.
+     * - Length Mself.lng
+     * - Extremities Mself.m_Start and Mself.m_End
+     * We must determine:
+     * Mself.nbrin = number of segments perpendicular to the direction
+     * (The coil nbrin will demicercles + 1 + 2 1 / 4 circle)
+     * Mself.lbrin = length of a strand
+     * Mself.radius = radius of rounded parts of the coil
+     * Mself.delta = segments extremities connection between him and the coil even
+     *
+     * The equations are
+     * Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin
+     * Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius
+     * Mself.lng = 2 * Mself.delta / / connections to the coil
+     + (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last
+     + (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded
+     * Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit
+     *
+     * The constraints are:
+     * Nbrin >= 2
+     * Mself.radius < Mself.m_Size.x
+     * Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord
+     * Mself.lbrin> Mself.radius * 2
+     *
+     * The calculation is conducted in the following way:
+     * Initially:
+     * Nbrin = 2
+     * Radius = 4 * m_Size.x (arbitrarily fixed value)
+     * Then:
+     * Increasing the number of segments to the desired length
+     * (Radius decreases if necessary)
+     */
+
+    D_PAD*   pad;
+    int      ll;
+    wxString msg;
+
+    auto pt = inductorPattern.m_End - inductorPattern.m_Start;
+    int     min_len = KiROUND( EuclideanNorm( pt ) );
+    inductorPattern.m_length = min_len;
+
+    // Enter the desired length.
+    msg = StringFromValue( g_UserUnit, inductorPattern.m_length );
+    wxTextEntryDialog dlg( NULL, wxEmptyString, _( "Length of Trace:" ), msg );
+
+    if( dlg.ShowModal() != wxID_OK )
+        return NULL; // canceled by user
+
+    msg = dlg.GetValue();
+    inductorPattern.m_length = ValueFromString( g_UserUnit, msg );
+
+    // Control values (ii = minimum length)
+    if( inductorPattern.m_length < min_len )
+    {
+        aErrorMessage = _( "Requested length < minimum length" );
+        return NULL;
+    }
+
+    // Calculate the elements.
+    std::vector <wxPoint> buffer;
+    ll = BuildCornersList_S_Shape( buffer, inductorPattern.m_Start,
+                                   inductorPattern.m_End, inductorPattern.m_length,
+                                   inductorPattern.m_Width );
+
+    if( !ll )
+    {
+        aErrorMessage = _( "Requested length too large" );
+        return NULL;
+    }
+
+    // Generate footprint. the value is also used as footprint name.
+    msg.Empty();
+    wxTextEntryDialog cmpdlg( NULL, wxEmptyString, _( "Component Value:" ), msg );
+    cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );
+
+    if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
+        return NULL;    //  Aborted by user
+
+    MODULE* module = aPcbFrame->CreateNewModule( msg );
+
+    // here the module is already in the BOARD, CreateNewModule() does that.
+    module->SetFPID( LIB_ID( std::string( "mw_inductor" ) ) );
+    module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
+    module->ClearFlags();
+    module->SetPosition( inductorPattern.m_End );
+
+    // Generate segments
+    for( unsigned jj = 1; jj < buffer.size(); jj++ )
+    {
+        EDGE_MODULE* PtSegm;
+        PtSegm = new EDGE_MODULE( module );
+        PtSegm->SetStart( buffer[jj - 1] );
+        PtSegm->SetEnd( buffer[jj] );
+        PtSegm->SetWidth( inductorPattern.m_Width );
+        PtSegm->SetLayer( module->GetLayer() );
+        PtSegm->SetShape( S_SEGMENT );
+        PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
+        PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() );
+        module->GraphicalItems().PushBack( PtSegm );
+    }
+
+    // Place a pad on each end of coil.
+    pad = new D_PAD( module );
+
+    module->Pads().PushFront( pad );
+
+    pad->SetPadName( "1" );
+    pad->SetPosition( inductorPattern.m_End );
+    pad->SetPos0( pad->GetPosition() - module->GetPosition() );
+
+    pad->SetSize( wxSize( inductorPattern.m_Width, inductorPattern.m_Width ) );
+
+    pad->SetLayerSet( LSET( module->GetLayer() ) );
+    pad->SetAttribute( PAD_ATTRIB_SMD );
+    pad->SetShape( PAD_SHAPE_CIRCLE );
+
+    D_PAD* newpad = new D_PAD( *pad );
+
+    module->Pads().Insert( newpad, pad->Next() );
+
+    pad = newpad;
+    pad->SetPadName( "2" );
+    pad->SetPosition( inductorPattern.m_Start );
+    pad->SetPos0( pad->GetPosition() - module->GetPosition() );
+
+    // Modify text positions.
+    wxPoint refPos( ( inductorPattern.m_Start.x + inductorPattern.m_End.x ) / 2,
+                    ( inductorPattern.m_Start.y + inductorPattern.m_End.y ) / 2 );
+
+    wxPoint valPos = refPos;
+
+    refPos.y -= module->Reference().GetTextSize().y;
+    module->Reference().SetPosition( refPos );
+    valPos.y += module->Value().GetTextSize().y;
+    module->Value().SetPosition( valPos );
+
+    module->CalculateBoundingBox();
+    return module;
+}
diff --git a/pcbnew/microwave/microwave_inductor.h b/pcbnew/microwave/microwave_inductor.h
new file mode 100644
index 000000000..195019237
--- /dev/null
+++ b/pcbnew/microwave/microwave_inductor.h
@@ -0,0 +1,55 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef MICROWAVE_INDUCTOR__H_
+#define MICROWAVE_INDUCTOR__H_
+
+#include <wx/wx.h> // for wxPoint
+
+class MODULE;
+class PCB_EDIT_FRAME;
+
+namespace MWAVE
+{
+
+/**
+ * Parameters for construction of a microwave inductor
+ */
+struct INDUCTOR_PATTERN
+{
+public:
+    wxPoint m_Start;
+    wxPoint m_End;
+    int     m_length;       // full length trace.
+    int     m_Width;        // Trace width.
+};
+
+/**
+ * Creates an S-shaped coil footprint for microwave applications.
+ */
+MODULE* CreateMicrowaveInductor( INDUCTOR_PATTERN& aPattern,
+        PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage );
+
+}
+
+#endif // MICROWAVE_INDUCTOR__H_
diff --git a/pcbnew/muonde.cpp b/pcbnew/muonde.cpp
index ef86a00ca..d49014d93 100644
--- a/pcbnew/muonde.cpp
+++ b/pcbnew/muonde.cpp
@@ -42,12 +42,14 @@
 #include <gr_basic.h>
 #include <macros.h>
 #include <base_units.h>
-#include <validators.h>
+#include <validators.h> //
 
 #include <class_board.h>
 #include <class_module.h>
 #include <class_edge_mod.h>
 
+#include <microwave/microwave_inductor.h>
+
 #include <pcbnew.h>
 
 static std::vector< wxRealPoint > PolyEdges;
@@ -57,10 +59,6 @@ static int     PolyShapeType;
 
 
 static void Exit_Self( EDA_DRAW_PANEL* aPanel, wxDC* aDC );
-static void gen_arc( std::vector <wxPoint>& aBuffer,
-                     wxPoint                aStartPoint,
-                     wxPoint                aCenter,
-                     int                    a_ArcAngle );
 
 static void ShowBoundingBoxMicroWaveInductor( EDA_DRAW_PANEL* aPanel,
                                               wxDC*           aDC,
@@ -68,30 +66,11 @@ static void ShowBoundingBoxMicroWaveInductor( EDA_DRAW_PANEL* aPanel,
                                               bool            aErase );
 
 
-static int  BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer,
-                                      wxPoint aStartPoint, wxPoint aEndPoint,
-                                      int aLength, int aWidth );
-
-/**
- * Creates a self-shaped coil for microwave applications.
- */
-static MODULE* CreateMicrowaveInductor( PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage );
-
+///> An inductor pattern temporarily used during mu-wave inductor creation
+static MWAVE::INDUCTOR_PATTERN s_inductor_pattern;
 
-class MUWAVE_INDUCTOR
-{
-public:
-    wxPoint m_Start;
-    wxPoint m_End;
-    wxSize  m_Size;
-    int     m_length;       // full length trace.
-    int     m_Width;        // Trace width.
-    // A flag set to true when mu-wave inductor is being created
-    bool    m_Flag;
-};
-
-// An instance of MUWAVE_INDUCTOR temporary used during mu-wave inductor creation
-static MUWAVE_INDUCTOR s_inductor_pattern;
+///> A flag set to true when mu-wave inductor is being created
+static bool s_inductorInProgress = false;
 
 
 /* This function shows on screen the bounding box of the inductor that will be
@@ -152,14 +131,14 @@ void Exit_Self( EDA_DRAW_PANEL* aPanel, wxDC* aDC )
     if( aPanel->IsMouseCaptured() )
         aPanel->CallMouseCapture( aDC, wxDefaultPosition, false );
 
-    s_inductor_pattern.m_Flag = false;
+    s_inductorInProgress = false;
     aPanel->SetMouseCapture( NULL, NULL );
 }
 
 
 void PCB_EDIT_FRAME::Begin_Self( wxDC* DC )
 {
-    if( s_inductor_pattern.m_Flag )
+    if( s_inductorInProgress )
     {
         m_canvas->CallMouseCapture( DC, wxDefaultPosition, false );
         m_canvas->SetMouseCapture( NULL, NULL );
@@ -172,7 +151,10 @@ void PCB_EDIT_FRAME::Begin_Self( wxDC* DC )
         s_inductor_pattern.m_Width = GetDesignSettings().GetCurrentTrackWidth();
         s_inductor_pattern.m_End = GetCrossHairPosition();
 
-        MODULE* footprint = CreateMicrowaveInductor( this, errorMessage );
+        wxASSERT( s_inductorInProgress );
+        s_inductorInProgress = false;
+
+        MODULE* footprint = MWAVE::CreateMicrowaveInductor( s_inductor_pattern, this, errorMessage );
 
         if( footprint )
         {
@@ -189,7 +171,7 @@ void PCB_EDIT_FRAME::Begin_Self( wxDC* DC )
     s_inductor_pattern.m_Start = GetCrossHairPosition();
     s_inductor_pattern.m_End   = s_inductor_pattern.m_Start;
 
-    s_inductor_pattern.m_Flag = true;
+    s_inductorInProgress = true;
 
     // Update the initial coordinates.
     GetScreen()->m_O_Curseur = GetCrossHairPosition();
@@ -200,378 +182,6 @@ void PCB_EDIT_FRAME::Begin_Self( wxDC* DC )
 }
 
 
-
-MODULE* CreateMicrowaveInductor( PCB_EDIT_FRAME* aPcbFrame, wxString& aErrorMessage )
-{
-    /* Build a microwave inductor footprint.
-     * - Length Mself.lng
-     * - Extremities Mself.m_Start and Mself.m_End
-     * We must determine:
-     * Mself.nbrin = number of segments perpendicular to the direction
-     * (The coil nbrin will demicercles + 1 + 2 1 / 4 circle)
-     * Mself.lbrin = length of a strand
-     * Mself.radius = radius of rounded parts of the coil
-     * Mself.delta = segments extremities connection between him and the coil even
-     *
-     * The equations are
-     * Mself.m_Size.x = 2 * Mself.radius + Mself.lbrin
-     * Mself.m_Size.y * Mself.delta = 2 + 2 * Mself.nbrin * Mself.radius
-     * Mself.lng = 2 * Mself.delta / / connections to the coil
-     + (Mself.nbrin-2) * Mself.lbrin / / length of the strands except 1st and last
-     + (Mself.nbrin 1) * (PI * Mself.radius) / / length of rounded
-     * Mself.lbrin + / 2 - Melf.radius * 2) / / length of 1st and last bit
-     *
-     * The constraints are:
-     * Nbrin >= 2
-     * Mself.radius < Mself.m_Size.x
-     * Mself.m_Size.y = Mself.radius * 4 + 2 * Mself.raccord
-     * Mself.lbrin> Mself.radius * 2
-     *
-     * The calculation is conducted in the following way:
-     * Initially:
-     * Nbrin = 2
-     * Radius = 4 * m_Size.x (arbitrarily fixed value)
-     * Then:
-     * Increasing the number of segments to the desired length
-     * (Radius decreases if necessary)
-     */
-
-    D_PAD*   pad;
-    int      ll;
-    wxString msg;
-
-    wxASSERT( s_inductor_pattern.m_Flag );
-
-    s_inductor_pattern.m_Flag = false;
-
-    wxPoint pt = s_inductor_pattern.m_End - s_inductor_pattern.m_Start;
-    int     min_len = KiROUND( EuclideanNorm( pt ) );
-    s_inductor_pattern.m_length = min_len;
-
-    // Enter the desired length.
-    msg = StringFromValue( g_UserUnit, s_inductor_pattern.m_length );
-    wxTextEntryDialog dlg( NULL, wxEmptyString, _( "Length of Trace:" ), msg );
-
-    if( dlg.ShowModal() != wxID_OK )
-        return NULL; // canceled by user
-
-    msg = dlg.GetValue();
-    s_inductor_pattern.m_length = ValueFromString( g_UserUnit, msg );
-
-    // Control values (ii = minimum length)
-    if( s_inductor_pattern.m_length < min_len )
-    {
-        aErrorMessage = _( "Requested length < minimum length" );
-        return NULL;
-    }
-
-    // Calculate the elements.
-    std::vector <wxPoint> buffer;
-    ll = BuildCornersList_S_Shape( buffer, s_inductor_pattern.m_Start,
-                                   s_inductor_pattern.m_End, s_inductor_pattern.m_length,
-                                   s_inductor_pattern.m_Width );
-
-    if( !ll )
-    {
-        aErrorMessage = _( "Requested length too large" );
-        return NULL;
-    }
-
-    // Generate footprint. the value is also used as footprint name.
-    msg.Empty();
-    wxTextEntryDialog cmpdlg( NULL, wxEmptyString, _( "Component Value:" ), msg );
-    cmpdlg.SetTextValidator( FILE_NAME_CHAR_VALIDATOR( &msg ) );
-
-    if( ( cmpdlg.ShowModal() != wxID_OK ) || msg.IsEmpty() )
-        return NULL;    //  Aborted by user
-
-    MODULE* module = aPcbFrame->CreateNewModule( msg );
-
-    // here the module is already in the BOARD, CreateNewModule() does that.
-    module->SetFPID( LIB_ID( std::string( "mw_inductor" ) ) );
-    module->SetAttributes( MOD_VIRTUAL | MOD_CMS );
-    module->ClearFlags();
-    module->SetPosition( s_inductor_pattern.m_End );
-
-    // Generate segments
-    for( unsigned jj = 1; jj < buffer.size(); jj++ )
-    {
-        EDGE_MODULE* PtSegm;
-        PtSegm = new EDGE_MODULE( module );
-        PtSegm->SetStart( buffer[jj - 1] );
-        PtSegm->SetEnd( buffer[jj] );
-        PtSegm->SetWidth( s_inductor_pattern.m_Width );
-        PtSegm->SetLayer( module->GetLayer() );
-        PtSegm->SetShape( S_SEGMENT );
-        PtSegm->SetStart0( PtSegm->GetStart() - module->GetPosition() );
-        PtSegm->SetEnd0( PtSegm->GetEnd() - module->GetPosition() );
-        module->GraphicalItems().PushBack( PtSegm );
-    }
-
-    // Place a pad on each end of coil.
-    pad = new D_PAD( module );
-
-    module->Pads().PushFront( pad );
-
-    pad->SetPadName( wxT( "1" ) );
-    pad->SetPosition( s_inductor_pattern.m_End );
-    pad->SetPos0( pad->GetPosition() - module->GetPosition() );
-
-    pad->SetSize( wxSize( s_inductor_pattern.m_Width, s_inductor_pattern.m_Width ) );
-
-    pad->SetLayerSet( LSET( module->GetLayer() ) );
-    pad->SetAttribute( PAD_ATTRIB_SMD );
-    pad->SetShape( PAD_SHAPE_CIRCLE );
-
-    D_PAD* newpad = new D_PAD( *pad );
-
-    module->Pads().Insert( newpad, pad->Next() );
-
-    pad = newpad;
-    pad->SetPadName( wxT( "2" ) );
-    pad->SetPosition( s_inductor_pattern.m_Start );
-    pad->SetPos0( pad->GetPosition() - module->GetPosition() );
-
-    // Modify text positions.
-    wxPoint refPos( ( s_inductor_pattern.m_Start.x + s_inductor_pattern.m_End.x ) / 2,
-                    ( s_inductor_pattern.m_Start.y + s_inductor_pattern.m_End.y ) / 2 );
-
-    wxPoint valPos = refPos;
-
-    refPos.y -= module->Reference().GetTextSize().y;
-    module->Reference().SetPosition( refPos );
-    valPos.y += module->Value().GetTextSize().y;
-    module->Value().SetPosition( valPos );
-
-    module->CalculateBoundingBox();
-    return module;
-}
-
-
-/**
- * Function  gen_arc
- * generates an arc using arc approximation by lines:
- * Center aCenter
- * Angle "angle" (in 0.1 deg)
- * @param  aBuffer = a buffer to store points.
- * @param  aStartPoint = starting point of arc.
- * @param  aCenter = arc centre.
- * @param  a_ArcAngle = arc length in 0.1 degrees.
- */
-static void gen_arc( std::vector <wxPoint>& aBuffer,
-                     wxPoint                aStartPoint,
-                     wxPoint                aCenter,
-                     int                    a_ArcAngle )
-{
-    #define SEGM_COUNT_PER_360DEG 16
-    wxPoint first_point = aStartPoint - aCenter;
-    int     seg_count   = ( ( abs( a_ArcAngle ) ) * SEGM_COUNT_PER_360DEG ) / 3600;
-
-    if( seg_count == 0 )
-        seg_count = 1;
-
-    double increment_angle = (double) a_ArcAngle * M_PI / 1800 / seg_count;
-
-    // Creates nb_seg point to approximate arc by segments:
-    for( int ii = 1; ii <= seg_count; ii++ )
-    {
-        double  rot_angle = increment_angle * ii;
-        double  fcos = cos( rot_angle );
-        double  fsin = sin( rot_angle );
-        wxPoint currpt;
-
-        // Rotate current point:
-        currpt.x = KiROUND( ( first_point.x * fcos + first_point.y * fsin ) );
-        currpt.y = KiROUND( ( first_point.y * fcos - first_point.x * fsin ) );
-
-        wxPoint corner = aCenter + currpt;
-        aBuffer.push_back( corner );
-    }
-}
-
-
-/**
- * Function BuildCornersList_S_Shape
- * Create a path like a S-shaped coil
- * @param  aBuffer =  a buffer where to store points (ends of segments)
- * @param  aStartPoint = starting point of the path
- * @param  aEndPoint = ending point of the path
- * @param  aLength = full length of the path
- * @param  aWidth = segment width
- */
-int BuildCornersList_S_Shape( std::vector <wxPoint>& aBuffer,
-                              wxPoint aStartPoint, wxPoint aEndPoint,
-                              int aLength, int aWidth )
-{
-/* We must determine:
- * segm_count = number of segments perpendicular to the direction
- * segm_len = length of a strand
- * radius = radius of rounded parts of the coil
- * stubs_len = length of the 2 stubs( segments parallel to the direction)
- *         connecting the start point to the start point of the S shape
- *         and the ending point to the end point of the S shape
- * The equations are (assuming the area size of the entire shape is Size:
- * Size.x = 2 * radius + segm_len
- * Size.y = (segm_count + 2 ) * 2 * radius + 2 * stubs_len
- * s_inductor_pattern.m_length = 2 * delta // connections to the coil
- *             + (segm_count-2) * segm_len      // length of the strands except 1st and last
- *             + (segm_count) * (PI * radius)   // length of rounded
- * segm_len + / 2 - radius * 2)                 // length of 1st and last bit
- *
- * The constraints are:
- * segm_count >= 2
- * radius < m_Size.x
- * Size.y = (radius * 4) + (2 * stubs_len)
- * segm_len > radius * 2
- *
- * The calculation is conducted in the following way:
- * first:
- * segm_count = 2
- * radius = 4 * Size.x (arbitrarily fixed value)
- * Then:
- * Increasing the number of segments to the desired length
- * (radius decreases if necessary)
- */
-    wxSize size;
-
-    // This scale factor adjusts the arc length to handle
-    // the arc to segment approximation.
-    // because we use SEGM_COUNT_PER_360DEG segment to approximate a circle,
-    // the trace len must be corrected when calculated using arcs
-    // this factor adjust calculations and must be changed if SEGM_COUNT_PER_360DEG is modified
-    // because trace using segment is shorter the corresponding arc
-    // ADJUST_SIZE is the ratio between tline len and the arc len for an arc
-    // of 360/ADJUST_SIZE angle
-    #define ADJUST_SIZE 0.988
-
-    wxPoint pt       = aEndPoint - aStartPoint;
-    double  angle    = -ArcTangente( pt.y, pt.x );
-    int     min_len  = KiROUND( EuclideanNorm( pt ) );
-    int     segm_len = 0;           // length of segments
-    int     full_len;               // full len of shape (sum of length of all segments + arcs)
-
-
-    /* Note: calculations are made for a vertical coil (more easy calculations)
-     * and after points are rotated to their actual position
-     * So the main direction is the Y axis.
-     * the 2 stubs are on the Y axis
-     * the others segments are parallel to the X axis.
-     */
-
-    // Calculate the size of area (for a vertical shape)
-    size.x = min_len / 2;
-    size.y = min_len;
-
-    // Choose a reasonable starting value for the radius of the arcs.
-    int radius = std::min( aWidth * 5, size.x / 4 );
-
-    int segm_count;     // number of full len segments
-                        // the half size segments (first and last segment) are not counted here
-    int stubs_len = 0;  // length of first or last segment (half size of others segments)
-
-    for( segm_count = 0; ; segm_count++ )
-    {
-        stubs_len = ( size.y - ( radius * 2 * (segm_count + 2 ) ) ) / 2;
-
-        if( stubs_len < size.y / 10 ) // Reduce radius.
-        {
-            stubs_len = size.y / 10;
-            radius    = ( size.y - (2 * stubs_len) ) / ( 2 * (segm_count + 2) );
-
-            if( radius < aWidth ) // Radius too small.
-            {
-                // Unable to create line: Requested length value is too large for room
-                return 0;
-            }
-        }
-
-        segm_len  = size.x - ( radius * 2 );
-        full_len  = 2 * stubs_len;               // Length of coil connections.
-        full_len += segm_len * segm_count;       // Length of full length segments.
-        full_len += KiROUND( ( segm_count + 2 ) * M_PI * ADJUST_SIZE * radius );    // Ard arcs len
-        full_len += segm_len - (2 * radius);     // Length of first and last segments
-                                                 // (half size segments len = segm_len/2 - radius).
-
-        if( full_len >= aLength )
-            break;
-    }
-
-    // Adjust len by adjusting segm_len:
-    int delta_size = full_len - aLength;
-
-    // reduce len of the segm_count segments + 2 half size segments (= 1 full size segment)
-    segm_len -= delta_size / (segm_count + 1);
-
-    // Generate first line (the first stub) and first arc (90 deg arc)
-    pt = aStartPoint;
-    aBuffer.push_back( pt );
-    pt.y += stubs_len;
-    aBuffer.push_back( pt );
-
-    wxPoint centre = pt;
-    centre.x -= radius;
-    gen_arc( aBuffer, pt, centre, -900 );
-    pt = aBuffer.back();
-
-    int half_size_seg_len = segm_len / 2 - radius;
-
-    if( half_size_seg_len )
-    {
-        pt.x -= half_size_seg_len;
-        aBuffer.push_back( pt );
-    }
-
-    // Create shape.
-    int ii;
-    int sign = 1;
-    segm_count += 1;    // increase segm_count to create the last half_size segment
-
-    for( ii = 0; ii < segm_count; ii++ )
-    {
-        int arc_angle;
-
-        if( ii & 1 ) // odd order arcs are greater than 0
-            sign = -1;
-        else
-            sign = 1;
-
-        arc_angle = 1800 * sign;
-        centre    = pt;
-        centre.y += radius;
-        gen_arc( aBuffer, pt, centre, arc_angle );
-        pt    = aBuffer.back();
-        pt.x += segm_len * sign;
-        aBuffer.push_back( pt );
-    }
-
-    // The last point is false:
-    // it is the end of a full size segment, but must be
-    // the end of the second half_size segment. Change it.
-    sign *= -1;
-    aBuffer.back().x = aStartPoint.x + radius * sign;
-
-    // create last arc
-    pt        = aBuffer.back();
-    centre    = pt;
-    centre.y += radius;
-    gen_arc( aBuffer, pt, centre, 900 * sign );
-    aBuffer.back();
-
-    // Rotate point
-    angle += 900;
-
-    for( unsigned jj = 0; jj < aBuffer.size(); jj++ )
-    {
-        RotatePoint( &aBuffer[jj].x, &aBuffer[jj].y, aStartPoint.x, aStartPoint.y, angle );
-    }
-
-    // push last point (end point)
-    aBuffer.push_back( aEndPoint );
-
-    return 1;
-}
-
-
 MODULE* PCB_EDIT_FRAME::CreateMuWaveBaseFootprint( const wxString& aValue,
                                                    int aTextSize, int aPadCount )
 {
-- 
2.12.0


Follow ups