KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_barcode.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 Thomas Pointhuber <[email protected]>
5 * Copyright (C) 2020 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20 * or you may search the http://www.gnu.org website for the version 2 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <core/type_helpers.h>
26#include <bitmaps.h>
27#include <gr_basic.h>
28#include <macros.h>
29#include <pcb_edit_frame.h>
30#include <richio.h>
31#include <trigo.h>
32
33#include <base_units.h>
34#include <api/api_enums.h>
35#include <api/board/board_types.pb.h>
36#include <pcb_barcode.h>
37#include <board.h>
39#include <pcb_text.h>
40#include <math/util.h> // for KiROUND
42#include <wx/log.h>
43#include <pgm_base.h>
46#include <scoped_set_reset.h>
47#include <stdexcept>
48#include <utility>
49#include <algorithm>
50#include <api/api_utils.h>
51#include <footprint.h>
52
53#include <backend/zint.h>
55#include <hash.h>
56#include <google/protobuf/any.pb.h>
57#include <properties/property.h>
59
60constexpr int ECI_UTF8 = 26;
61
63 BOARD_ITEM( aParent, PCB_BARCODE_T ),
64 m_width( pcbIUScale.mmToIU( 40 ) ),
65 m_height( pcbIUScale.mmToIU( 40 ) ),
66 m_pos( 0, 0 ),
67 m_text( this ),
69 m_angle( 0 ),
71{
73}
74
75
79
80
82 BOARD_ITEM( aOther ),
83 m_width( aOther.m_width ),
84 m_height( aOther.m_height ),
85 m_pos( aOther.m_pos ),
86 m_margin( aOther.m_margin ),
87 m_text( aOther.m_text ),
88 m_kind( aOther.m_kind ),
89 m_angle( aOther.m_angle ),
91{
92 m_text.SetParent( this );
93}
94
95
97{
98 if( this != &aOther )
99 {
100 BOARD_ITEM::operator=( aOther );
101
102 m_width = aOther.m_width;
103 m_height = aOther.m_height;
104 m_pos = aOther.m_pos;
105 m_margin = aOther.m_margin;
106 m_text = aOther.m_text;
107 m_kind = aOther.m_kind;
108 m_angle = aOther.m_angle;
110
111 m_cache.reset();
112
113 m_text.SetParent( this );
114 }
115
116 return *this;
117}
118
119
121{
122 VECTOR2I delta = aPos - m_pos;
123 Move( delta );
124}
125
126
128{
129 return m_pos;
130}
131
132
133void PCB_BARCODE::SetText( const wxString& aNewText )
134{
135 m_text.SetText( aNewText );
136}
137
138
139wxString PCB_BARCODE::GetText() const
140{
141 return m_text.GetText();
142}
143
144
146{
147 return m_text.GetShownText( true );
148}
149
150
151void PCB_BARCODE::Serialize( google::protobuf::Any& aContainer ) const
152{
153 using namespace kiapi::board::types;
154
155 Barcode barcode;
156
157 barcode.mutable_id()->set_value( m_Uuid.AsStdString() );
158 barcode.set_text( GetText().ToUTF8() );
159
160 switch( m_kind )
161 {
162 case BARCODE_T::CODE_39: barcode.set_kind( BK_CODE_39 ); break;
163 case BARCODE_T::CODE_128: barcode.set_kind( BK_CODE_128 ); break;
164 case BARCODE_T::DATA_MATRIX: barcode.set_kind( BK_DATA_MATRIX ); break;
165 case BARCODE_T::QR_CODE: barcode.set_kind( BK_QR_CODE ); break;
166 case BARCODE_T::MICRO_QR_CODE: barcode.set_kind( BK_MICRO_QR_CODE ); break;
167 }
168
169 switch( m_errorCorrection )
170 {
171 case BARCODE_ECC_T::L: barcode.set_error_correction( BEC_L ); break;
172 case BARCODE_ECC_T::M: barcode.set_error_correction( BEC_M ); break;
173 case BARCODE_ECC_T::Q: barcode.set_error_correction( BEC_Q ); break;
174 case BARCODE_ECC_T::H: barcode.set_error_correction( BEC_H ); break;
175 }
176
177 kiapi::common::PackVector2( *barcode.mutable_position(), m_pos );
178 barcode.mutable_orientation()->set_value_degrees( m_angle.AsDegrees() );
179 barcode.set_layer( ToProtoEnum<PCB_LAYER_ID, BoardLayer>( GetLayer() ) );
180
181 barcode.mutable_width()->set_value_nm( m_width );
182 barcode.mutable_height()->set_value_nm( m_height );
183
184 barcode.set_show_text( m_text.IsVisible() );
185 barcode.mutable_text_height()->set_value_nm( m_text.GetTextHeight() );
186
187 barcode.set_knockout( IsKnockout() );
188 kiapi::common::PackVector2( *barcode.mutable_knockout_margin(), m_margin );
189
190 barcode.set_locked( IsLocked() ? kiapi::common::types::LockedState::LS_LOCKED
191 : kiapi::common::types::LockedState::LS_UNLOCKED );
192
193 aContainer.PackFrom( barcode );
194}
195
196
197bool PCB_BARCODE::Deserialize( const google::protobuf::Any& aContainer )
198{
199 using namespace kiapi::board::types;
200
201 Barcode barcode;
202
203 if( !aContainer.UnpackTo( &barcode ) )
204 return false;
205
206 SetUuidDirect( KIID( barcode.id().value() ) );
207 SetText( wxString::FromUTF8( barcode.text() ) );
208
209 switch( barcode.kind() )
210 {
211 case BK_CODE_39: SetKind( BARCODE_T::CODE_39 ); break;
212 case BK_CODE_128: SetKind( BARCODE_T::CODE_128 ); break;
213 case BK_DATA_MATRIX: SetKind( BARCODE_T::DATA_MATRIX ); break;
214 case BK_QR_CODE: SetKind( BARCODE_T::QR_CODE ); break;
215 case BK_MICRO_QR_CODE: SetKind( BARCODE_T::MICRO_QR_CODE ); break;
216 default: SetKind( BARCODE_T::QR_CODE ); break;
217 }
218
219 switch( barcode.error_correction() )
220 {
221 case BEC_L: SetErrorCorrection( BARCODE_ECC_T::L ); break;
222 case BEC_M: SetErrorCorrection( BARCODE_ECC_T::M ); break;
223 case BEC_Q: SetErrorCorrection( BARCODE_ECC_T::Q ); break;
224 case BEC_H: SetErrorCorrection( BARCODE_ECC_T::H ); break;
225 default: SetErrorCorrection( BARCODE_ECC_T::L ); break;
226 }
227
228 m_pos = kiapi::common::UnpackVector2( barcode.position() );
229 m_angle = EDA_ANGLE( barcode.orientation().value_degrees(), DEGREES_T );
231
232 m_width = barcode.width().value_nm();
233 m_height = barcode.height().value_nm();
234
235 m_text.SetLayer( m_layer );
236 m_text.SetVisible( barcode.show_text() );
237
238 if( barcode.has_text_height() )
239 {
240 int textSize = std::max( 1, static_cast<int>( barcode.text_height().value_nm() ) );
241 m_text.SetTextSize( VECTOR2I( textSize, textSize ) );
242 m_text.SetTextThickness( std::max( 1, GetPenSizeForNormal( m_text.GetTextHeight() ) ) );
243 }
244
245 m_margin = kiapi::common::UnpackVector2( barcode.knockout_margin() );
246 BOARD_ITEM::SetIsKnockout( barcode.knockout() );
247 SetLocked( barcode.locked() == kiapi::common::types::LockedState::LS_LOCKED );
248
250
251 return true;
252}
253
254
256{
257 m_layer = aLayer;
258 m_text.SetLayer( aLayer );
260}
261
262
263void PCB_BARCODE::SetTextSize( int aTextSize )
264{
265 m_text.SetTextSize( VECTOR2I( std::max( 1, aTextSize ), std::max( 1, aTextSize ) ) );
266 m_text.SetTextThickness( std::max( 1, GetPenSizeForNormal( m_text.GetTextHeight() ) ) );
268}
269
270
272{
273 return m_text.GetTextHeight();
274}
275
276
277void PCB_BARCODE::Move( const VECTOR2I& offset )
278{
279 m_pos += offset;
280 m_text.Move( offset );
281
282 if( m_cache )
283 {
284 m_cache->symbolPoly.Move( offset );
285 m_cache->textPoly.Move( offset );
286 m_cache->poly.Move( offset );
287 m_cache->bbox.Move( offset );
288 m_cache->keyHash = computeCacheKey();
289 }
290}
291
292
293void PCB_BARCODE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
294{
295 RotatePoint( m_pos, aRotCentre, aAngle );
296 m_angle += aAngle;
298}
299
300
301void PCB_BARCODE::Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
302{
303 MIRROR( m_pos, aCentre, aFlipDirection );
304
305 if( aFlipDirection == FLIP_DIRECTION::TOP_BOTTOM )
307
310}
311
312
313void PCB_BARCODE::StyleFromSettings( const BOARD_DESIGN_SETTINGS& settings, bool aCheckSide )
314{
315 SetTextSize( settings.GetTextSize( GetLayer() ).y );
316}
317
318
320{
322 static_cast<int>( m_kind ), m_angle.AsDegrees(), static_cast<int>( m_errorCorrection ),
323 m_text.IsVisible(), m_text.GetTextHeight(), IsKnockout(), static_cast<int>( m_layer ) );
324}
325
326
328{
329 size_t key = computeCacheKey();
330
331 if( m_cache && m_cache->keyHash == key )
332 return;
333
334 if( !m_cache )
335 m_cache = std::make_unique<PCB_BARCODE_CACHE>();
336
338
339 // Scale the symbol polygon to the desired barcode width/height and center it at m_pos
341
343
344 // Build full m_poly from symbol + optional text, then apply knockout if requested
345 m_cache->poly.RemoveAllContours();
346 m_cache->poly.Append( m_cache->symbolPoly );
347
348 if( m_text.IsVisible() && m_cache->textPoly.OutlineCount() )
349 m_cache->poly.Append( m_cache->textPoly );
350
351 m_cache->poly.Fracture();
352
353 if( IsKnockout() )
354 {
355 // Enforce minimum margin: at least 10% of the smallest side of the barcode, rounded up
356 // to the nearest 0.1 mm. Use this as a lower bound for both axes.
357 int minSide = std::min( m_width, m_height );
358 int tenPercent = ( minSide + 9 ) / 10; // ceil(minSide * 0.1)
359 int step01mm = std::max( 1, pcbIUScale.mmToIU( 0.1 ) );
360 int tenPercentRounded = ( ( tenPercent + step01mm - 1 ) / step01mm ) * step01mm;
361
362 // Build inversion rectangle based on the local bbox of the current combined geometry
363 BOX2I bbox = m_cache->poly.BBox();
364 bbox.Inflate( std::max( m_margin.x, tenPercentRounded ), std::max( m_margin.y, tenPercentRounded ) );
365
366 SHAPE_LINE_CHAIN rect;
367 rect.Append( bbox.GetLeft(), bbox.GetTop() );
368 rect.Append( bbox.GetRight(), bbox.GetTop() );
369 rect.Append( bbox.GetRight(), bbox.GetBottom() );
370 rect.Append( bbox.GetLeft(), bbox.GetBottom() );
371 rect.SetClosed( true );
372
374 ko.AddOutline( rect );
375 ko.BooleanSubtract( m_cache->poly );
376 ko.Fracture();
377 m_cache->poly = std::move( ko );
378 }
379
382
383 if( !m_angle.IsZero() )
384 m_cache->poly.Rotate( m_angle, m_pos );
385
386 m_cache->poly.CacheTriangulation();
387 m_cache->bbox = m_cache->poly.BBox();
388 m_cache->keyHash = key;
389}
390
391
393{
394 m_cache->textPoly.RemoveAllContours();
395
396 if( !m_text.IsVisible() )
397 return;
398
399 SHAPE_POLY_SET textPoly;
400 m_text.TransformTextToPolySet( textPoly, 0, GetMaxError(), ERROR_INSIDE );
401
402 if( textPoly.OutlineCount() == 0 )
403 return;
404
405 if( m_cache->symbolPoly.OutlineCount() == 0 )
406 return;
407
408 BOX2I textBBox = textPoly.BBox();
409 BOX2I symbolBBox = m_cache->symbolPoly.BBox();
410 VECTOR2I textPos;
411 int textOffset = pcbIUScale.mmToIU( 1 );
412 textPos.x = symbolBBox.GetCenter().x - textBBox.GetCenter().x;
413 textPos.y = symbolBBox.GetBottom() - textBBox.GetTop() + textOffset;
414
415 textPoly.Move( textPos );
416
417 m_cache->textPoly = std::move( textPoly );
418 m_cache->textPoly.CacheTriangulation();
419}
420
421
423{
424 m_cache->symbolPoly.RemoveAllContours();
425 m_cache->lastError.clear();
426
427 std::unique_ptr<zint_symbol, decltype( &ZBarcode_Delete )> symbol( ZBarcode_Create(), &ZBarcode_Delete );
428
429 if( !symbol )
430 {
431 wxLogError( wxT( "Zint: failed to allocate symbol" ) );
432 return;
433 }
434
435 symbol->input_mode = UNICODE_MODE;
436 symbol->show_hrt = 0; // do not show HRT
437
438 switch( m_kind )
439 {
441 symbol->symbology = BARCODE_CODE39;
442 break;
444 symbol->symbology = BARCODE_CODE128;
445 break;
447 symbol->symbology = BARCODE_QRCODE;
448 symbol->option_1 = to_underlying( m_errorCorrection );
449 break;
451 symbol->symbology = BARCODE_MICROQR;
452 symbol->option_1 = to_underlying( m_errorCorrection );
453 break;
455 symbol->symbology = BARCODE_DATAMATRIX;
456 break;
457 default:
458 wxLogError( wxT( "Zint: invalid barcode type" ) );
459 return;
460 }
461
462 wxString text = GetShownText();
463 wxScopedCharBuffer utf8Text = text.ToUTF8();
464 size_t length = utf8Text.length();
465 unsigned char* dataPtr = reinterpret_cast<unsigned char*>( utf8Text.data() );
466
467 if( text.empty() )
468 return;
469
470 if( ( m_kind == BARCODE_T::QR_CODE || m_kind == BARCODE_T::DATA_MATRIX ) && !text.IsAscii() )
471 {
472 symbol->eci = ECI_UTF8;
473 }
474
475 if( ZBarcode_Encode( symbol.get(), dataPtr, length ) >= ZINT_ERROR )
476 {
477 if( !text.IsAscii() )
478 {
479 m_cache->lastError = _( "This barcode type does not support international "
480 "characters. Use QR Code or Data Matrix instead." );
481 }
482 else
483 {
484 m_cache->lastError = wxString::FromUTF8( symbol->errtxt );
485 }
486 return;
487 }
488
489 if( ZBarcode_Buffer_Vector( symbol.get(), 0 ) >= ZINT_ERROR )
490 {
491 m_cache->lastError = wxString::FromUTF8( symbol->errtxt );
492 return;
493 }
494
495 for( zint_vector_rect* rect = symbol->vector->rectangles; rect != nullptr; rect = rect->next )
496 {
497 // Round using absolute edges to avoid cumulative rounding drift across modules.
498 int x1 = KiROUND( rect->x * symbol->scale );
499 int x2 = KiROUND( ( rect->x + rect->width ) * symbol->scale );
500 int y1 = KiROUND( rect->y * symbol->scale );
501 int y2 = KiROUND( ( rect->y + rect->height ) * symbol->scale );
502
503 SHAPE_LINE_CHAIN shapeline;
504 shapeline.Append( x1, y1 );
505 shapeline.Append( x2, y1 );
506 shapeline.Append( x2, y2 );
507 shapeline.Append( x1, y2 );
508 shapeline.SetClosed( true );
509
510 m_cache->symbolPoly.AddOutline( shapeline );
511 }
512
513 for( zint_vector_hexagon* hex = symbol->vector->hexagons; hex != nullptr; hex = hex->next )
514 {
515 // Compute vertices from center using minimal-diameter (inscribed circle) radius.
516 double r = hex->diameter / 2.0; // minimal radius
517 double cx = hex->x;
518 double cy = hex->y;
519
520 // Base orientation has apex at top; hex->rotation rotates by 0/90/180/270 degrees.
521 double baseAngles[6] = { 90.0, 30.0, -30.0, -90.0, -150.0, 150.0 };
522 double rot = static_cast<double>( hex->rotation );
523
524 SHAPE_LINE_CHAIN poly;
525
526 for( int k = 0; k < 6; ++k )
527 {
528 double ang = ( baseAngles[k] + rot ) * M_PI / 180.0;
529 int vx = KiROUND( cx + r * cos( ang ) );
530 int vy = KiROUND( cy + r * sin( ang ) );
531 poly.Append( vx, vy );
532 }
533 poly.SetClosed( true );
534
535 m_cache->symbolPoly.AddOutline( poly );
536 }
537
538 // Set the position of the barcode to the center of the symbol polygon
539 if( m_cache->symbolPoly.OutlineCount() > 0 )
540 {
541 VECTOR2I pos = m_cache->symbolPoly.BBox().GetCenter();
542 m_cache->symbolPoly.Move( -pos );
543 }
544
545 m_cache->symbolPoly.CacheTriangulation();
546}
547
548
549void PCB_BARCODE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
550{
551 FOOTPRINT* parentFP = GetParentFootprint();
552
553 if( parentFP && aFrame->GetName() == PCB_EDIT_FRAME_NAME )
554 aList.emplace_back( _( "Footprint" ), parentFP->GetReference() );
555
556 aList.emplace_back( _( "Barcode" ), ENUM_MAP<BARCODE_T>::Instance().ToString( m_kind ) );
557
558 // Don't use GetShownText() here; we want to show the user the variable references
559 aList.emplace_back( _( "Text" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
560
561 if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
562 aList.emplace_back( _( "Status" ), _( "Locked" ) );
563
564 aList.emplace_back( _( "Layer" ), GetLayerName() );
565
566 aList.emplace_back( _( "Angle" ), wxString::Format( wxT( "%g" ), m_angle.AsDegrees() ) );
567
568 aList.emplace_back( _( "Text Height" ), aFrame->MessageTextFromValue( m_text.GetTextHeight() ) );
569}
570
571
572bool PCB_BARCODE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
573{
575
576 if( !GetBoundingBox().Contains( aPosition ) )
577 return false;
578
579 SHAPE_POLY_SET hulls;
580
582
583 return hulls.Collide( aPosition );
584}
585
586
587bool PCB_BARCODE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
588{
589 BOX2I arect = aRect;
590 arect.Inflate( aAccuracy );
591
592 BOX2I rect = GetBoundingBox();
593
594 if( aAccuracy )
595 rect.Inflate( aAccuracy );
596
597 if( aContained )
598 return arect.Contains( rect );
599
600 return arect.Intersects( rect );
601}
602
603
604void PCB_BARCODE::rescaleSymbolPoly( const VECTOR2I& aTopLeft, const VECTOR2I& aBotRight ) const
605{
606 // Rescale only the symbol polygon to the requested rectangle
607 BOX2I bbox = m_cache->symbolPoly.BBox();
608 int oldW = bbox.GetWidth();
609 int oldH = bbox.GetHeight();
610
611 VECTOR2I newPosition = ( aTopLeft + aBotRight ) / 2;
612 int newW = aBotRight.x - aTopLeft.x;
613 int newH = aBotRight.y - aTopLeft.y;
614 // Guard against zero/negative sizes from interactive edits; enforce a tiny minimum
615 int minIU = std::max( 1, pcbIUScale.mmToIU( 0.01 ) );
616 newW = std::max( newW, minIU );
617 newH = std::max( newH, minIU );
618
619 double scaleX = oldW ? static_cast<double>( newW ) / oldW : 1.0;
620 double scaleY = oldH ? static_cast<double>( newH ) / oldH : 1.0;
621
622 VECTOR2I oldCenter = bbox.GetCenter();
623 m_cache->symbolPoly.Scale( scaleX, scaleY, oldCenter );
624
625 // After scaling, move the symbol polygon to be centered at the new position
626 VECTOR2I newCenter = m_cache->symbolPoly.BBox().GetCenter();
627 VECTOR2I delta = newPosition - newCenter;
628
629 if( delta != VECTOR2I( 0, 0 ) )
630 m_cache->symbolPoly.Move( delta );
631}
632
633
635{
636 if( !m_cache )
638 return m_cache->bbox;
639}
640
641
642wxString PCB_BARCODE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
643{
644 return wxString::Format( _( "Barcode '%s' on %s" ), GetText(), GetLayerName() );
645}
646
647
652
653
655{
656 if( !m_cache )
658 return m_cache->bbox;
659}
660
661
663 int aClearance, int aMaxError,
664 ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
665{
666 if( aLayer != m_layer && aLayer != UNDEFINED_LAYER )
667 return;
668
670
671 if( aClearance == 0 )
672 {
673 aBuffer.Append( m_cache->poly );
674 }
675 else
676 {
677 SHAPE_POLY_SET poly = m_cache->poly;
678 poly.Inflate( aClearance, CORNER_STRATEGY::CHAMFER_ACUTE_CORNERS, aMaxError, aErrorLoc );
679 aBuffer.Append( poly );
680 }
681}
682
683
684std::shared_ptr<SHAPE> PCB_BARCODE::GetEffectiveShape( PCB_LAYER_ID aLayer, FLASHING aFlash ) const
685{
686 SHAPE_POLY_SET poly;
687 TransformShapeToPolygon( poly, aLayer, 0, 0, ERROR_INSIDE, true );
688
689 return std::make_shared<SHAPE_POLY_SET>( std::move( poly ) );
690}
691
692
693void PCB_BARCODE::GetBoundingHull( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
694 int aMaxError, ERROR_LOC aErrorLoc ) const
695{
697
698 auto getBoundingHull =
699 [this]( SHAPE_POLY_SET& aLocBuffer, const SHAPE_POLY_SET& aSource, int aLocClearance )
700 {
701 BOX2I rect = aSource.BBox( aLocClearance );
702 VECTOR2I corners[4];
703
704 corners[0].x = rect.GetOrigin().x;
705 corners[0].y = rect.GetOrigin().y;
706 corners[1].y = corners[0].y;
707 corners[1].x = rect.GetRight();
708 corners[2].x = corners[1].x;
709 corners[2].y = rect.GetBottom();
710 corners[3].y = corners[2].y;
711 corners[3].x = corners[0].x;
712
713 aLocBuffer.NewOutline();
714
715 for( VECTOR2I& corner : corners )
716 {
717 RotatePoint( corner, m_pos, m_angle );
718 aLocBuffer.Append( corner.x, corner.y );
719 }
720 };
721
722 if( aLayer == m_layer || aLayer == UNDEFINED_LAYER )
723 {
724 getBoundingHull( aBuffer, m_cache->symbolPoly, aClearance );
725 getBoundingHull( aBuffer, m_cache->textPoly, aClearance );
726 }
727}
728
729
731{
732 // Micro QR codes do not support High (H) error correction level
733 if( m_kind == BARCODE_T::MICRO_QR_CODE && aErrorCorrection == BARCODE_ECC_T::H )
735 else
736 m_errorCorrection = aErrorCorrection;
737 // Don't auto-compute here as it may be called during loading
738}
739
740
742{
743 m_kind = aKind;
744
745 // When switching to Micro QR, validate and adjust ECC if needed
748
749 // Don't auto-compute here as it may be called during loading
750}
751
752
754{
755 SetErrorCorrection( aErrorCorrection );
757}
758
759
761{
762 m_width = aWidth;
763
764 if( KeepSquare() )
765 m_height = aWidth;
766
768}
769
770
772{
773 m_height = aHeight;
774
775 if( KeepSquare() )
776 m_width = aHeight;
777
779}
780
781
783{
784 SetKind( aKind );
786}
787
788
790{
791 PCB_BARCODE* item = new PCB_BARCODE( *this );
792 item->CopyFrom( this );
793 return item;
794}
795
796
798{
799 wxCHECK_RET( aImage && aImage->Type() == PCB_BARCODE_T,
800 wxT( "Cannot swap data with non-barcode item." ) );
801
802 PCB_BARCODE* other = static_cast<PCB_BARCODE*>( aImage );
803
804 std::swap( m_layer, other->m_layer );
805 std::swap( m_isKnockout, other->m_isKnockout );
806 std::swap( m_isLocked, other->m_isLocked );
807 std::swap( m_width, other->m_width );
808 std::swap( m_height, other->m_height );
809 std::swap( m_pos, other->m_pos );
810 std::swap( m_margin, other->m_margin );
811 std::swap( m_text, other->m_text );
812 std::swap( m_kind, other->m_kind );
813 std::swap( m_angle, other->m_angle );
814 std::swap( m_errorCorrection, other->m_errorCorrection );
815 std::swap( m_cache, other->m_cache );
816
817 m_text.SetParent( this );
818 other->m_text.SetParent( other );
819}
820
821double PCB_BARCODE::Similarity( const BOARD_ITEM& aItem ) const
822{
823 if( !ClassOf( &aItem ) )
824 return 0.0;
825
826 const PCB_BARCODE* other = static_cast<const PCB_BARCODE*>( &aItem );
827
828 // Compare text, width, height, text height, position, and kind
829 double similarity = 0.0;
830 const double weight = 1.0 / 6.0;
831
832 if( GetText() == other->GetText() )
833 similarity += weight;
834 if( m_width == other->m_width )
835 similarity += weight;
836 if( m_height == other->m_height )
837 similarity += weight;
838 if( GetTextSize() == other->GetTextSize() )
839 similarity += weight;
840 if( GetPosition() == other->GetPosition() )
841 similarity += weight;
842 if( m_kind == other->m_kind )
843 similarity += weight;
844
845 return similarity;
846}
847
848int PCB_BARCODE::Compare( const PCB_BARCODE* aBarcode, const PCB_BARCODE* aOther )
849{
850 int diff;
851
852 if( ( diff = aBarcode->GetPosition().x - aOther->GetPosition().x ) != 0 )
853 return diff;
854
855 if( ( diff = aBarcode->GetPosition().y - aOther->GetPosition().y ) != 0 )
856 return diff;
857
858 if( ( diff = aBarcode->GetText().Cmp( aOther->GetText() ) ) != 0 )
859 return diff;
860
861 if( ( diff = aBarcode->GetWidth() - aOther->GetWidth() ) != 0 )
862 return diff;
863
864 if( ( diff = aBarcode->GetHeight() - aOther->GetHeight() ) != 0 )
865 return diff;
866
867 if( ( diff = aBarcode->GetTextSize() - aOther->GetTextSize() ) != 0 )
868 return diff;
869
870 if( ( diff = (int) aBarcode->GetKind() - (int) aOther->GetKind() ) != 0 )
871 return diff;
872
873 if( ( diff = aBarcode->m_angle.AsTenthsOfADegree() - aOther->m_angle.AsTenthsOfADegree() ) != 0 )
874 return diff;
875
876 if( ( diff = (int) aBarcode->GetErrorCorrection() - (int) aOther->GetErrorCorrection() ) != 0 )
877 return diff;
878
879 return 0;
880}
881
882
883bool PCB_BARCODE::operator==( const BOARD_ITEM& aItem ) const
884{
885 if( !ClassOf( &aItem ) )
886 return false;
887
888 const PCB_BARCODE& other = static_cast<const PCB_BARCODE&>( aItem );
889
890 return *this == other;
891}
892
893
894bool PCB_BARCODE::operator==( const PCB_BARCODE& aOther ) const
895{
896 // Compare text, width, height, text height, position, and kind
897 return ( GetText() == aOther.GetText()
898 && m_width == aOther.m_width
899 && m_height == aOther.m_height
900 && GetTextSize() == aOther.GetTextSize()
901 && GetPosition() == aOther.GetPosition()
902 && m_kind == aOther.m_kind );
903}
904
905// ---- Property registration ----
906static struct PCB_BARCODE_DESC
907{
909 {
913
914 const wxString groupBarcode = _HKI( "Barcode Properties" );
915
917 if( kindMap.Choices().GetCount() == 0 )
918 {
919 kindMap.Undefined( BARCODE_T::QR_CODE );
920 kindMap.Map( BARCODE_T::CODE_39, _HKI( "CODE_39" ) )
921 .Map( BARCODE_T::CODE_128, _HKI( "CODE_128" ) )
922 .Map( BARCODE_T::DATA_MATRIX, _HKI( "DATA_MATRIX" ) )
923 .Map( BARCODE_T::QR_CODE, _HKI( "QR_CODE" ) )
924 .Map( BARCODE_T::MICRO_QR_CODE, _HKI( "MICRO_QR_CODE" ) );
925 }
926
928 if( eccMap.Choices().GetCount() == 0 )
929 {
930 eccMap.Undefined( BARCODE_ECC_T::L );
931 eccMap.Map( BARCODE_ECC_T::L, _HKI( "L (Low)" ) )
932 .Map( BARCODE_ECC_T::M, _HKI( "M (Medium)" ) )
933 .Map( BARCODE_ECC_T::Q, _HKI( "Q (Quartile)" ) )
934 .Map( BARCODE_ECC_T::H, _HKI( "H (High)" ) );
935 }
936
937 auto hasKnockout =
938 []( INSPECTABLE* aItem ) -> bool
939 {
940 if( PCB_BARCODE* bc = dynamic_cast<PCB_BARCODE*>( aItem ) )
941 return bc->IsKnockout();
942 return false;
943 };
944
945 propMgr.AddProperty( new PROPERTY<PCB_BARCODE, wxString>( _HKI( "Text" ),
947
948 propMgr.AddProperty( new PROPERTY<PCB_BARCODE, bool>( _HKI( "Show Text" ),
950
951 propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Text Size" ),
953 PROPERTY_DISPLAY::PT_COORD ), groupBarcode );
954
955 propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Width" ),
957 PROPERTY_DISPLAY::PT_COORD ), groupBarcode );
958
959 propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Height" ),
961 PROPERTY_DISPLAY::PT_COORD ), groupBarcode );
962
963 propMgr.AddProperty( new PROPERTY<PCB_BARCODE, double>( _HKI( "Orientation" ),
965
966 propMgr.AddProperty( new PROPERTY_ENUM<PCB_BARCODE, BARCODE_T>( _HKI( "Barcode Type" ),
968
969 auto isQRCode =
970 []( INSPECTABLE* aItem ) -> bool
971 {
972 if( PCB_BARCODE* bc = dynamic_cast<PCB_BARCODE*>( aItem ) )
973 return bc->GetKind() == BARCODE_T::QR_CODE || bc->GetKind() == BARCODE_T::MICRO_QR_CODE;
974
975 return false;
976 };
977
978 propMgr.AddProperty( new PROPERTY_ENUM<PCB_BARCODE, BARCODE_ECC_T>( _HKI( "Error Correction" ),
980 groupBarcode )
981 .SetAvailableFunc( isQRCode )
982 .SetChoicesFunc( []( INSPECTABLE* aItem )
983 {
984 PCB_BARCODE* barcode = static_cast<PCB_BARCODE*>( aItem );
985 wxPGChoices choices;
986
987 choices.Add( _( "L (Low)" ), static_cast<int>( BARCODE_ECC_T::L ) );
988 choices.Add( _( "M (Medium)" ), static_cast<int>( BARCODE_ECC_T::M ) );
989 choices.Add( _( "Q (Quartile)" ), static_cast<int>( BARCODE_ECC_T::Q ) );
990
991 // Only QR_CODE has High
992 if( barcode->GetKind() == BARCODE_T::QR_CODE )
993 choices.Add( _( "H (High)" ), static_cast<int>( BARCODE_ECC_T::H ) );
994
995 return choices;
996 } );
997
998 propMgr.AddProperty( new PROPERTY<PCB_BARCODE, bool>( _HKI( "Knockout" ),
1000
1001 propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Margin X" ),
1003 PROPERTY_DISPLAY::PT_COORD ), groupBarcode ).SetAvailableFunc( hasKnockout );
1004
1005 propMgr.AddProperty( new PROPERTY<PCB_BARCODE, int>( _HKI( "Margin Y" ),
1007 PROPERTY_DISPLAY::PT_COORD ), groupBarcode ).SetAvailableFunc( hasKnockout );
1008 }
1010
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:44
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
constexpr int ARC_LOW_DEF
Definition base_units.h:140
BITMAPS
A list of all bitmap identifiers.
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
Container for design settings for a BOARD object.
VECTOR2I GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
BOARD_ITEM(BOARD_ITEM *aParent, KICAD_T idtype, PCB_LAYER_ID aLayer=F_Cu)
Definition board_item.h:86
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:268
void SetUuidDirect(const KIID &aUuid)
Raw UUID assignment.
void SetLocked(bool aLocked) override
Definition board_item.h:359
bool m_isKnockout
Definition board_item.h:491
PCB_LAYER_ID m_layer
Definition board_item.h:490
bool m_isLocked
Definition board_item.h:492
bool IsLocked() const override
virtual void SetIsKnockout(bool aKnockout)
Definition board_item.h:356
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
FOOTPRINT * GetParentFootprint() const
virtual void CopyFrom(const BOARD_ITEM *aOther)
BOARD_ITEM & operator=(const BOARD_ITEM &aOther)
Definition board_item.h:103
bool IsSideSpecific() const
wxString GetLayerName() const
Return the name of the PCB layer on which the item resides.
int GetMaxError() const
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:558
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr const Vec GetCenter() const
Definition box2.h:230
constexpr size_type GetHeight() const
Definition box2.h:215
constexpr coord_type GetLeft() const
Definition box2.h:228
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:168
constexpr const Vec & GetOrigin() const
Definition box2.h:210
constexpr coord_type GetRight() const
Definition box2.h:217
constexpr coord_type GetTop() const
Definition box2.h:229
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition box2.h:311
constexpr coord_type GetBottom() const
Definition box2.h:222
int AsTenthsOfADegree() const
Definition eda_angle.h:118
The base class for create windows for drawing purpose.
const KIID m_Uuid
Definition eda_item.h:528
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:112
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.cpp:93
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:41
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition property.h:727
static ENUM_MAP< T > & Instance()
Definition property.h:721
ENUM_MAP & Undefined(T aValue)
Definition property.h:734
wxPGChoices & Choices()
Definition property.h:770
const wxString & GetReference() const
Definition footprint.h:829
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:38
Definition kiid.h:48
void SetKind(BARCODE_T aKind)
void SetTextSize(int aTextSize)
Change the height of the human-readable text displayed below the barcode.
double GetOrientation() const
std::unique_ptr< PCB_BARCODE_CACHE > m_cache
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
~PCB_BARCODE()
Destructor.
VECTOR2I m_pos
Position of the barcode.
const BOX2I GetBoundingBox() const override
Get the axis-aligned bounding box of the barcode including text.
void SetBarcodeErrorCorrection(BARCODE_ECC_T aErrorCorrection)
virtual const BOX2I ViewBBox() const override
Get the bbox used for drawing/view culling, may include additional view-only extents.
EDA_ITEM * Clone() const override
Create a copy of this item.
void SetErrorCorrection(BARCODE_ECC_T aErrorCorrection)
Set the error correction level used for QR codes.
void SetBarcodeHeight(int aHeight)
void SetShowText(bool aShow)
EDA_ANGLE m_angle
void SetMarginY(int aY)
void StyleFromSettings(const BOARD_DESIGN_SETTINGS &settings, bool aCheckSide) override
bool KeepSquare() const
void SetBarcodeText(const wxString &aText)
wxString GetItemDescription(UNITS_PROVIDER *aUnitsProvider, bool aFull) const override
Produce a short human-readable description of the item for UI lists.
void AssembleBarcode() const
Assemble the barcode polygon and text polygons into a single polygonal representation.
void SetBarcodeKind(BARCODE_T aKind)
void GetBoundingHull(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE) const
PCB_BARCODE & operator=(const PCB_BARCODE &aOther)
Copy assignment operator.
VECTOR2I GetPosition() const override
Get the position (center) of the barcode in internal units.
void ComputeTextPoly() const
Generate the internal polygon representation for the human-readable text.
wxString GetText() const
void SetPosition(const VECTOR2I &aPos) override
void SetMarginX(int aX)
void SetOrientation(double aDegrees)
double Similarity(const BOARD_ITEM &aItem) const override
Compute a simple similarity score between this barcode and another board item.
int m_height
Barcode height.
PCB_TEXT m_text
void SetLayer(PCB_LAYER_ID aLayer) override
Set the drawing layer for the barcode and its text.
bool operator==(const BOARD_ITEM &aItem) const override
Equality comparison operator for board-level deduplication.
VECTOR2I m_margin
Margin around the barcode (only valid for knockout)
void GetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList) override
Populate message panel information entries (e.g.
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
int GetTextSize() const
void ComputeBarcode() const
Generate the internal polygon representation for the current barcode text, kind and error correction.
void SetBarcodeWidth(int aWidth)
bool IsKnockout() const override
int GetHeight() const
Get the barcode height (in internal units).
BITMAPS GetMenuImage() const override
Icon to show in context menus/toolbars for this item type.
wxString GetShownText() const
size_t computeCacheKey() const
Compute a hash of all cache-key inputs (shown text + geometry parameters + layer).
BARCODE_ECC_T m_errorCorrection
Error correction level for QR codes.
void SetIsKnockout(bool aEnable) override
static bool ClassOf(const EDA_ITEM *aItem)
Type-check helper.
void rescaleSymbolPoly(const VECTOR2I &aTopLeft, const VECTOR2I &aBotRight) const
Scale and translate the symbol polygon to fill the given bounding rectangle.
bool HitTest(const VECTOR2I &aPosition, int aAccuracy) const override
Hit-test a point against the barcode (text and symbol area).
PCB_BARCODE(BOARD_ITEM *aParent)
Construct a PCB_BARCODE.
int m_width
Barcode width.
int GetMarginY() const
void swapData(BOARD_ITEM *aImage) override
BARCODE_ECC_T GetErrorCorrection() const
bool GetShowText() const
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate the barcode around a given centre by the given angle.
int GetMarginX() const
BARCODE_T m_kind
static int Compare(const PCB_BARCODE *aBarcode, const PCB_BARCODE *aOther)
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, PCB_LAYER_ID aLayer, int aClearance, int aMaxError, ERROR_LOC aErrorLoc=ERROR_INSIDE, bool ignoreLineWidth=false) const override
Convert the barcode (text + symbol shapes) to polygonal geometry suitable for filling/collision tests...
void Flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipLeftRight) override
Flip the barcode horizontally or vertically around a centre point.
BARCODE_T GetKind() const
Returns the type of the barcode (QR, CODE_39, etc.).
int GetWidth() const
Get the barcode width (in internal units).
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
void SetText(const wxString &aText)
Set the barcode content text to encode.
void Move(const VECTOR2I &offset) override
Function Move.
PROPERTY_BASE & SetChoicesFunc(std::function< wxPGChoices(INSPECTABLE *)> aFunc)
Definition property.h:276
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:262
Provide class metadata.Helper macro to map type hashes to names.
void InheritsAfter(TYPE_ID aDerived, TYPE_ID aBase)
Declare an inheritance relationship between types.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
int NewOutline()
Creates a new empty polygon in the set and returns its index.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
@ CHAMFER_ACUTE_CORNERS
Acute angles are chamfered.
#define _(s)
@ DEGREES_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_180
Definition eda_angle.h:415
#define PCB_EDIT_FRAME_NAME
int GetPenSizeForNormal(int aTextSize)
Definition gr_text.cpp:61
static constexpr std::size_t hash_val(const Types &... args)
Definition hash.h:51
PCB_LAYER_ID FlipLayer(PCB_LAYER_ID aLayerId, int aCopperLayersCount)
Definition layer_id.cpp:173
FLASHING
Enum used during connectivity building to ensure we do not query connectivity while building the data...
Definition layer_ids.h:184
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition layer_ids.h:805
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:60
@ Dwgs_User
Definition layer_ids.h:107
@ UNDEFINED_LAYER
Definition layer_ids.h:61
This file contains miscellaneous commonly used macros and functions.
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition mirror.h:45
FLIP_DIRECTION
Definition mirror.h:27
@ LEFT_RIGHT
Flip left to right (around the Y axis)
Definition mirror.h:28
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
Definition mirror.h:29
KICOMMON_API wxString EllipsizeStatusText(wxWindow *aWindow, const wxString &aString)
Ellipsize text (at the end) to be no more than 1/3 of the window width.
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput, const EDA_IU_SCALE &aScale)
#define _HKI(x)
Definition page_info.cpp:44
static struct PCB_BARCODE_DESC _PCB_BARCODE_DESC
constexpr int ECI_UTF8
BARCODE class definition.
BARCODE_ECC_T
Definition pcb_barcode.h:53
BARCODE_T
Definition pcb_barcode.h:44
see class PGM_BASE
#define TYPE_HASH(x)
Definition property.h:74
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition property.h:821
@ PT_COORD
Coordinate expressed in distance units (mm/inch)
Definition property.h:65
#define REGISTER_TYPE(x)
int delta
#define M_PI
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:229
constexpr auto to_underlying(E e) noexcept
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:98
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687