/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2014 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> * Copyright (C) 2014 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 <stdarg.h> #include <assert.h> #include <layers_id_colors_and_visibility.h> #include <class_board.h> LSET::LSET( const LAYER_ID* aArray, unsigned aCount ) : BASE_SET() { for( unsigned i=0; i<aCount; ++i ) set( aArray[i] ); } LSET::LSET( unsigned aIdCount, LAYER_ID aFirst, ... ) : BASE_SET() { // The constructor, without the mandatory aFirst argument, could have been confused // by the compiler with the LSET( LAYER_ID ). With aFirst, that ambiguity is not // present. Therefore aIdCount must always be >=1. wxASSERT_MSG( aIdCount > 0, wxT( "aIdCount must be >= 1" ) ); set( aFirst ); if( --aIdCount ) { va_list ap; va_start( ap, aFirst ); for( unsigned i=0; i<aIdCount; ++i ) { LAYER_ID id = (LAYER_ID) va_arg( ap, int ); // printf( "%s: id:%d LAYER_ID_COUNT:%d\n", __func__, id, LAYER_ID_COUNT ); assert( unsigned( id ) < LAYER_ID_COUNT ); set( id ); } va_end( ap ); } } const wxChar* LSET::Name( LAYER_ID aLayerId ) { const wxChar* txt; // using a switch to explicitly show the mapping more clearly switch( aLayerId ) { case F_Cu: txt = wxT( "F.Cu" ); break; case In1_Cu: txt = wxT( "In1.Cu" ); break; case In2_Cu: txt = wxT( "In2.Cu" ); break; case In3_Cu: txt = wxT( "In3.Cu" ); break; case In4_Cu: txt = wxT( "In4.Cu" ); break; case In5_Cu: txt = wxT( "In5.Cu" ); break; case In6_Cu: txt = wxT( "In6.Cu" ); break; case In7_Cu: txt = wxT( "In7.Cu" ); break; case In8_Cu: txt = wxT( "In8.Cu" ); break; case In9_Cu: txt = wxT( "In9.Cu" ); break; case In10_Cu: txt = wxT( "In10.Cu" ); break; case In11_Cu: txt = wxT( "In11.Cu" ); break; case In12_Cu: txt = wxT( "In12.Cu" ); break; case In13_Cu: txt = wxT( "In13.Cu" ); break; case In14_Cu: txt = wxT( "In14.Cu" ); break; case In15_Cu: txt = wxT( "In15.Cu" ); break; case In16_Cu: txt = wxT( "In16.Cu" ); break; case In17_Cu: txt = wxT( "In17.Cu" ); break; case In18_Cu: txt = wxT( "In18.Cu" ); break; case In19_Cu: txt = wxT( "In19.Cu" ); break; case In20_Cu: txt = wxT( "In20.Cu" ); break; case In21_Cu: txt = wxT( "In21.Cu" ); break; case In22_Cu: txt = wxT( "In22.Cu" ); break; case In23_Cu: txt = wxT( "In23.Cu" ); break; case In24_Cu: txt = wxT( "In24.Cu" ); break; case In25_Cu: txt = wxT( "In25.Cu" ); break; case In26_Cu: txt = wxT( "In26.Cu" ); break; case In27_Cu: txt = wxT( "In27.Cu" ); break; case In28_Cu: txt = wxT( "In28.Cu" ); break; case In29_Cu: txt = wxT( "In29.Cu" ); break; case In30_Cu: txt = wxT( "In30.Cu" ); break; case B_Cu: txt = wxT( "B.Cu" ); break; // Technicals case B_Adhes: txt = wxT( "B.Adhes" ); break; case F_Adhes: txt = wxT( "F.Adhes" ); break; case B_Paste: txt = wxT( "B.Paste" ); break; case F_Paste: txt = wxT( "F.Paste" ); break; case B_SilkS: txt = wxT( "B.SilkS" ); break; case F_SilkS: txt = wxT( "F.SilkS" ); break; case B_Mask: txt = wxT( "B.Mask" ); break; case F_Mask: txt = wxT( "F.Mask" ); break; // Users case Dwgs_User: txt = wxT( "Dwgs.User" ); break; case Cmts_User: txt = wxT( "Cmts.User" ); break; case Eco1_User: txt = wxT( "Eco1.User" ); break; case Eco2_User: txt = wxT( "Eco2.User" ); break; case Edge_Cuts: txt = wxT( "Edge.Cuts" ); break; case Margin: txt = wxT( "Margin" ); break; // Footprint case F_CrtYd: txt = wxT( "F.CrtYd" ); break; case B_CrtYd: txt = wxT( "B.CrtYd" ); break; case F_Fab: txt = wxT( "F.Fab" ); break; case B_Fab: txt = wxT( "B.Fab" ); break; default: wxASSERT_MSG( 0, wxT( "aLayerId out of range" ) ); txt = wxT( "BAD INDEX!" ); break; } return txt; } LSEQ LSET::CuStack() const { // desired sequence static const LAYER_ID sequence[] = { F_Cu, In1_Cu, In2_Cu, In3_Cu, In4_Cu, In5_Cu, In6_Cu, In7_Cu, In8_Cu, In9_Cu, In10_Cu, In11_Cu, In12_Cu, In13_Cu, In14_Cu, In15_Cu, In16_Cu, In17_Cu, In18_Cu, In19_Cu, In20_Cu, In21_Cu, In22_Cu, In23_Cu, In24_Cu, In25_Cu, In26_Cu, In27_Cu, In28_Cu, In29_Cu, In30_Cu, B_Cu, // 31 }; return Seq( sequence, DIM( sequence ) ); } LSEQ LSET::Technicals( LSET aSetToOmit ) const { // desired sequence static const LAYER_ID sequence[] = { B_Adhes, F_Adhes, B_Paste, F_Paste, B_SilkS, F_SilkS, B_Mask, F_Mask, B_CrtYd, F_CrtYd, B_Fab, F_Fab, }; LSET subset = ~aSetToOmit & *this; return subset.Seq( sequence, DIM( sequence ) ); } LSEQ LSET::Users() const { // desired static const LAYER_ID sequence[] = { Dwgs_User, Cmts_User, Eco1_User, Eco2_User, Edge_Cuts, Margin, }; return Seq( sequence, DIM( sequence ) ); } std::string LSET::FmtBin() const { std::string ret; int bit_count = size(); for( int bit=0; bit<bit_count; ++bit ) { if( bit ) { if( !( bit % 8 ) ) ret += '|'; else if( !( bit % 4 ) ) ret += '_'; } ret += (*this)[bit] ? '1' : '0'; } // reverse of string return std::string( ret.rbegin(), ret.rend() ); } std::string LSET::FmtHex() const { std::string ret; static const char hex[] = "0123456789abcdef"; int nibble_count = ( size() + 3 ) / 4; for( int nibble=0; nibble<nibble_count; ++nibble ) { unsigned ndx = 0; // test 4 consecutive bits and set ndx to 0-15: for( int nibble_bit=0; nibble_bit<4; ++nibble_bit ) { if( (*this)[nibble_bit + nibble*4] ) ndx |= (1 << nibble_bit); } if( nibble && !( nibble % 8 ) ) ret += '_'; assert( ndx < DIM( hex ) ); ret += hex[ndx]; } // reverse of string return std::string( ret.rbegin(), ret.rend() ); } int LSET::ParseHex( const char* aStart, int aCount ) { LSET tmp; const char* rstart = aStart + aCount - 1; const char* rend = aStart - 1; const int bitcount = size(); int nibble_ndx = 0; while( rstart > rend ) { int cc = *rstart--; if( cc == '_' ) continue; int nibble; if( cc >= '0' && cc <= '9' ) nibble = cc - '0'; else if( cc >= 'a' && cc <= 'f' ) nibble = cc - 'a' + 10; else if( cc >= 'A' && cc <= 'F' ) nibble = cc - 'A' + 10; else break; int bit = nibble_ndx * 4; for( int ndx=0; bit<bitcount && ndx<4; ++bit, ++ndx ) if( nibble & (1<<ndx) ) tmp.set( bit ); if( bit >= bitcount ) break; ++nibble_ndx; } int byte_count = aStart + aCount - 1 - rstart; assert( byte_count >= 0 ); if( byte_count > 0 ) *this = tmp; return byte_count; } LSEQ LSET::Seq( const LAYER_ID* aWishListSequence, unsigned aCount ) const { LSEQ ret; #if defined(DEBUG) && 0 LSET dup_detector; for( unsigned i=0; i<aCount; ++i ) { LAYER_ID id = aWishListSequence[i]; if( test( id ) ) { wxASSERT_MSG( !dup_detector[id], wxT( "Duplicate in aWishListSequence" ) ); dup_detector[id] = true; ret.push_back( id ); } } #else for( unsigned i=0; i<aCount; ++i ) { LAYER_ID id = aWishListSequence[i]; if( test( id ) ) ret.push_back( id ); } #endif return ret; } LSEQ LSET::Seq() const { LSEQ ret; for( unsigned i=0; i<size(); ++i ) { if( test(i) ) ret.push_back( LAYER_ID( i ) ); } return ret; } LSEQ LSET::SeqStackupBottom2Top() const { // bottom-to-top stack-up layers static const LAYER_ID sequence[] = { B_Fab, B_CrtYd, B_Adhes, B_SilkS, B_Paste, B_Mask, B_Cu, In30_Cu, In29_Cu, In28_Cu, In27_Cu, In26_Cu, In25_Cu, In24_Cu, In23_Cu, In22_Cu, In21_Cu, In20_Cu, In19_Cu, In18_Cu, In17_Cu, In16_Cu, In15_Cu, In14_Cu, In13_Cu, In12_Cu, In11_Cu, In10_Cu, In9_Cu, In8_Cu, In7_Cu, In6_Cu, In5_Cu, In4_Cu, In3_Cu, In2_Cu, In1_Cu, F_Cu, F_Mask, F_Paste, F_SilkS, F_Adhes, F_CrtYd, F_Fab, Dwgs_User, Cmts_User, Eco1_User, Eco2_User, Margin, Edge_Cuts, }; return Seq( sequence, DIM( sequence ) ); } LAYER_ID FlipLayer( LAYER_ID aLayerId, int aCopperLayersCount ) { switch( aLayerId ) { case B_Cu: return F_Cu; case F_Cu: return B_Cu; case B_SilkS: return F_SilkS; case F_SilkS: return B_SilkS; case B_Adhes: return F_Adhes; case F_Adhes: return B_Adhes; case B_Mask: return F_Mask; case F_Mask: return B_Mask; case B_Paste: return F_Paste; case F_Paste: return B_Paste; case B_CrtYd: return F_CrtYd; case F_CrtYd: return B_CrtYd; case B_Fab: return F_Fab; case F_Fab: return B_Fab; default: // change internal layer if aCopperLayersCount is >= 4 if( IsCopperLayer( aLayerId ) && aCopperLayersCount >= 4 ) { // internal copper layers count is aCopperLayersCount-2 LAYER_ID fliplayer = LAYER_ID(aCopperLayersCount - 2 - ( aLayerId - In1_Cu ) ); // Ensure fliplayer has a value which does not crash pcbnew: if( fliplayer < F_Cu ) fliplayer = F_Cu; if( fliplayer > B_Cu ) fliplayer = B_Cu; return fliplayer; } // No change for the other layers return aLayerId; } } LSET FlipLayerMask( LSET aMask, int aCopperLayersCount ) { // layers on physical outside of a board: const static LSET and_mask( 16, // !! update count B_Cu, F_Cu, B_SilkS, F_SilkS, B_Adhes, F_Adhes, B_Mask, F_Mask, B_Paste, F_Paste, B_Adhes, F_Adhes, B_CrtYd, F_CrtYd, B_Fab, F_Fab ); LSET newMask = aMask & ~and_mask; if( aMask[B_Cu] ) newMask.set( F_Cu ); if( aMask[F_Cu] ) newMask.set( B_Cu ); if( aMask[B_SilkS] ) newMask.set( F_SilkS ); if( aMask[F_SilkS] ) newMask.set( B_SilkS ); if( aMask[B_Adhes] ) newMask.set( F_Adhes ); if( aMask[F_Adhes] ) newMask.set( B_Adhes ); if( aMask[B_Mask] ) newMask.set( F_Mask ); if( aMask[F_Mask] ) newMask.set( B_Mask ); if( aMask[B_Paste] ) newMask.set( F_Paste ); if( aMask[F_Paste] ) newMask.set( B_Paste ); if( aMask[B_Adhes] ) newMask.set( F_Adhes ); if( aMask[F_Adhes] ) newMask.set( B_Adhes ); if( aMask[B_CrtYd] ) newMask.set( F_CrtYd ); if( aMask[F_CrtYd] ) newMask.set( B_CrtYd ); if( aMask[B_Fab] ) newMask.set( F_Fab ); if( aMask[F_Fab] ) newMask.set( B_Fab ); if( aCopperLayersCount >= 4 ) // Internal layers exist { LSET internalMask = aMask & ~LSET::InternalCuMask(); if( internalMask != LSET::InternalCuMask() ) { // the mask does not include all internal layers. Therefore // the flipped mask for internal copper layers must be built int innerLayerCnt = aCopperLayersCount -2; for( int ii = 0; ii < innerLayerCnt; ii++ ) { if( internalMask[innerLayerCnt - ii + In1_Cu] ) newMask.set( ii + In1_Cu ); else newMask.reset( ii + In1_Cu ); } } } return newMask; } LAYER_ID LSET::ExtractLayer() const { unsigned set_count = count(); if( !set_count ) return UNSELECTED_LAYER; else if( set_count > 1 ) return UNDEFINED_LAYER; for( unsigned i=0; i < size(); ++i ) { if( test( i ) ) return LAYER_ID( i ); } wxASSERT( 0 ); // set_count was verified as 1 above, what did you break? return UNDEFINED_LAYER; } LSET LSET::InternalCuMask() { static const LAYER_ID cu_internals[] = { In1_Cu, In2_Cu, In3_Cu, In4_Cu, In5_Cu, In6_Cu, In7_Cu, In8_Cu, In9_Cu, In10_Cu, In11_Cu, In12_Cu, In13_Cu, In14_Cu, In15_Cu, In16_Cu, In17_Cu, In18_Cu, In19_Cu, In20_Cu, In21_Cu, In22_Cu, In23_Cu, In24_Cu, In25_Cu, In26_Cu, In27_Cu, In28_Cu, In29_Cu, In30_Cu, }; static const LSET saved( cu_internals, DIM( cu_internals ) ); return saved; } LSET LSET::AllCuMask( int aCuLayerCount ) { // retain all in static as the full set, which is a common case. static const LSET all = InternalCuMask().set( F_Cu ).set( B_Cu ); if( aCuLayerCount == MAX_CU_LAYERS ) return all; // subtract out some Cu layers not wanted in the mask. LSET ret = all; int clear_count = MAX_CU_LAYERS - aCuLayerCount; clear_count = Clamp( 0, clear_count, MAX_CU_LAYERS - 2 ); for( LAYER_NUM elem=In30_Cu; clear_count; --elem, --clear_count ) { ret.set( elem, false ); } return ret; } LSET LSET::AllNonCuMask() { static const LSET saved = LSET().set() & ~AllCuMask(); return saved; } LSET LSET::AllLayersMask() { static const LSET saved = LSET().set(); return saved; } LSET LSET::BackTechMask() { // (SILKSCREEN_LAYER_BACK | SOLDERMASK_LAYER_BACK | ADHESIVE_LAYER_BACK | SOLDERPASTE_LAYER_BACK) static const LSET saved( 6, B_SilkS, B_Mask, B_Adhes, B_Paste, B_CrtYd, B_Fab ); return saved; } LSET LSET::FrontTechMask() { // (SILKSCREEN_LAYER_FRONT | SOLDERMASK_LAYER_FRONT | ADHESIVE_LAYER_FRONT | SOLDERPASTE_LAYER_FRONT) static const LSET saved( 6, F_SilkS, F_Mask, F_Adhes, F_Paste, F_CrtYd, F_Fab ); return saved; } LSET LSET::AllTechMask() { static const LSET saved = BackTechMask() | FrontTechMask(); return saved; } LSET LSET::UserMask() { static const LSET saved( 6, Dwgs_User, Cmts_User, Eco1_User, Eco2_User, Edge_Cuts, Margin ); return saved; } LSET LSET::FrontMask() { static const LSET saved = FrontTechMask().set( F_Cu ); return saved; } LSET LSET::BackMask() { static const LSET saved = BackTechMask().set( B_Cu ); return saved; } LSEQ LSET::UIOrder() const { LAYER_ID order[LAYER_ID_COUNT]; // Assmuming that the LAYER_ID order is according to preferred UI order, as of // today this is true. When that becomes not true, its easy to change the order // in here to compensate. for( unsigned i=0; i<DIM(order); ++i ) order[i] = LAYER_ID( i ); return Seq( order, DIM( order ) ); } LAYER_ID ToLAYER_ID( int aLayer ) { wxASSERT( unsigned( aLayer ) < LAYER_ID_COUNT ); return LAYER_ID( aLayer ); }