diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 5935f720fd..7b77a24149 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -272,6 +272,7 @@ set( COMMON_SRCS ${COMMON_PREVIEW_ITEMS_SRCS} ${PLOTTERS_CONTROL_SRCS} advanced_config.cpp + array_options.cpp base_struct.cpp bezier_curves.cpp bin_mod.cpp diff --git a/common/array_options.cpp b/common/array_options.cpp new file mode 100644 index 0000000000..8a2faa4e84 --- /dev/null +++ b/common/array_options.cpp @@ -0,0 +1,213 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 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 + +#include + +const wxString& ARRAY_OPTIONS::AlphabetFromNumberingScheme( NUMBERING_TYPE_T type ) +{ + static const wxString alphaNumeric = "0123456789"; + static const wxString alphaHex = "0123456789ABCDEF"; + static const wxString alphaFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY"; + + switch( type ) + { + default: + case NUMBERING_NUMERIC: return alphaNumeric; + case NUMBERING_HEX: return alphaHex; + case NUMBERING_ALPHA_NO_IOSQXZ: return alphaNoIOSQXZ; + case NUMBERING_ALPHA_FULL: return alphaFull; + } +} + + +bool ARRAY_OPTIONS::SchemeNonUnitColsStartAt0( NUMBERING_TYPE_T type ) +{ + return type == NUMBERING_ALPHA_FULL || type == NUMBERING_ALPHA_NO_IOSQXZ; +} + + +bool ARRAY_OPTIONS::GetNumberingOffset( + const wxString& str, ARRAY_OPTIONS::NUMBERING_TYPE_T type, int& offsetToFill ) +{ + const wxString& alphabet = ARRAY_OPTIONS::AlphabetFromNumberingScheme( type ); + + int offset = 0; + const int radix = alphabet.length(); + + for( unsigned i = 0; i < str.length(); i++ ) + { + int chIndex = alphabet.Find( str[i], false ); + + if( chIndex == wxNOT_FOUND ) + return false; + + const bool start0 = ARRAY_OPTIONS::SchemeNonUnitColsStartAt0( type ); + + // eg "AA" is actually index 27, not 26 + if( start0 && i < str.length() - 1 ) + chIndex++; + + offset *= radix; + offset += chIndex; + } + + offsetToFill = offset; + return true; +} + + +wxString ARRAY_OPTIONS::getCoordinateNumber( int n, NUMBERING_TYPE_T type ) +{ + wxString itemNum; + const wxString& alphabet = AlphabetFromNumberingScheme( type ); + + const bool nonUnitColsStartAt0 = SchemeNonUnitColsStartAt0( type ); + + bool firstRound = true; + int radix = alphabet.Length(); + + do + { + int modN = n % radix; + + if( nonUnitColsStartAt0 && !firstRound ) + modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx" + + itemNum.insert( 0, 1, alphabet[modN] ); + + n /= radix; + firstRound = false; + } while( n ); + + return itemNum; +} + + +int ARRAY_GRID_OPTIONS::GetArraySize() const +{ + return m_nx * m_ny; +} + + +VECTOR2I ARRAY_GRID_OPTIONS::getGridCoords( int n ) const +{ + const int axisSize = m_horizontalThenVertical ? m_nx : m_ny; + + int x = n % axisSize; + int y = n / axisSize; + + // reverse on this row/col? + if( m_reverseNumberingAlternate && ( y % 2 ) ) + x = axisSize - x - 1; + + wxPoint coords( x, y ); + + return coords; +} + + +ARRAY_OPTIONS::TRANSFORM ARRAY_GRID_OPTIONS::GetTransform( int n, const VECTOR2I& aPos ) const +{ + VECTOR2I point; + + VECTOR2I coords = getGridCoords( n ); + + // swap axes if needed + if( !m_horizontalThenVertical ) + std::swap( coords.x, coords.y ); + + point.x = coords.x * m_delta.x + coords.y * m_offset.x; + point.y = coords.y * m_delta.y + coords.x * m_offset.y; + + if( std::abs( m_stagger ) > 1 ) + { + const int stagger = std::abs( m_stagger ); + const bool sr = m_stagger_rows; + const int stagger_idx = ( ( sr ? coords.y : coords.x ) % stagger ); + + VECTOR2I stagger_delta( ( sr ? m_delta.x : m_offset.x ), ( sr ? m_offset.y : m_delta.y ) ); + + // Stagger to the left/up if the sign of the stagger is negative + point += stagger_delta * copysign( stagger_idx, m_stagger ) / stagger; + } + + // this is already relative to the first array entry + return { point, 0.0 }; +} + + +wxString ARRAY_GRID_OPTIONS::GetItemNumber( int n ) const +{ + wxString itemNum; + + if( m_2dArrayNumbering ) + { + VECTOR2I coords = getGridCoords( n ); + + itemNum += getCoordinateNumber( coords.x + m_numberingOffsetX, m_priAxisNumType ); + itemNum += getCoordinateNumber( coords.y + m_numberingOffsetY, m_secAxisNumType ); + } + else + { + itemNum += getCoordinateNumber( n + m_numberingOffsetX, m_priAxisNumType ); + } + + return itemNum; +} + + +int ARRAY_CIRCULAR_OPTIONS::GetArraySize() const +{ + return m_nPts; +} + + +ARRAY_OPTIONS::TRANSFORM ARRAY_CIRCULAR_OPTIONS::GetTransform( int n, const VECTOR2I& aPos ) const +{ + double angle; + + if( m_angle == 0 ) + // angle is zero, divide evenly into m_nPts + angle = 10 * 360.0 * n / double( m_nPts ); + else + // n'th step + angle = m_angle * n; + + VECTOR2I new_pos = aPos; + RotatePoint( new_pos, m_centre, angle ); + + // take off the rotation (but not the translation) if needed + if( !m_rotateItems ) + angle = 0; + + return { new_pos - aPos, angle / 10.0 }; +} + + +wxString ARRAY_CIRCULAR_OPTIONS::GetItemNumber( int aN ) const +{ + return getCoordinateNumber( aN + m_numberingOffset, m_numberingType ); +} diff --git a/include/array_options.h b/include/array_options.h new file mode 100644 index 0000000000..8be7d5ccf5 --- /dev/null +++ b/include/array_options.h @@ -0,0 +1,223 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 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 PCBNEW_ARRAY_OPTIONS__H +#define PCBNEW_ARRAY_OPTIONS__H + +#include + +/** + * Options that govern the setup of an "array" of multiple item. + * The base #ARRAY_OPTIONS do not encode a specific geometry or numbering + * method, this is done by derived classes. + */ +class ARRAY_OPTIONS +{ +public: + enum ARRAY_TYPE_T + { + ARRAY_GRID, ///< A grid (x*y) array + ARRAY_CIRCULAR, ///< A circular array + }; + + // NOTE: do not change order relative to charSetDescriptions + enum NUMBERING_TYPE_T + { + NUMBERING_NUMERIC = 0, ///< Arabic numerals: 0,1,2,3,4,5,6,7,8,9,10,11... + NUMBERING_HEX, + NUMBERING_ALPHA_NO_IOSQXZ, /*!< Alphabet, excluding IOSQXZ + * + * Per ASME Y14.35M-1997 sec. 5.2 (previously MIL-STD-100 sec. 406.5) + * as these can be confused with numerals and are often not used + * for pin numbering on BGAs, etc + */ + NUMBERING_ALPHA_FULL, ///< Full 26-character alphabet + }; + + ARRAY_OPTIONS( ARRAY_TYPE_T aType ) + : m_type( aType ), m_shouldNumber( false ), m_numberingStartIsSpecified( false ) + { + } + + virtual ~ARRAY_OPTIONS(){}; + + /** + * Get the alphabet for a particular numbering scheme. + * @param type the numbering scheme + * @return the alphabet (as a string) + */ + static const wxString& AlphabetFromNumberingScheme( NUMBERING_TYPE_T type ); + + /** + * @return False for schemes like 0,1...9,10 + * True for schemes like A,B..Z,AA (where the tens column starts with char 0) + */ + static bool SchemeNonUnitColsStartAt0( NUMBERING_TYPE_T type ); + + /** + * Get the numbering offset for a given numbering string + * @param str a numbering string, say "B" or "5" + * @param type the type this string should be + * @param offsetToFill the offset to set, if found + * @return true if the string is a valid offset of this type + */ + static bool GetNumberingOffset( + const wxString& str, ARRAY_OPTIONS::NUMBERING_TYPE_T type, int& offsetToFill ); + + /** + * Transform applied to an object by this array + */ + struct TRANSFORM + { + VECTOR2I m_offset; + double m_rotation; // in degrees + }; + + /** + * Get the transform of the n-th point in the array + * @param aN the index of the array point (0 is the original point) + * @param aPos the existing item position + * @return a transform (an offset and a rotation) + */ + virtual TRANSFORM GetTransform( int aN, const VECTOR2I& aPos ) const = 0; + + /** + * The number of points in this array + */ + virtual int GetArraySize() const = 0; + + /** + * Get the position number (name) for the n'th array point + * @param n array point index, from 0 to GetArraySize() - 1 + * @return the point's name + */ + virtual wxString GetItemNumber( int n ) const = 0; + + /*! + * @return are the items in this array numbered, or are all the + * items numbered the same? + */ + bool ShouldNumberItems() const + { + return m_shouldNumber; + } + + void SetShouldNumber( bool aShouldNumber ) + { + m_shouldNumber = aShouldNumber; + } + + /*! + * @return is the numbering is enabled and should start at a point + * specified in these options or is it implicit according to the calling + * code? + */ + bool GetNumberingStartIsSpecified() const + { + return m_shouldNumber && m_numberingStartIsSpecified; + } + + void SetNumberingStartIsSpecified( bool aIsSpecified ) + { + m_numberingStartIsSpecified = aIsSpecified; + } + +protected: + static wxString getCoordinateNumber( int n, NUMBERING_TYPE_T type ); + + ARRAY_TYPE_T m_type; + + /// True if this array numbers the new items + bool m_shouldNumber; + + /// True if this array's number starts from the preset point + /// False if the array numbering starts from some externally provided point + bool m_numberingStartIsSpecified; +}; + + +struct ARRAY_GRID_OPTIONS : public ARRAY_OPTIONS +{ + ARRAY_GRID_OPTIONS() + : ARRAY_OPTIONS( ARRAY_GRID ), + m_nx( 0 ), + m_ny( 0 ), + m_horizontalThenVertical( true ), + m_reverseNumberingAlternate( false ), + m_stagger( 0 ), + m_stagger_rows( true ), + m_2dArrayNumbering( false ), + m_numberingOffsetX( 0 ), + m_numberingOffsetY( 0 ), + m_priAxisNumType( NUMBERING_NUMERIC ), + m_secAxisNumType( NUMBERING_NUMERIC ) + { + } + + long m_nx, m_ny; + bool m_horizontalThenVertical, m_reverseNumberingAlternate; + VECTOR2I m_delta; + VECTOR2I m_offset; + long m_stagger; + bool m_stagger_rows; + bool m_2dArrayNumbering; + int m_numberingOffsetX, m_numberingOffsetY; + NUMBERING_TYPE_T m_priAxisNumType, m_secAxisNumType; + + TRANSFORM GetTransform( int aN, const VECTOR2I& aPos ) const override; + int GetArraySize() const override; + wxString GetItemNumber( int n ) const override; + +private: + VECTOR2I getGridCoords( int n ) const; +}; + + +struct ARRAY_CIRCULAR_OPTIONS : public ARRAY_OPTIONS +{ + ARRAY_CIRCULAR_OPTIONS() + : ARRAY_OPTIONS( ARRAY_CIRCULAR ), + m_nPts( 0 ), + m_angle( 0.0f ), + m_rotateItems( false ), + m_numberingType( NUMBERING_NUMERIC ), + m_numberingOffset( 0 ) + { + } + + /// number of point in the array + long m_nPts; + /// angle between points, or 0 for each point separated by this value (decideg) + double m_angle; + VECTOR2I m_centre; + bool m_rotateItems; + NUMBERING_TYPE_T m_numberingType; + long m_numberingOffset; + + TRANSFORM GetTransform( int aN, const VECTOR2I& aPos ) const override; + int GetArraySize() const override; + wxString GetItemNumber( int n ) const override; +}; + + +#endif // PCBNEW_ARRAY_OPTIONS__H \ No newline at end of file diff --git a/pcbnew/array_creator.cpp b/pcbnew/array_creator.cpp index 169a60e8b7..c56ad1ee91 100644 --- a/pcbnew/array_creator.cpp +++ b/pcbnew/array_creator.cpp @@ -34,6 +34,22 @@ #include +/** + * Transform a #BOARD_ITEM from the given #ARRAY_OPTIONS and an index into the array. + * + * @param aArrOpts The array options that describe the array + * @param aIndex The index in the array of this item + * @param aItem The item to transform + */ +static void TransformItem( const ARRAY_OPTIONS& aArrOpts, int aIndex, BOARD_ITEM& aItem ) +{ + const ARRAY_OPTIONS::TRANSFORM transform = aArrOpts.GetTransform( aIndex, aItem.GetPosition() ); + + aItem.Move( (wxPoint) transform.m_offset ); + aItem.Rotate( aItem.GetPosition(), transform.m_rotation * 10 ); +} + + void ARRAY_CREATOR::Invoke() { const int numItems = getNumberOfItemsToArray(); @@ -51,7 +67,7 @@ void ARRAY_CREATOR::Invoke() DIALOG_CREATE_ARRAY dialog( &m_parent, enableArrayNumbering, rotPoint ); int ret = dialog.ShowModal(); - DIALOG_CREATE_ARRAY::ARRAY_OPTIONS* const array_opts = dialog.GetArrayOptions(); + ARRAY_OPTIONS* const array_opts = dialog.GetArrayOptions(); if( ret != wxID_OK || array_opts == NULL ) return; @@ -94,7 +110,7 @@ void ARRAY_CREATOR::Invoke() if( new_item ) { - array_opts->TransformItem( ptN, new_item, rotPoint ); + TransformItem( *array_opts, ptN, *new_item ); prePushAction( new_item ); commit.Add( new_item ); postPushAction( new_item ); @@ -103,7 +119,7 @@ void ARRAY_CREATOR::Invoke() // attempt to renumber items if the array parameters define // a complete numbering scheme to number by (as opposed to // implicit numbering by incrementing the items during creation - if( new_item && array_opts->NumberingStartIsSpecified() ) + if( new_item && array_opts->GetNumberingStartIsSpecified() ) { // Renumber non-aperture pads. if( new_item->Type() == PCB_PAD_T ) diff --git a/pcbnew/dialogs/dialog_create_array.cpp b/pcbnew/dialogs/dialog_create_array.cpp index a93696fb2d..08dc1745d8 100644 --- a/pcbnew/dialogs/dialog_create_array.cpp +++ b/pcbnew/dialogs/dialog_create_array.cpp @@ -124,65 +124,6 @@ void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event ) } -static const wxString& alphabetFromNumberingScheme( DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T type ) -{ - static const wxString alphaNumeric = "0123456789"; - static const wxString alphaHex = "0123456789ABCDEF"; - static const wxString alphaFull = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - static const wxString alphaNoIOSQXZ = "ABCDEFGHJKLMNPRTUVWY"; - - switch( type ) - { - default: - case DIALOG_CREATE_ARRAY::NUMBERING_NUMERIC: return alphaNumeric; - case DIALOG_CREATE_ARRAY::NUMBERING_HEX: return alphaHex; - case DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_NO_IOSQXZ: return alphaNoIOSQXZ; - case DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_FULL: return alphaFull; - } -} - - -/** - * @return False for schemes like 0,1...9,10 - * True for schemes like A,B..Z,AA (where the tens column starts with char 0) - */ -static bool schemeNonUnitColsStartAt0( DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T type ) -{ - return type == DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_FULL - || type == DIALOG_CREATE_ARRAY::NUMBERING_ALPHA_NO_IOSQXZ; -} - - -static bool getNumberingOffset( const wxString& str, DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T type, - int& offsetToFill ) -{ - const wxString& alphabet = alphabetFromNumberingScheme( type ); - - int offset = 0; - const int radix = alphabet.length(); - - for( unsigned i = 0; i < str.length(); i++ ) - { - int chIndex = alphabet.Find( str[i], false ); - - if( chIndex == wxNOT_FOUND ) - return false; - - const bool start0 = schemeNonUnitColsStartAt0( type ); - - // eg "AA" is actually index 27, not 26 - if( start0 && i < str.length() - 1 ) - chIndex++; - - offset *= radix; - offset += chIndex; - } - - offsetToFill = offset; - return true; -} - - /** * Validates and saves (if valid) the type and offset of an array axis numbering * @@ -195,16 +136,16 @@ static bool getNumberingOffset( const wxString& str, DIALOG_CREATE_ARRAY::NUMBER */ static bool validateNumberingTypeAndOffset( const wxTextCtrl& offsetEntry, const wxChoice& typeEntry, - DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T& type, + ARRAY_OPTIONS::NUMBERING_TYPE_T& type, int& offset, wxArrayString& errors ) { const int typeVal = typeEntry.GetSelection(); // mind undefined casts to enums (should not be able to happen) - bool ok = typeVal <= DIALOG_CREATE_ARRAY::NUMBERING_TYPE_MAX; + bool ok = typeVal <= ARRAY_OPTIONS::NUMBERING_TYPE_MAX; if( ok ) { - type = (DIALOG_CREATE_ARRAY::NUMBERING_TYPE_T) typeVal; + type = (ARRAY_OPTIONS::NUMBERING_TYPE_T) typeVal; } else { @@ -216,11 +157,11 @@ static bool validateNumberingTypeAndOffset( const wxTextCtrl& offsetEntry, } const wxString text = offsetEntry.GetValue(); - ok = getNumberingOffset( text, type, offset ); + ok = ARRAY_OPTIONS::GetNumberingOffset( text, type, offset ); if( !ok ) { - const wxString& alphabet = alphabetFromNumberingScheme( type ); + const wxString& alphabet = ARRAY_OPTIONS::AlphabetFromNumberingScheme( type ); wxString err; err.Printf( _( "Could not determine numbering start from \"%s\": " @@ -287,7 +228,7 @@ bool DIALOG_CREATE_ARRAY::TransferDataFromWindow() newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0; newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue(); - newGrid->m_shouldNumber = m_numberingEnabled; + newGrid->SetShouldNumber( m_numberingEnabled ); if ( m_numberingEnabled ) { @@ -308,7 +249,7 @@ bool DIALOG_CREATE_ARRAY::TransferDataFromWindow() ok = ok && numOk; - newGrid->m_numberingStartIsSpecified = m_rbGridStartNumberingOpt->GetSelection() == 1; + newGrid->SetNumberingStartIsSpecified( m_rbGridStartNumberingOpt->GetSelection() == 1 ); } // Only use settings if all values are good @@ -329,12 +270,12 @@ bool DIALOG_CREATE_ARRAY::TransferDataFromWindow() ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors); newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue(); - newCirc->m_shouldNumber = m_numberingEnabled; + newCirc->SetShouldNumber( m_numberingEnabled ); if ( m_numberingEnabled ) { - newCirc->m_numberingStartIsSpecified = m_rbCircStartNumberingOpt->GetSelection() == 1; - newCirc->m_numberingType = NUMBERING_NUMERIC; + newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 ); + newCirc->m_numberingType = ARRAY_OPTIONS::NUMBERING_NUMERIC; ok = ok && validateLongEntry(*m_entryCircNumberingStart, newCirc->m_numberingOffset, _("numbering start"), errors); @@ -420,157 +361,10 @@ void DIALOG_CREATE_ARRAY::setControlEnablement() void DIALOG_CREATE_ARRAY::calculateCircularArrayProperties() { - wxPoint centre( m_hCentre.GetValue(), m_vCentre.GetValue() ); + VECTOR2I centre( m_hCentre.GetValue(), m_vCentre.GetValue() ); // Find the radius, etc of the circle centre -= m_originalItemPosition; - const double radius = VECTOR2I(centre.x, centre.y).EuclideanNorm(); - m_circRadius.SetValue( int( radius ) ); -} - - -// ARRAY OPTION implementation functions -------------------------------------- - -wxString DIALOG_CREATE_ARRAY::ARRAY_OPTIONS::getCoordinateNumber( int n, - NUMBERING_TYPE_T type ) -{ - wxString itemNum; - const wxString& alphabet = alphabetFromNumberingScheme( type ); - - const bool nonUnitColsStartAt0 = schemeNonUnitColsStartAt0( type ); - - bool firstRound = true; - int radix = alphabet.Length(); - - do { - int modN = n % radix; - - if( nonUnitColsStartAt0 && !firstRound ) - modN--; // Start the "tens/hundreds/etc column" at "Ax", not "Bx" - - itemNum.insert( 0, 1, alphabet[modN] ); - - n /= radix; - firstRound = false; - } while( n ); - - return itemNum; -} - - -wxString DIALOG_CREATE_ARRAY::ARRAY_OPTIONS::InterpolateNumberIntoString( - int aN, const wxString& aPattern ) const -{ - wxString newStr( aPattern ); - newStr.Replace( "%s", GetItemNumber( aN ), false ); - - return newStr; -} - - -int DIALOG_CREATE_ARRAY::ARRAY_GRID_OPTIONS::GetArraySize() const -{ - return m_nx * m_ny; -} - - -wxPoint DIALOG_CREATE_ARRAY::ARRAY_GRID_OPTIONS::getGridCoords( int n ) const -{ - const int axisSize = m_horizontalThenVertical ? m_nx : m_ny; - - int x = n % axisSize; - int y = n / axisSize; - - // reverse on this row/col? - if( m_reverseNumberingAlternate && ( y % 2 ) ) - x = axisSize - x - 1; - - wxPoint coords( x, y ); - - return coords; -} - - -void DIALOG_CREATE_ARRAY::ARRAY_GRID_OPTIONS::TransformItem( int n, BOARD_ITEM* item, - const wxPoint& rotPoint ) const -{ - wxPoint point; - - wxPoint coords = getGridCoords( n ); - - // swap axes if needed - if( !m_horizontalThenVertical ) - std::swap( coords.x, coords.y ); - - point.x = coords.x * m_delta.x + coords.y * m_offset.x; - point.y = coords.y * m_delta.y + coords.x * m_offset.y; - - if( std::abs( m_stagger ) > 1 ) - { - const int stagger = std::abs( m_stagger ); - const bool sr = m_stagger_rows; - const int stagger_idx = ( ( sr ? coords.y : coords.x ) % stagger ); - - wxPoint stagger_delta( ( sr ? m_delta.x : m_offset.x ), - ( sr ? m_offset.y : m_delta.y ) ); - - // Stagger to the left/up if the sign of the stagger is negative - point += stagger_delta * copysign( stagger_idx, m_stagger ) / stagger; - } - - // this is already relative to the first array entry - item->Move( point ); -} - - -wxString DIALOG_CREATE_ARRAY::ARRAY_GRID_OPTIONS::GetItemNumber( int n ) const -{ - wxString itemNum; - - if( m_2dArrayNumbering ) - { - wxPoint coords = getGridCoords( n ); - - itemNum += getCoordinateNumber( coords.x + m_numberingOffsetX, m_priAxisNumType ); - itemNum += getCoordinateNumber( coords.y + m_numberingOffsetY, m_secAxisNumType ); - } - else - { - itemNum += getCoordinateNumber( n + m_numberingOffsetX, m_priAxisNumType ); - } - - return itemNum; -} - - -int DIALOG_CREATE_ARRAY::ARRAY_CIRCULAR_OPTIONS::GetArraySize() const -{ - return m_nPts; -} - - -void DIALOG_CREATE_ARRAY::ARRAY_CIRCULAR_OPTIONS::TransformItem( int n, BOARD_ITEM* item, - const wxPoint& rotPoint ) const -{ - double angle; - - if( m_angle == 0 ) - // angle is zero, divide evenly into m_nPts - angle = 3600.0 * n / double( m_nPts ); - else - // n'th step - angle = m_angle * n; - - item->Rotate( m_centre, angle ); - - // take off the rotation (but not the translation) if needed - if( !m_rotateItems ) - item->Rotate( item->GetCenter(), -angle ); -} - - -wxString DIALOG_CREATE_ARRAY::ARRAY_CIRCULAR_OPTIONS::GetItemNumber( int aN ) const -{ - return getCoordinateNumber( aN + m_numberingOffset, m_numberingType ); -} + m_circRadius.SetValue( int( centre.EuclideanNorm() ) ); +} \ No newline at end of file diff --git a/pcbnew/dialogs/dialog_create_array.h b/pcbnew/dialogs/dialog_create_array.h index efafca40e5..be360ce9a0 100644 --- a/pcbnew/dialogs/dialog_create_array.h +++ b/pcbnew/dialogs/dialog_create_array.h @@ -28,6 +28,7 @@ // Include the wxFormBuider header base: #include +#include #include #include @@ -193,146 +194,8 @@ class DIALOG_CREATE_ARRAY : public DIALOG_CREATE_ARRAY_BASE, { public: - enum ARRAY_TYPE_T - { - ARRAY_GRID, ///< A grid (x*y) array - ARRAY_CIRCULAR, ///< A circular array - }; - - // NOTE: do not change order relative to charSetDescriptions - enum NUMBERING_TYPE_T - { - NUMBERING_NUMERIC = 0, ///< Arabic numerals: 0,1,2,3,4,5,6,7,8,9,10,11... - NUMBERING_HEX, - NUMBERING_ALPHA_NO_IOSQXZ, /*!< Alphabet, excluding IOSQXZ - * - * Per ASME Y14.35M-1997 sec. 5.2 (previously MIL-STD-100 sec. 406.5) - * as these can be confused with numerals and are often not used - * for pin numbering on BGAs, etc - */ - NUMBERING_ALPHA_FULL, ///< Full 26-character alphabet - }; - #define NUMBERING_TYPE_MAX NUMBERING_ALPHA_FULL - /** - * Persistent dialog options - */ - struct ARRAY_OPTIONS - { - ARRAY_OPTIONS( ARRAY_TYPE_T aType ) : - m_type( aType ), - m_shouldNumber( false ), - m_numberingStartIsSpecified( false ) - {} - - virtual ~ARRAY_OPTIONS() {}; - - ARRAY_TYPE_T m_type; - - /*! - * Function GetArrayPositions - * Returns the set of points that represent the array - * in order, if that is important - * - * TODO: Can/should this be done with some sort of iterator? - */ - virtual void TransformItem( int n, BOARD_ITEM* item, - const wxPoint& rotPoint ) const = 0; - virtual int GetArraySize() const = 0; - virtual wxString GetItemNumber( int n ) const = 0; - virtual wxString InterpolateNumberIntoString( int n, const wxString& pattern ) const; - - /*! - * @return are the items in this array numberred, or are all the - * items numbered the same - */ - bool ShouldNumberItems() const - { - return m_shouldNumber; - } - - /*! - * @return is the numbering is enabled and should start at a point - * specified in these options or is it implicit according to the calling - * code? - */ - bool NumberingStartIsSpecified() const - { - return m_shouldNumber && m_numberingStartIsSpecified; - } - - protected: - static wxString getCoordinateNumber( int n, NUMBERING_TYPE_T type ); - - // allow the dialog to set directly - friend class DIALOG_CREATE_ARRAY; - - /// True if this array numbers the new items - bool m_shouldNumber; - - /// True if this array's number starts from the preset point - /// False if the array numbering starts from some externally provided point - bool m_numberingStartIsSpecified; - }; - - struct ARRAY_GRID_OPTIONS : public ARRAY_OPTIONS - { - ARRAY_GRID_OPTIONS() : - ARRAY_OPTIONS( ARRAY_GRID ), - m_nx( 0 ), m_ny( 0 ), - m_horizontalThenVertical( true ), - m_reverseNumberingAlternate( false ), - m_stagger( 0 ), - m_stagger_rows( true ), - m_2dArrayNumbering( false ), - m_numberingOffsetX( 0 ), - m_numberingOffsetY( 0 ), - m_priAxisNumType( NUMBERING_NUMERIC ), - m_secAxisNumType( NUMBERING_NUMERIC ) - {} - - long m_nx, m_ny; - bool m_horizontalThenVertical, m_reverseNumberingAlternate; - wxPoint m_delta; - wxPoint m_offset; - long m_stagger; - bool m_stagger_rows; - bool m_2dArrayNumbering; - int m_numberingOffsetX, m_numberingOffsetY; - NUMBERING_TYPE_T m_priAxisNumType, m_secAxisNumType; - - void TransformItem( int n, BOARD_ITEM* item, const wxPoint& rotPoint ) const override; - int GetArraySize() const override; - wxString GetItemNumber( int n ) const override; - -private: - wxPoint getGridCoords( int n ) const; - }; - - struct ARRAY_CIRCULAR_OPTIONS : public ARRAY_OPTIONS - { - ARRAY_CIRCULAR_OPTIONS() : - ARRAY_OPTIONS( ARRAY_CIRCULAR ), - m_nPts( 0 ), - m_angle( 0.0f ), - m_rotateItems( false ), - m_numberingType( NUMBERING_NUMERIC ), - m_numberingOffset( 0 ) - {} - - long m_nPts; - double m_angle; - wxPoint m_centre; - bool m_rotateItems; - NUMBERING_TYPE_T m_numberingType; - long m_numberingOffset; - - void TransformItem( int n, BOARD_ITEM* item, const wxPoint& rotPoint ) const override; - int GetArraySize() const override; - wxString GetItemNumber( int n ) const override; - }; - // Constructor and destructor DIALOG_CREATE_ARRAY( PCB_BASE_FRAME* aParent, bool enableNumbering, wxPoint aOrigPos ); diff --git a/qa/common/CMakeLists.txt b/qa/common/CMakeLists.txt index 24b0651411..5adca44353 100644 --- a/qa/common/CMakeLists.txt +++ b/qa/common/CMakeLists.txt @@ -36,6 +36,7 @@ set( common_srcs ../../common/colors.cpp ../../common/observable.cpp + test_array_options.cpp test_color4d.cpp test_coroutine.cpp test_format_units.cpp diff --git a/qa/common/test_array_options.cpp b/qa/common/test_array_options.cpp new file mode 100644 index 0000000000..ba77317be6 --- /dev/null +++ b/qa/common/test_array_options.cpp @@ -0,0 +1,477 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2019 KiCad Developers, see CHANGELOG.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 + */ + +/** + * @file test_array_options.cpp + * Test suite for #ARRAY_OPTIONS + */ + +#include +#include + +#include +#include + +#include + +/** + * Define a stream function for logging this type. + * + * TODO: convert to boost_test_print_type when Boost minver > 1.64 + */ +std::ostream& operator<<( std::ostream& os, const ARRAY_OPTIONS::TRANSFORM& aObj ) +{ + os << "TRANSFORM[ " << aObj.m_offset << " r " << aObj.m_rotation << "deg" + << " ]"; + return os; +} + + +/** + * Predicate to see if a #ARRAY_OPTIONS::TRANSFORM is equal or nearly equal + */ +bool TransformIsClose( const ARRAY_OPTIONS::TRANSFORM& aL, const ARRAY_OPTIONS::TRANSFORM& aR ) +{ + return KI_TEST::IsVecWithinTol( aL.m_offset, aR.m_offset, 1 ) + && KI_TEST::IsWithin( aL.m_rotation, aR.m_rotation, 0.001 ); +} + + +/** + * Generate all array transforms for an array descriptor and compare + * against a list of expected transforms + * @param aOpts the array descriptor + * @param aPos the position of the reference item + * @param aExp expected transform list + */ +void CheckArrayTransforms( const ARRAY_OPTIONS& aOpts, const VECTOR2I& aPos, + const std::vector& aExp ) +{ + std::vector transforms; + + for( int i = 0; i < aOpts.GetArraySize(); ++i ) + { + transforms.push_back( aOpts.GetTransform( i, aPos ) ); + } + + BOOST_CHECK_EQUAL( transforms.size(), aExp.size() ); + + for( unsigned i = 0; i < std::min( transforms.size(), aExp.size() ); ++i ) + { + BOOST_TEST_CONTEXT( "Index " << i ) + { + BOOST_CHECK_PREDICATE( TransformIsClose, ( transforms[i] )( aExp[i] ) ); + } + } +} + + +/** + * Declare the test suite + */ +BOOST_AUTO_TEST_SUITE( ArrayOptions ) + + +struct GRID_ARRAY_GEOM_PARAMS +{ + int m_nx; + int m_ny; + VECTOR2I m_delta; + VECTOR2I m_offset; + int m_stagger; + bool m_stagger_by_row; + bool m_alternate_numbers; + bool m_h_then_v; +}; + +struct GRID_ARRAY_TEST_CASE +{ + std::string m_case_name; + GRID_ARRAY_GEOM_PARAMS m_geom; + VECTOR2I m_item_pos; + std::vector m_exp_transforms; +}; + + +// clang-format off +static const std::vector grid_geom_cases = { + { + "2x3 rect grid", + { + 2, + 3, + { Millimeter2iu( 2 ), Millimeter2iu( 2 ) }, + { 0, 0 }, + 1, + true, + false, + true, + }, + { 0, 0 }, + { + { { Millimeter2iu( 0 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 0 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 0 ), Millimeter2iu( 4 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 4 ) }, 0 }, + }, + }, + { + "2x3 offset grid", + { + 2, + 3, + { Millimeter2iu( 2 ), Millimeter2iu( 2 ) }, + { Millimeter2iu( 0.1 ), Millimeter2iu( 0.2 ) }, + 1, + true, + false, + true, + }, + { 0, 0 }, + { + // add the offsets for each positions + { { Millimeter2iu( 0 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 0.2 ) }, 0 }, + { { Millimeter2iu( 0.1 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 2.1 ), Millimeter2iu( 2.2 ) }, 0 }, + { { Millimeter2iu( 0.2 ), Millimeter2iu( 4.0 ) }, 0 }, + { { Millimeter2iu( 2.2 ), Millimeter2iu( 4.2 ) }, 0 }, + }, + }, + { + "2x3 stagger rows", + { + 2, + 3, + { Millimeter2iu( 3 ), Millimeter2iu( 2 ) }, + { 0, 0 }, + 3, + true, + false, + true, + }, + { 0, 0 }, + { + // add the offsets for each positions + { { Millimeter2iu( 0 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 3 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 1 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 4 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 4 ) }, 0 }, + { { Millimeter2iu( 5 ), Millimeter2iu( 4 ) }, 0 }, + }, + }, + { + "2x3 stagger cols", + { + 2, + 3, + { Millimeter2iu( 3 ), Millimeter2iu( 2 ) }, + { 0, 0 }, + 2, + false, + false, + true, + }, + { 0, 0 }, + { + // add the offsets for each positions + { { Millimeter2iu( 0 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 3 ), Millimeter2iu( 1 ) }, 0 }, + { { Millimeter2iu( 0 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 3 ), Millimeter2iu( 3 ) }, 0 }, + { { Millimeter2iu( 0 ), Millimeter2iu( 4 ) }, 0 }, + { { Millimeter2iu( 3 ), Millimeter2iu( 5 ) }, 0 }, + }, + }, + { + "2x3 rect alternate", + { + 2, + 3, + { Millimeter2iu( 2 ), Millimeter2iu( 2 ) }, + { 0, 0 }, + 1, + true, + true, + true, + }, + { 0, 0 }, + { + { { Millimeter2iu( 0 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 0 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 0 ), Millimeter2iu( 4 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 4 ) }, 0 }, + }, + }, + { + "2x3 rect v then h", + { + 2, + 3, + { Millimeter2iu( 2 ), Millimeter2iu( 2 ) }, + { 0, 0 }, + 1, + true, + false, + false, + }, + { 0, 0 }, + { + { { Millimeter2iu( 0 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 0 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 0 ), Millimeter2iu( 4 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 0 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 2 ) }, 0 }, + { { Millimeter2iu( 2 ), Millimeter2iu( 4 ) }, 0 }, + }, + }, +}; +// clang-format on + + +/** + * Test of grid array geometry + */ +BOOST_AUTO_TEST_CASE( GridGeometry ) +{ + for( const auto& c : grid_geom_cases ) + { + BOOST_TEST_CONTEXT( c.m_case_name ) + { + ARRAY_GRID_OPTIONS grid_opts; + + grid_opts.m_nx = c.m_geom.m_nx; + grid_opts.m_ny = c.m_geom.m_ny; + grid_opts.m_delta = (wxPoint) c.m_geom.m_delta; + grid_opts.m_offset = (wxPoint) c.m_geom.m_offset; + grid_opts.m_stagger = c.m_geom.m_stagger; + grid_opts.m_stagger_rows = c.m_geom.m_stagger_by_row; + grid_opts.m_reverseNumberingAlternate = c.m_geom.m_alternate_numbers; + grid_opts.m_horizontalThenVertical = c.m_geom.m_h_then_v; + + CheckArrayTransforms( grid_opts, c.m_item_pos, c.m_exp_transforms ); + } + } +} + + +struct CIRC_ARRAY_GEOM_PARAMS +{ + int n; + double angle_offset; + VECTOR2I centre; + bool rotate; +}; + +struct CIRC_ARRAY_TEST_CASE +{ + std::string m_case_name; + CIRC_ARRAY_GEOM_PARAMS m_geom; + VECTOR2I m_item_pos; + std::vector m_exp_transforms; +}; + + +// clang-format off +static const std::vector circ_geom_cases = { + { + "Quad, no rotate items", + { + 4, + 0, + { 0, 0 }, + false, + }, + { Millimeter2iu( 10 ), 0 }, + { + // diamond shape + { { Millimeter2iu( 0 ), Millimeter2iu( 0 ) } , 0 }, + { { Millimeter2iu( -10 ), Millimeter2iu( -10 ) } , 0 }, + { { Millimeter2iu( -20 ), Millimeter2iu( 0 ) } , 0 }, + { {Millimeter2iu( -10 ), Millimeter2iu( 10 ) } , 0 }, + }, + }, + { + "Quad, rotate items", + { + 4, + 0, + { 0, 0 }, + true, + }, + { Millimeter2iu( 10 ), 0 }, + { + { { Millimeter2iu( 0 ), Millimeter2iu( 0 ) } , 0 }, + { { Millimeter2iu( -10 ), Millimeter2iu( -10 ) } , 90 }, + { { Millimeter2iu( -20 ), Millimeter2iu( 0 ) } , 180 }, + { {Millimeter2iu( -10 ), Millimeter2iu( 10 ) } , 270 }, + }, + }, + { + "Three pts, 90 deg angle", + { + 3, + 45.0, + { 0, 0 }, + true, + }, + { Millimeter2iu( 10 ), 0 }, + { + { { Millimeter2iu( 0 ), Millimeter2iu( 0 ) } , 0 }, + // 10 * [ 1-sin(45), sin(45) ] + { { Millimeter2iu( -2.9289321881 ), Millimeter2iu( -7.0710678118 ) } , 45 }, + { { Millimeter2iu( -10 ), Millimeter2iu( -10 ) } , 90 }, + }, + }, +}; +// clang-format on + +/** + * Test of circular array geometry + */ +BOOST_AUTO_TEST_CASE( CircularGeometry ) +{ + for( const auto& c : circ_geom_cases ) + { + BOOST_TEST_CONTEXT( c.m_case_name ) + { + ARRAY_CIRCULAR_OPTIONS grid_opts; + + grid_opts.m_nPts = c.m_geom.n; + grid_opts.m_angle = 10 * c.m_geom.angle_offset; + grid_opts.m_centre = c.m_geom.centre; + grid_opts.m_rotateItems = c.m_geom.rotate; + + CheckArrayTransforms( grid_opts, c.m_item_pos, c.m_exp_transforms ); + } + } +} + +/** + * Generate all array names and check against expected + * @param aOpts the array descriptor + * @param aExp expected name list + */ +void CheckArrayNumbering( const ARRAY_OPTIONS& aOpts, const std::vector& aExp ) +{ + std::vector names; + + for( int i = 0; i < aOpts.GetArraySize(); ++i ) + { + names.push_back( aOpts.GetItemNumber( i ).ToStdString() ); + } + + BOOST_CHECK_EQUAL_COLLECTIONS( names.begin(), names.end(), aExp.begin(), aExp.end() ); +} + + +struct GRID_ARRAY_NAMING_PARAMS +{ + ARRAY_OPTIONS::NUMBERING_TYPE_T m_pri_type; + ARRAY_OPTIONS::NUMBERING_TYPE_T m_sec_type; + std::string m_start_at_x; + std::string m_start_at_y; + bool m_2d_numbering; + int m_nx; + int m_ny; +}; + + +struct GRID_ARRAY_NAMING_CASE +{ + std::string m_case_name; + GRID_ARRAY_NAMING_PARAMS m_prms; + std::vector m_exp_names; +}; + + +// clang-format off +static const std::vector grid_name_cases = { + { + "Linear grid", + { + ARRAY_OPTIONS::NUMBERING_TYPE_T::NUMBERING_NUMERIC, + ARRAY_OPTIONS::NUMBERING_TYPE_T::NUMBERING_NUMERIC, // doesn't matter here + "1", + "2", + false, + 2, + 3, + }, + { "1", "2", "3", "4", "5", "6" }, + }, + { + // Tests a 2d grid, with different types and offsets (and alphabet wrap) + "2D grid", + { + ARRAY_OPTIONS::NUMBERING_TYPE_T::NUMBERING_NUMERIC, + ARRAY_OPTIONS::NUMBERING_TYPE_T::NUMBERING_ALPHA_FULL, + "5", + "Z", + true, + 2, + 3, + }, + { "5Z", "6Z", "5AA", "6AA", "5AB", "6AB" }, + }, +}; +// clang-format on + + +/** + * Test of grid array geometry + */ +BOOST_AUTO_TEST_CASE( GridNaming ) +{ + for( const auto& c : grid_name_cases ) + { + BOOST_TEST_CONTEXT( c.m_case_name ) + { + ARRAY_GRID_OPTIONS grid_opts; + + grid_opts.m_nx = c.m_prms.m_nx; + grid_opts.m_ny = c.m_prms.m_ny; + + ARRAY_OPTIONS::GetNumberingOffset( + c.m_prms.m_start_at_x, c.m_prms.m_pri_type, grid_opts.m_numberingOffsetX ); + ARRAY_OPTIONS::GetNumberingOffset( + c.m_prms.m_start_at_y, c.m_prms.m_sec_type, grid_opts.m_numberingOffsetY ); + + grid_opts.m_priAxisNumType = c.m_prms.m_pri_type; + grid_opts.m_secAxisNumType = c.m_prms.m_sec_type; + + grid_opts.m_2dArrayNumbering = c.m_prms.m_2d_numbering; + + // other grid settings (geom) can be defaulted, as they don't affect numbering + + CheckArrayNumbering( grid_opts, c.m_exp_names ); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/qa/unit_test_utils/include/unit_test_utils/geometry.h b/qa/unit_test_utils/include/unit_test_utils/geometry.h index 3d189cc6d0..a1ff13b3b0 100644 --- a/qa/unit_test_utils/include/unit_test_utils/geometry.h +++ b/qa/unit_test_utils/include/unit_test_utils/geometry.h @@ -1,3 +1,25 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2018 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 QA_UNIT_TEST_UTILS_GEOM__H #define QA_UNIT_TEST_UTILS_GEOM__H