You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

787 lines
25 KiB

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2020 Thomas Pointhuber <thomas.pointhuber@gmx.at>
* Copyright (C) 2020 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 <core/type_helpers.h>
#include <bitmaps.h>
#include <gr_basic.h>
#include <macros.h>
#include <pcb_edit_frame.h>
#include <richio.h>
#include <trigo.h>
#include <base_units.h>
#include <pcb_barcode.h>
#include <board.h>
#include <geometry/shape_poly_set.h>
#include <pcb_text.h>
#include <math/util.h> // for KiROUND
#include <convert_basic_shapes_to_polygon.h>
#include <wx/log.h>
#include <pgm_base.h>
#include <settings/color_settings.h>
#include <settings/settings_manager.h>
#include <scoped_set_reset.h>
#include <stdexcept>
#include <utility>
#include <algorithm>
#include <footprint.h>
#include <backend/zint.h>
#include <board_design_settings.h>
PCB_BARCODE::PCB_BARCODE( BOARD_ITEM* aParent ) :
BOARD_ITEM( aParent, PCB_BARCODE_T ),
m_width( pcbIUScale.mmToIU( 40 ) ),
m_height( pcbIUScale.mmToIU( 40 ) ),
m_pos( 0, 0 ),
m_text( this ),
m_kind( BARCODE_T::QR_CODE ),
m_angle( 0 ),
m_errorCorrection( BARCODE_ECC_T::L )
{
m_layer = Dwgs_User;
}
PCB_BARCODE::~PCB_BARCODE()
{
}
void PCB_BARCODE::SetPosition( const VECTOR2I& aPos )
{
VECTOR2I delta = aPos - m_pos;
Move( delta );
}
VECTOR2I PCB_BARCODE::GetPosition() const
{
return m_pos;
}
void PCB_BARCODE::SetText( const wxString& aNewText )
{
m_text.SetText( aNewText );
}
wxString PCB_BARCODE::GetText() const
{
return m_text.GetText();
}
wxString PCB_BARCODE::GetShownText() const
{
return m_text.GetShownText( true );
}
void PCB_BARCODE::SetLayer( PCB_LAYER_ID aLayer )
{
m_layer = aLayer;
m_text.SetLayer( aLayer );
AssembleBarcode( true, true );
}
void PCB_BARCODE::SetTextSize( int aTextSize )
{
m_text.SetTextSize( VECTOR2I( std::max( 1, aTextSize ), std::max( 1, aTextSize ) ) );
m_text.SetTextThickness( std::max( 1, GetPenSizeForNormal( m_text.GetTextHeight() ) ) );
AssembleBarcode( false, true );
}
int PCB_BARCODE::GetTextSize() const
{
return m_text.GetTextHeight();
}
void PCB_BARCODE::Move( const VECTOR2I& offset )
{
m_pos += offset;
m_symbolPoly.Move( offset );
m_textPoly.Move( offset );
m_poly.Move( offset );
m_text.Move( offset );
m_bbox.Move( offset );
}
void PCB_BARCODE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
{
RotatePoint( m_pos, aRotCentre, aAngle );
m_angle += aAngle;
AssembleBarcode( true, true );
}
void PCB_BARCODE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
{
MIRROR( m_pos, aCentre, aFlipDirection );
if( aFlipDirection == FLIP_DIRECTION::TOP_BOTTOM )
m_angle += ANGLE_180;
SetLayer( GetBoard()->FlipLayer( GetLayer() ) );
AssembleBarcode( true, true );
}
void PCB_BARCODE::StyleFromSettings( const BOARD_DESIGN_SETTINGS& settings, bool aCheckSide )
{
SetTextSize( settings.GetTextSize( GetLayer() ).y );
}
void PCB_BARCODE::AssembleBarcode( bool aRebuildBarcode, bool aRebuildText )
{
if( aRebuildBarcode )
ComputeBarcode();
// Scale the symbol polygon to the desired barcode width/height (property values) and center it at m_pos
// Note: SetRect will rescale the symbol-only polygon and then rebuild m_poly
SetRect( m_pos - VECTOR2I( m_width / 2, m_height / 2 ),
m_pos + VECTOR2I( m_width / 2, m_height / 2 ) );
if( aRebuildText )
ComputeTextPoly();
// Build full m_poly from symbol + optional text, then apply knockout if requested
m_poly.RemoveAllContours();
m_poly.Append( m_symbolPoly );
if( m_text.IsVisible() && m_textPoly.OutlineCount() )
m_poly.Append( m_textPoly );
m_poly.Fracture();
if( IsKnockout() )
{
// Enforce minimum margin: at least 10% of the smallest side of the barcode, rounded up
// to the nearest 0.1 mm. Use this as a lower bound for both axes.
int minSide = std::min( m_width, m_height );
int tenPercent = ( minSide + 9 ) / 10; // ceil(minSide * 0.1)
int step01mm = std::max( 1, pcbIUScale.mmToIU( 0.1 ) );
int tenPercentRounded = ( ( tenPercent + step01mm - 1 ) / step01mm ) * step01mm;
// Build inversion rectangle based on the local bbox of the current combined geometry
BOX2I bbox = m_poly.BBox();
bbox.Inflate( std::max( m_margin.x, tenPercentRounded ), std::max( m_margin.y, tenPercentRounded ) );
SHAPE_LINE_CHAIN rect;
rect.Append( bbox.GetLeft(), bbox.GetTop() );
rect.Append( bbox.GetRight(), bbox.GetTop() );
rect.Append( bbox.GetRight(), bbox.GetBottom() );
rect.Append( bbox.GetLeft(), bbox.GetBottom() );
rect.SetClosed( true );
SHAPE_POLY_SET ko;
ko.AddOutline( rect );
ko.BooleanSubtract( m_poly );
ko.Fracture();
m_poly = std::move( ko );
}
if( IsSideSpecific() && GetBoard() && GetBoard()->IsBackLayer( m_layer ) )
m_poly.Mirror( m_pos, FLIP_DIRECTION::LEFT_RIGHT );
if( !m_angle.IsZero() )
m_poly.Rotate( m_angle, m_pos );
m_poly.CacheTriangulation( false );
m_bbox = m_poly.BBox();
}
void PCB_BARCODE::ComputeTextPoly()
{
m_textPoly.RemoveAllContours();
if( !m_text.IsVisible() )
return;
SHAPE_POLY_SET textPoly;
m_text.TransformTextToPolySet( textPoly, 0, GetMaxError(), ERROR_INSIDE );
if( textPoly.OutlineCount() == 0 )
return;
if( m_symbolPoly.OutlineCount() == 0 )
return;
BOX2I textBBox = textPoly.BBox();
BOX2I symbolBBox = m_symbolPoly.BBox();
VECTOR2I textPos;
int textOffset = pcbIUScale.mmToIU( 1 );
textPos.x = symbolBBox.GetCenter().x - textBBox.GetCenter().x;
textPos.y = symbolBBox.GetBottom() - textBBox.GetTop() + textOffset;
textPoly.Move( textPos );
m_textPoly = std::move( textPoly );
m_textPoly.CacheTriangulation();
}
void PCB_BARCODE::ComputeBarcode()
{
m_symbolPoly.RemoveAllContours();
std::unique_ptr<zint_symbol, decltype( &ZBarcode_Delete )> symbol( ZBarcode_Create(), &ZBarcode_Delete );
if( !symbol )
{
wxLogError( wxT( "Zint: failed to allocate symbol" ) );
return;
}
symbol->input_mode = UNICODE_MODE;
symbol->show_hrt = 0; // do not show HRT
switch( m_kind )
{
case BARCODE_T::CODE_39:
symbol->symbology = BARCODE_CODE39;
break;
case BARCODE_T::CODE_128:
symbol->symbology = BARCODE_CODE128;
break;
case BARCODE_T::QR_CODE:
symbol->symbology = BARCODE_QRCODE;
symbol->option_1 = to_underlying( m_errorCorrection );
break;
case BARCODE_T::MICRO_QR_CODE:
symbol->symbology = BARCODE_MICROQR;
symbol->option_1 = to_underlying( m_errorCorrection );
break;
case BARCODE_T::DATA_MATRIX:
symbol->symbology = BARCODE_DATAMATRIX;
break;
default:
wxLogError( wxT( "Zint: invalid barcode type" ) );
return;
}
wxString text = GetText();
wxScopedCharBuffer utf8Text = text.ToUTF8();
size_t length = utf8Text.length();
unsigned char* dataPtr = reinterpret_cast<unsigned char*>( utf8Text.data() );
if( text.empty() )
return;
if( ZBarcode_Encode( symbol.get(), dataPtr, length ) )
{
wxLogDebug( wxT( "Zint encode error: %s" ), wxString::FromUTF8( symbol->errtxt ) );
return;
}
if( ZBarcode_Buffer_Vector( symbol.get(), 0 ) ) // 0 means success
{
wxLogDebug( wxT( "Zint render error: %s" ), wxString::FromUTF8( symbol->errtxt ) );
return;
}
for( zint_vector_rect* rect = symbol->vector->rectangles; rect != nullptr; rect = rect->next )
{
// Round using absolute edges to avoid cumulative rounding drift across modules.
int x1 = KiROUND( rect->x * symbol->scale );
int x2 = KiROUND( ( rect->x + rect->width ) * symbol->scale );
int y1 = KiROUND( rect->y * symbol->scale );
int y2 = KiROUND( ( rect->y + rect->height ) * symbol->scale );
SHAPE_LINE_CHAIN shapeline;
shapeline.Append( x1, y1 );
shapeline.Append( x2, y1 );
shapeline.Append( x2, y2 );
shapeline.Append( x1, y2 );
shapeline.SetClosed( true );
m_symbolPoly.AddOutline( shapeline );
}
for( zint_vector_hexagon* hex = symbol->vector->hexagons; hex != nullptr; hex = hex->next )
{
// Compute vertices from center using minimal-diameter (inscribed circle) radius.
double r = hex->diameter / 2.0; // minimal radius
double cx = hex->x;
double cy = hex->y;
// Base orientation has apex at top; hex->rotation rotates by 0/90/180/270 degrees.
double baseAngles[6] = { 90.0, 30.0, -30.0, -90.0, -150.0, 150.0 };
double rot = static_cast<double>( hex->rotation );
SHAPE_LINE_CHAIN poly;
for( int k = 0; k < 6; ++k )
{
double ang = ( baseAngles[k] + rot ) * M_PI / 180.0;
int vx = KiROUND( cx + r * cos( ang ) );
int vy = KiROUND( cy + r * sin( ang ) );
poly.Append( vx, vy );
}
poly.SetClosed( true );
m_symbolPoly.AddOutline( poly );
}
// Set the position of the barcode to the center of the symbol polygon
if( m_symbolPoly.OutlineCount() > 0 )
{
VECTOR2I pos = m_symbolPoly.BBox().GetCenter();
m_symbolPoly.Move( -pos );
}
m_symbolPoly.CacheTriangulation();
}
void PCB_BARCODE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
{
FOOTPRINT* parentFP = GetParentFootprint();
if( parentFP && aFrame->GetName() == PCB_EDIT_FRAME_NAME )
aList.emplace_back( _( "Footprint" ), parentFP->GetReference() );
aList.emplace_back( _( "Barcode" ), ENUM_MAP<BARCODE_T>::Instance().ToString( m_kind ) );
// Don't use GetShownText() here; we want to show the user the variable references
aList.emplace_back( _( "Text" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
aList.emplace_back( _( "Status" ), _( "Locked" ) );
aList.emplace_back( _( "Layer" ), GetLayerName() );
aList.emplace_back( _( "Angle" ), wxString::Format( wxT( "%g" ), m_angle.AsDegrees() ) );
aList.emplace_back( _( "Text Height" ), aFrame->MessageTextFromValue( m_text.GetTextHeight() ) );
}
bool PCB_BARCODE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
{
if( !GetBoundingBox().Contains( aPosition ) )
return false;
SHAPE_POLY_SET hulls;
GetBoundingHull( hulls, UNDEFINED_LAYER, aAccuracy, ARC_LOW_DEF, ERROR_OUTSIDE );
return hulls.Collide( aPosition );
}
bool PCB_BARCODE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
{
BOX2I arect = aRect;
arect.Inflate( aAccuracy );
BOX2I rect = GetBoundingBox();
if( aAccuracy )
rect.Inflate( aAccuracy );
if( aContained )
return arect.Contains( rect );
return arect.Intersects( rect );
}
void PCB_BARCODE::SetRect( const VECTOR2I& aTopLeft, const VECTOR2I& aBotRight )
{
// Rescale only the symbol polygon to the requested rectangle; text is rebuilt below
BOX2I bbox = m_symbolPoly.BBox();
int oldW = bbox.GetWidth();
int oldH = bbox.GetHeight();
VECTOR2I newPosition = ( aTopLeft + aBotRight ) / 2;
SetPosition( newPosition );
int newW = aBotRight.x - aTopLeft.x;
int newH = aBotRight.y - aTopLeft.y;
// Guard against zero/negative sizes from interactive edits; enforce a tiny minimum
int minIU = std::max( 1, pcbIUScale.mmToIU( 0.01 ) );
newW = std::max( newW, minIU );
newH = std::max( newH, minIU );
double scaleX = oldW ? static_cast<double>( newW ) / oldW : 1.0;
double scaleY = oldH ? static_cast<double>( newH ) / oldH : 1.0;
VECTOR2I oldCenter = bbox.GetCenter();
m_symbolPoly.Scale( scaleX, scaleY, oldCenter );
// After scaling, move the symbol polygon to be centered at the new position
VECTOR2I newCenter = m_symbolPoly.BBox().GetCenter();
VECTOR2I delta = newPosition - newCenter;
if( delta != VECTOR2I( 0, 0 ) )
m_symbolPoly.Move( delta );
// Update intended barcode symbol size (without text/margins)
m_width = newW;
m_height = newH;
}
const BOX2I PCB_BARCODE::GetBoundingBox() const
{
return m_bbox;
}
wxString PCB_BARCODE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
{
return wxString::Format( _( "Barcode '%s' on %s" ), GetText(), GetLayerName() );
}
BITMAPS PCB_BARCODE::GetMenuImage() const
{
return BITMAPS::add_barcode;
}
const BOX2I PCB_BARCODE::ViewBBox() const
{
return m_bbox;
}
void PCB_BARCODE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
int aClearance, int aMaxError,
ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
{
if( aLayer != m_layer && aLayer != UNDEFINED_LAYER )
return;
if( aClearance == 0 )
{
aBuffer.Append( m_poly );
}
else
{
SHAPE_POLY_SET poly = m_poly;
poly.Inflate( aClearance, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, aMaxError, aErrorLoc );
aBuffer.Append( poly );
}
}
std::shared_ptr<SHAPE> PCB_BARCODE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
{
SHAPE_POLY_SET poly;
TransformShapeToPolygon( poly, aLayer, 0, 0, ERROR_INSIDE, true );
return std::make_shared<SHAPE_POLY_SET>( std::move( poly ) );
}
void PCB_BARCODE::GetBoundingHull( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
int aMaxError, ERROR_LOC aErrorLoc ) const
{
auto getBoundingHull =
[this]( SHAPE_POLY_SET& aLocBuffer, const SHAPE_POLY_SET& aSource, int aLocClearance )
{
BOX2I rect = aSource.BBox( aLocClearance );
VECTOR2I corners[4];
corners[0].x = rect.GetOrigin().x;
corners[0].y = rect.GetOrigin().y;
corners[1].y = corners[0].y;
corners[1].x = rect.GetRight();
corners[2].x = corners[1].x;
corners[2].y = rect.GetBottom();
corners[3].y = corners[2].y;
corners[3].x = corners[0].x;
aLocBuffer.NewOutline();
for( VECTOR2I& corner : corners )
{
RotatePoint( corner, m_pos, m_angle );
aLocBuffer.Append( corner.x, corner.y );
}
};
if( aLayer == m_layer || aLayer == UNDEFINED_LAYER )
{
getBoundingHull( aBuffer, m_symbolPoly, aClearance );
getBoundingHull( aBuffer, m_textPoly, aClearance );
}
}
void PCB_BARCODE::SetErrorCorrection( BARCODE_ECC_T aErrorCorrection )
{
// Micro QR codes do not support High (H) error correction level
if( m_kind == BARCODE_T::MICRO_QR_CODE && aErrorCorrection == BARCODE_ECC_T::H )
m_errorCorrection = BARCODE_ECC_T::Q;
else
m_errorCorrection = aErrorCorrection;
// Don't auto-compute here as it may be called during loading
}
void PCB_BARCODE::SetKind( BARCODE_T aKind )
{
m_kind = aKind;
// When switching to Micro QR, validate and adjust ECC if needed
if( m_kind == BARCODE_T::MICRO_QR_CODE && m_errorCorrection == BARCODE_ECC_T::H )
m_errorCorrection = BARCODE_ECC_T::Q;
// Don't auto-compute here as it may be called during loading
}
void PCB_BARCODE::SetBarcodeErrorCorrection( BARCODE_ECC_T aErrorCorrection )
{
SetErrorCorrection( aErrorCorrection );
AssembleBarcode( true, false );
}
void PCB_BARCODE::SetBarcodeKind( BARCODE_T aKind )
{
SetKind( aKind );
AssembleBarcode( true, false );
}
EDA_ITEM* PCB_BARCODE::Clone() const
{
PCB_BARCODE* item = new PCB_BARCODE( *this );
item->CopyFrom( this );
item->m_text.SetParent( item );
return item;
}
void PCB_BARCODE::swapData( BOARD_ITEM* aImage )
{
wxCHECK_RET( aImage && aImage->Type() == PCB_BARCODE_T,
wxT( "Cannot swap data with non-barcode item." ) );
PCB_BARCODE* other = static_cast<PCB_BARCODE*>( aImage );
std::swap( *this, *other );
m_text.SetParent( this );
other->m_text.SetParent( other );
}
double PCB_BARCODE::Similarity( const BOARD_ITEM& aItem ) const
{
if( !ClassOf( &aItem ) )
return 0.0;
const PCB_BARCODE* other = static_cast<const PCB_BARCODE*>( &aItem );
// Compare text, width, height, text height, position, and kind
double similarity = 0.0;
const double weight = 1.0 / 6.0;
if( GetText() == other->GetText() )
similarity += weight;
if( m_width == other->m_width )
similarity += weight;
if( m_height == other->m_height )
similarity += weight;
if( GetTextSize() == other->GetTextSize() )
similarity += weight;
if( GetPosition() == other->GetPosition() )
similarity += weight;
if( m_kind == other->m_kind )
similarity += weight;
return similarity;
}
int PCB_BARCODE::Compare( const PCB_BARCODE* aBarcode, const PCB_BARCODE* aOther )
{
int diff;
if( ( diff = aBarcode->GetPosition().x - aOther->GetPosition().x ) != 0 )
return diff;
if( ( diff = aBarcode->GetPosition().y - aOther->GetPosition().y ) != 0 )
return diff;
if( ( diff = aBarcode->GetText().Cmp( aOther->GetText() ) ) != 0 )
return diff;
if( ( diff = aBarcode->GetWidth() - aOther->GetWidth() ) != 0 )
return diff;
if( ( diff = aBarcode->GetHeight() - aOther->GetHeight() ) != 0 )
return diff;
if( ( diff = aBarcode->GetTextSize() - aOther->GetTextSize() ) != 0 )
return diff;
if( ( diff = (int) aBarcode->GetKind() - (int) aOther->GetKind() ) != 0 )
return diff;
if( ( diff = (int) aBarcode->GetErrorCorrection() - (int) aOther->GetErrorCorrection() ) != 0 )
return diff;
return 0;
}
bool PCB_BARCODE::operator==( const BOARD_ITEM& aItem ) const
{
if( !ClassOf( &aItem ) )
return false;
const PCB_BARCODE* other = static_cast<const PCB_BARCODE*>( &aItem );
// Compare text, width, height, text height, position, and kind
return ( GetText() == other->GetText()
&& m_width == other->m_width
&& m_height == other->m_height
&& GetTextSize() == other->GetTextSize()
&& GetPosition() == other->GetPosition()
&& m_kind == other->m_kind );
}
// ---- Property registration ----
static struct PCB_BARCODE_DESC
{
PCB_BARCODE_DESC()
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PCB_BARCODE );
propMgr.InheritsAfter( TYPE_HASH( PCB_BARCODE ), TYPE_HASH( BOARD_ITEM ) );
const wxString groupBarcode = _HKI( "Barcode Properties" );
ENUM_MAP<BARCODE_T>& kindMap = ENUM_MAP<BARCODE_T>::Instance();
if( kindMap.Choices().GetCount() == 0 )
{
kindMap.Undefined( BARCODE_T::QR_CODE );
kindMap.Map( BARCODE_T::CODE_39, _HKI( "CODE_39" ) )
.Map( BARCODE_T::CODE_128, _HKI( "CODE_128" ) )
.Map( BARCODE_T::DATA_MATRIX, _HKI( "DATA_MATRIX" ) )
.Map( BARCODE_T::QR_CODE, _HKI( "QR_CODE" ) )
.Map( BARCODE_T::MICRO_QR_CODE, _HKI( "MICRO_QR_CODE" ) );
}
ENUM_MAP<BARCODE_ECC_T>& eccMap = ENUM_MAP<BARCODE_ECC_T>::Instance();
if( eccMap.Choices().GetCount() == 0 )
{
eccMap.Undefined( BARCODE_ECC_T::L );
eccMap.Map( BARCODE_ECC_T::L, _HKI( "L (Low)" ) )
.Map( BARCODE_ECC_T::M, _HKI( "M (Medium)" ) )
.Map( BARCODE_ECC_T::Q, _HKI( "Q (Quartile)" ) )
.Map( BARCODE_ECC_T::H, _HKI( "H (High)" ) );
}
auto hasKnockout =
[]( INSPECTABLE* aItem ) -> bool
{
if( PCB_BARCODE* bc = dynamic_cast<PCB_BARCODE*>( aItem ) )
return bc->IsKnockout();
return false;
};
propMgr.AddProperty( new PROPERTY<PCB_BARCODE, wxString>( _HKI( "Text" ),
&PCB_BARCODE::SetBarcodeText, &PCB_BARCODE::GetText ), groupBarcode );
propMgr.AddProperty( new PROPERTY<PCB_BARCODE, bool>( _HKI( "Show Text" ),
&PCB_BARCODE::SetShowText, &PCB_BARCODE::GetShowText ), groupBarcode );
propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Text Size" ),
&PCB_BARCODE::SetTextSize, &PCB_BARCODE::GetTextSize,
PROPERTY_DISPLAY::PT_COORD ), groupBarcode );
propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Width" ),
&PCB_BARCODE::SetBarcodeWidth, &PCB_BARCODE::GetWidth,
PROPERTY_DISPLAY::PT_COORD ), groupBarcode );
propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Height" ),
&PCB_BARCODE::SetBarcodeHeight, &PCB_BARCODE::GetHeight,
PROPERTY_DISPLAY::PT_COORD ), groupBarcode );
propMgr.AddProperty( new PROPERTY<PCB_BARCODE, double>( _HKI( "Orientation" ),
&PCB_BARCODE::SetOrientation, &PCB_BARCODE::GetOrientation ), groupBarcode );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_BARCODE, BARCODE_T>( _HKI( "Barcode Type" ),
&PCB_BARCODE::SetBarcodeKind, &PCB_BARCODE::GetKind ), groupBarcode );
auto isQRCode =
[]( INSPECTABLE* aItem ) -> bool
{
if( PCB_BARCODE* bc = dynamic_cast<PCB_BARCODE*>( aItem ) )
return bc->GetKind() == BARCODE_T::QR_CODE || bc->GetKind() == BARCODE_T::MICRO_QR_CODE;
return false;
};
propMgr.AddProperty( new PROPERTY_ENUM<PCB_BARCODE, BARCODE_ECC_T>( _HKI( "Error Correction" ),
&PCB_BARCODE::SetBarcodeErrorCorrection, &PCB_BARCODE::GetErrorCorrection ),
groupBarcode )
.SetAvailableFunc( isQRCode )
.SetChoicesFunc( []( INSPECTABLE* aItem )
{
PCB_BARCODE* barcode = static_cast<PCB_BARCODE*>( aItem );
wxPGChoices choices;
choices.Add( _( "L (Low)" ), static_cast<int>( BARCODE_ECC_T::L ) );
choices.Add( _( "M (Medium)" ), static_cast<int>( BARCODE_ECC_T::M ) );
choices.Add( _( "Q (Quartile)" ), static_cast<int>( BARCODE_ECC_T::Q ) );
// Only QR_CODE has High
if( barcode->GetKind() == BARCODE_T::QR_CODE )
choices.Add( _( "H (High)" ), static_cast<int>( BARCODE_ECC_T::H ) );
return choices;
} );
propMgr.AddProperty( new PROPERTY<PCB_BARCODE, bool>( _HKI( "Knockout" ),
&PCB_BARCODE::SetIsKnockout, &PCB_BARCODE::IsKnockout ), groupBarcode );
propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Margin X" ),
&PCB_BARCODE::SetMarginX, &PCB_BARCODE::GetMarginX,
PROPERTY_DISPLAY::PT_COORD ), groupBarcode ).SetAvailableFunc( hasKnockout );
propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Margin Y" ),
&PCB_BARCODE::SetMarginY, &PCB_BARCODE::GetMarginY,
PROPERTY_DISPLAY::PT_COORD ), groupBarcode ).SetAvailableFunc( hasKnockout );
}
} _PCB_BARCODE_DESC;
// wxAny conversion implementations for enum properties (declarations in header)
IMPLEMENT_ENUM_TO_WXANY( BARCODE_T );
IMPLEMENT_ENUM_TO_WXANY( BARCODE_ECC_T );