KiCad PCB EDA Suite
Loading...
Searching...
No Matches
easypc_sch_items.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <cmath>
23#include <map>
24#include <set>
25
33
34#include <base_units.h>
35#include <eda_text.h>
36#include <font/font.h>
37#include <ki_exception.h>
38#include <lib_id.h>
39#include <lib_symbol.h>
40#include <sch_field.h>
41#include <sch_pin.h>
42#include <sch_shape.h>
43#include <sch_text.h>
44#include <string_utils.h>
45
46
47namespace EASYPC
48{
49
50std::vector<const SOURCE_SEGMENT*> ShapeVertices( const SOURCE_SHAPE& aShape )
51{
52 std::vector<const SOURCE_SEGMENT*> out;
53 const SOURCE_SEGMENT* first =
54 aShape.Segments.empty() ? nullptr : dynamic_cast<const SOURCE_SEGMENT*>( aShape.Segments[0] );
55
56 if( first && first->HasNextRef )
57 {
58 // Formats 1 and 2 link the vertices; a closed outline links the last back to the first
59 std::set<const SOURCE_SEGMENT*> seen;
60
61 for( const SOURCE_SEGMENT* seg = first; seg && seen.insert( seg ).second; seg = seg->Next )
62 out.push_back( seg );
63 }
64 else if( first )
65 {
66 for( OBJECT* obj : aShape.Segments )
67 {
68 if( const SOURCE_SEGMENT* seg = dynamic_cast<const SOURCE_SEGMENT*>( obj ) )
69 out.push_back( seg );
70 }
71 }
72
73 return out;
74}
75
76
77VECTOR2I SCH_FRAME::Map( int64_t aX, int64_t aY ) const
78{
79 int64_t x = aX - OriginX;
80 int64_t y = OriginY - aY;
81
82 if( std::abs( x ) > COORD_LIMIT || std::abs( y ) > COORD_LIMIT )
83 {
84 THROW_IO_ERROR( wxString::Format( _( "Easy-PC point (%lld, %lld) is outside KiCad's coordinate range" ),
85 static_cast<long long>( aX ), static_cast<long long>( aY ) ) );
86 }
87
88 return VECTOR2I( static_cast<int>( x ), static_cast<int>( y ) );
89}
90
91
92static VECTOR2D rotateAbout( const VECTOR2D& aPt, const VECTOR2D& aCentre, double aRadians )
93{
94 VECTOR2D r = aPt - aCentre;
95 double c = std::cos( aRadians );
96 double s = std::sin( aRadians );
97
98 return aCentre + VECTOR2D( r.x * c - r.y * s, r.x * s + r.y * c );
99}
100
101
103static VECTOR2D arcCentre( const VECTOR2D& aStart, const VECTOR2D& aEnd, int32_t aAngle, bool aAnticlockwise )
104{
105 VECTOR2D mid = ( aStart + aEnd ) / 2.0;
106 double t = std::tan( ( aAngle * M_PI / 180000.0 ) * ( aAnticlockwise ? 1 : -1 ) / 2.0 );
107
108 if( std::abs( t ) < 1e-12 )
109 return mid;
110
111 return VECTOR2D( mid.x - ( aEnd.y - aStart.y ) / ( 2 * t ), mid.y + ( aEnd.x - aStart.x ) / ( 2 * t ) );
112}
113
114
115static VECTOR2I mapD( const SCH_FRAME& aFrame, const VECTOR2D& aPt )
116{
117 return aFrame.Map( KiROUND<double, int64_t>( aPt.x ), KiROUND<double, int64_t>( aPt.y ) );
118}
119
120
122static std::vector<VECTOR2D> arcAsPolyline( const VECTOR2D& aStart, const VECTOR2D& aEnd, const VECTOR2D& aCentre,
123 int32_t aAngle, bool aAnticlockwise )
124{
125 double theta = std::abs( aAngle * M_PI / 180000.0 );
126 double radius = ( aStart - aCentre ).EuclideanNorm();
127
128 if( radius * ( 1.0 - std::cos( theta / 2.0 ) ) < 1.0 )
129 return { aEnd };
130
131 double step = 2.0 * std::acos( std::max( -1.0, 1.0 - schIUScale.mmToIU( ARC_HIGH_DEF_MM ) / radius ) );
132 double wanted = std::ceil( theta / step );
133 int count = wanted < 16384 ? std::max( 2, static_cast<int>( wanted ) ) : 16384;
134
135 std::vector<VECTOR2D> out;
136
137 for( int k = 1; k < count; ++k )
138 out.push_back( rotateAbout( aStart, aCentre, ( aAnticlockwise ? theta : -theta ) * k / count ) );
139
140 out.push_back( aEnd );
141 return out;
142}
143
144
145std::vector<std::unique_ptr<SCH_SHAPE>> ConvertShape( const SOURCE_SHAPE& aShape, const SCH_FRAME& aFrame,
146 SCH_LAYER_ID aLayer, int aStrokeWidth, bool aFilled, int aUnit )
147{
148 std::vector<std::unique_ptr<SCH_SHAPE>> out;
149 std::vector<const SOURCE_SEGMENT*> verts = ShapeVertices( aShape );
150 bool closed = aShape.IsClosed();
151
152 auto make = [&]( SHAPE_T aType )
153 {
154 std::unique_ptr<SCH_SHAPE> shape = std::make_unique<SCH_SHAPE>( aType, aLayer, aStrokeWidth );
155 shape->SetUnit( aUnit );
156
157 if( aFilled )
158 shape->SetFillMode( FILL_T::FILLED_SHAPE );
159
160 return shape;
161 };
162
163 if( verts.size() < 2 )
164 return out;
165
166 // A closed outline of two half circles in one direction is a circle
167 if( closed && verts.size() == 2 && verts[0]->ArcAngle == 180000 && verts[1]->ArcAngle == 180000
168 && verts[0]->Anticlockwise == verts[1]->Anticlockwise )
169 {
170 std::unique_ptr<SCH_SHAPE> circle = make( SHAPE_T::CIRCLE );
171
172 circle->SetCenter(
173 mapD( aFrame, ( VECTOR2D( verts[0]->X, verts[0]->Y ) + VECTOR2D( verts[1]->X, verts[1]->Y ) ) / 2.0 ) );
174 circle->SetEnd( aFrame.Map( verts[0]->X, verts[0]->Y ) );
175 out.push_back( std::move( circle ) );
176 return out;
177 }
178
179 size_t spans = closed ? verts.size() : verts.size() - 1;
180 bool hasArc = false;
181
182 for( size_t i = 0; i < spans; ++i )
183 hasArc |= verts[i]->ArcAngle != 0;
184
185 // KiCad cannot fill an outline made of separate arc and polyline shapes
186 bool onePolygon = aFilled && hasArc;
187 std::unique_ptr<SCH_SHAPE> poly;
188
189 for( size_t i = 0; i < spans; ++i )
190 {
191 const SOURCE_SEGMENT* a = verts[i];
192 const SOURCE_SEGMENT* b = verts[( i + 1 ) % verts.size()];
193 VECTOR2D s( a->X, a->Y );
194 VECTOR2D e( b->X, b->Y );
195
196 if( a->ArcAngle != 0 )
197 {
198 VECTOR2D c = arcCentre( s, e, a->ArcAngle, a->Anticlockwise );
199 double reach = ( s - c ).EuclideanNorm();
200
201 // KiCad's arc code works in integers around the centre; a nearly straight arc puts it out of range
202 bool arcFits = std::abs( c.x - aFrame.OriginX ) + reach < COORD_LIMIT
203 && std::abs( aFrame.OriginY - c.y ) + reach < COORD_LIMIT;
204
205 if( arcFits && !onePolygon )
206 {
207 if( poly )
208 out.push_back( std::move( poly ) );
209
210 double half = ( a->ArcAngle * M_PI / 180000.0 ) / 2.0 * ( a->Anticlockwise ? 1 : -1 );
211 std::unique_ptr<SCH_SHAPE> arc = make( SHAPE_T::ARC );
212
213 arc->SetArcGeometry( aFrame.Map( a->X, a->Y ), mapD( aFrame, rotateAbout( s, c, half ) ),
214 aFrame.Map( b->X, b->Y ) );
215 out.push_back( std::move( arc ) );
216 continue;
217 }
218
219 if( !poly )
220 {
221 poly = make( SHAPE_T::POLY );
222 poly->AddPoint( aFrame.Map( a->X, a->Y ) );
223 }
224
225 for( const VECTOR2D& pt : arcAsPolyline( s, e, c, a->ArcAngle, a->Anticlockwise ) )
226 poly->AddPoint( mapD( aFrame, pt ) );
227
228 continue;
229 }
230
231 if( !poly )
232 {
233 poly = make( SHAPE_T::POLY );
234 poly->AddPoint( aFrame.Map( a->X, a->Y ) );
235 }
236
237 poly->AddPoint( aFrame.Map( b->X, b->Y ) );
238 }
239
240 if( poly )
241 out.push_back( std::move( poly ) );
242
243 return out;
244}
245
246
247std::vector<std::unique_ptr<SCH_SHAPE>> ConvertShapeItem( const SOURCE_SHAPE_ITEM& aItem, const SCH_FRAME& aFrame,
248 SCH_LAYER_ID aLayer, int aUnit )
249{
250 const SOURCE_SHAPE* shape = dynamic_cast<const SOURCE_SHAPE*>( aItem.Shape );
251 const SOURCE_LINE_STYLE* style = dynamic_cast<const SOURCE_LINE_STYLE*>( aItem.Style );
252
253 if( !shape )
254 return {};
255
256 return ConvertShape( *shape, aFrame, aLayer, style ? style->Width : 0, aItem.Filled, aUnit );
257}
258
259
261{
262 // Easy-PC glyphs sit in a 240 unit cell whose capitals are 180 tall; a KiCad size is its capital height
263 return VECTOR2I( aStyle.Height, KiROUND( aStyle.Height * 180.0 / 240.0 ) );
264}
265
266
267bool DrawsUpright( const SOURCE_TEXT_POSITION& aPos, bool aKeepUpright )
268{
269 const SOURCE_TEXT_STYLE* style = dynamic_cast<const SOURCE_TEXT_STYLE*>( aPos.TextStyle );
270
271 return aKeepUpright && style && UprightRotation( aPos.Rotation, true ) != aPos.Rotation
272 && TEXT_METRICS( *style ).CanMeasure();
273}
274
275
276void ApplyTextPosition( EDA_TEXT& aText, const SOURCE_TEXT_POSITION& aPos, const SCH_FRAME& aFrame, bool aKeepUpright,
277 const wxString& aMeasured )
278{
279 const SOURCE_TEXT_STYLE* style = dynamic_cast<const SOURCE_TEXT_STYLE*>( aPos.TextStyle );
280
281 if( style )
282 {
283 aText.SetTextSize( TextSize( *style ) );
284 aText.SetTextThickness( style->LineWidth );
285 }
286
288
289 if( aPos.Alignment == TEXT_ALIGN_RIGHT )
291 else if( aPos.Alignment == TEXT_ALIGN_CENTRE )
293
294 auto flip = [&]()
295 {
296 if( h != GR_TEXT_H_ALIGN_CENTER )
298 };
299
300 // Mirrored text reads the other way from its anchor
301 if( aPos.Mirrored )
302 flip();
303
305 int32_t rot = NormalizeAngle( aPos.Rotation );
306 VECTOR2I anchor( aPos.Position.X, aPos.Position.Y );
307 wxString text = aText.GetText();
308
309 text.Replace( wxS( "\r" ), wxEmptyString );
310 aText.SetText( text );
311
312 if( DrawsUpright( aPos, aKeepUpright ) )
313 {
314 // Upright text is turned about the far end of its first line
315 anchor = TEXT_METRICS( *style ).UprightAnchor( aMeasured.IsEmpty() ? text : aMeasured, anchor, rot,
316 aPos.Mirrored, aPos.Alignment, true );
317 rot = NormalizeAngle( UprightRotation( rot, true ) );
318 }
319 else if( UprightRotation( rot, true ) != rot )
320 {
321 // KiCad never draws text upside down; turned back, the text keeps its box by flipping its justification
322 rot -= 180000;
323 flip();
325 }
326
327 bool vertical = rot >= 45000 && rot < 135000;
328
329 aText.SetTextAngle( vertical ? ANGLE_VERTICAL : ANGLE_HORIZONTAL );
330 aText.SetHorizJustify( h );
331 aText.SetVertJustify( v );
332
333 VECTOR2I pos = aFrame.Map( anchor.x, anchor.y );
334 int lines = static_cast<int>( text.Freq( '\n' ) ) + 1;
335
336 // Each stored line is an interline height below the last, while KiCad stacks a bottom block upwards
337 if( lines > 1 && style )
338 {
339 double pitch = style->Height * style->InterlineHeightPercent / 100;
340 double kicadPitch = KIFONT::FONT::GetFont()->GetInterline( TextSize( *style ).y, KIFONT::METRICS::Default() );
341 int shift = KiROUND( ( v == GR_TEXT_V_ALIGN_BOTTOM ? 1 : -1 ) * ( lines - 1 ) * pitch );
342
343 aText.SetLineSpacing( pitch / kicadPitch );
344 pos += vertical ? VECTOR2I( shift, 0 ) : VECTOR2I( 0, shift );
345 }
346
347 aText.SetTextPos( pos );
348}
349
350
351std::unique_ptr<SCH_TEXT> ConvertFreeText( const SOURCE_FREE_TEXT& aText, const SCH_FRAME& aFrame, SCH_LAYER_ID aLayer,
352 int aUnit, bool aKeepUpright )
353{
354 std::unique_ptr<SCH_TEXT> text =
355 std::make_unique<SCH_TEXT>( VECTOR2I( 0, 0 ), ToKiCadMarkup( aText.Text ), aLayer );
356 ApplyTextPosition( *text, aText, aFrame, aKeepUpright, aText.Text );
357 text->SetUnit( aUnit );
358 return text;
359}
360
361
362GATE_TERMINAL ResolveTerminal( const SOURCE_PCB_COMPONENT* aPcb, int aGate, int aTerminal )
363{
365 const SOURCE_DESIGN_ARRAY* maps = aPcb ? dynamic_cast<const SOURCE_DESIGN_ARRAY*>( aPcb->GateMaps ) : nullptr;
366
367 // Serialized object arrays may hold null elements, so the raw items are indexed
368 if( !maps || aGate < 0 || aGate >= static_cast<int>( maps->Items.size() ) || aTerminal < 0 )
369 return result;
370
371 const SOURCE_GATE_MAP* map = dynamic_cast<const SOURCE_GATE_MAP*>( maps->Items[aGate] );
372 const SOURCE_DESIGN_ARRAY* pins = map ? dynamic_cast<const SOURCE_DESIGN_ARRAY*>( map->Pins ) : nullptr;
373
374 if( pins )
375 {
376 const SOURCE_GATE_PIN* pin = aTerminal < static_cast<int>( pins->Items.size() )
377 ? dynamic_cast<const SOURCE_GATE_PIN*>( pins->Items[aTerminal] )
378 : nullptr;
379
380 if( !pin )
381 return result;
382
383 result.Mapped = true;
384 result.NoConnect = pin->IsNoConnect();
385 result.Number = pin->PinNumber;
386 ExpandPinNumberList( pin->PinNumber, &result.Display );
387
388 if( result.Display.IsEmpty() )
389 result.Display = pin->PinNumber;
390
391 if( IsPadList( pin->PcbSymbolPin ) )
392 {
393 wxArrayString stacked;
394
395 for( const wxString& number : ExpandPinNumberList( pin->PinNumber ) )
396 stacked.Add( EscapeStackedPinItem( number ) );
397
398 result.Number = wxS( "[" ) + wxJoin( stacked, ',', 0 ) + wxS( "]" );
399 }
400
401 return result;
402 }
403
404 // Before format 9000 a gate map stores only a pad number per terminal
405 if( map && !map->Pins && aTerminal < static_cast<int>( map->PcbSymbolPins.size() )
406 && map->PcbSymbolPins[aTerminal] != -1 )
407 {
408 result.Mapped = true;
409 result.Number = wxString::Format( wxS( "%d" ), map->PcbSymbolPins[aTerminal] );
410 result.Display = result.Number;
411 }
412
413 return result;
414}
415
416
417namespace
418{
419
420 void addText( LIB_SYMBOL& aSymbol, const SOURCE_TEXT_POSITION& aPos, const wxString& aText, const SCH_FRAME& aFrame,
421 int aUnit )
422 {
423 if( aText.IsEmpty() )
424 return;
425
427 ApplyTextPosition( *text, aPos, aFrame );
428 text->SetUnit( aUnit );
429 aSymbol.AddDrawItem( text, false );
430 }
431
432
433 void placeField( SCH_FIELD& aField, const SOURCE_VALUE_POSITION& aPos, const SCH_FRAME& aFrame )
434 {
435 ApplyTextPosition( aField, aPos, aFrame );
436 aField.SetVisible( aPos.Displayed );
437 }
438
439
441 void placeFields( LIB_SYMBOL& aSymbol, const SOURCE_SYMBOL& aGate0, const SCH_FRAME& aFrame,
442 const std::vector<const SOURCE_ATTRIBUTE*>& aAttributes )
443 {
444 std::vector<const SOURCE_VALUE_POSITION*> positions;
445
446 for( const SOURCE_VALUE_POSITION* pos : TypedItems<const SOURCE_VALUE_POSITION>( aGate0.ValuePositions ) )
447 {
448 if( pos->Displayed )
449 positions.push_back( pos );
450 }
451
452 const SOURCE_VALUE_POSITION* refPos = nullptr;
453 const SOURCE_VALUE_POSITION* valuePos = nullptr;
454
455 for( const SOURCE_VALUE_POSITION* pos : positions )
456 {
457 if( !refPos && ( pos->Types & VALUE_REFERENCE_NAME ) )
458 refPos = pos;
459 }
460
461 for( uint64_t wanted : { VALUE_COMPONENT_NAME, VALUE_VALUES } )
462 {
463 for( const SOURCE_VALUE_POSITION* pos : positions )
464 {
465 if( !valuePos && pos != refPos && ( pos->Types & wanted ) )
466 valuePos = pos;
467 }
468 }
469
470 if( refPos )
471 placeField( aSymbol.GetReferenceField(), *refPos, aFrame );
472
473 if( valuePos )
474 placeField( aSymbol.GetValueField(), *valuePos, aFrame );
475 else
476 aSymbol.GetValueField().SetVisible( false );
477
478 aSymbol.GetFootprintField().SetVisible( false );
479 aSymbol.GetDescriptionField().SetVisible( false );
480
481 for( const SOURCE_VALUE_POSITION* pos : positions )
482 {
483 if( ( pos->Types & VALUE_DESCRIPTION ) && pos != refPos && pos != valuePos )
484 placeField( aSymbol.GetDescriptionField(), *pos, aFrame );
485 }
486
487 for( const SOURCE_ATTRIBUTE* attr : aAttributes )
488 {
489 if( !attr->AttributeName || attr->AttributeName->Name.IsEmpty() )
490 continue;
491
492 SCH_FIELD* field = new SCH_FIELD( &aSymbol, FIELD_T::USER, attr->AttributeName->Name );
493 field->SetText( ToKiCadMarkup( attr->Value ) );
494 field->SetVisible( false );
495
496 for( const SOURCE_VALUE_POSITION* pos : positions )
497 {
498 if( ( pos->Types & VALUE_ATTRIBUTE ) && pos->Attribute == attr->AttributeName->Name )
499 {
500 placeField( *field, *pos, aFrame );
501 field->SetVisible( pos->Displayed && attr->Displayed );
502 break;
503 }
504 }
505
506 aSymbol.AddField( field );
507 }
508 }
509
510
512 void convertGate( LIB_SYMBOL& aSymbol, const SOURCE_SYMBOL& aGateSymbol, int aGate,
513 const SOURCE_PCB_COMPONENT* aPcb, const SOURCE_GATE* aGateDef, bool aPinTexts )
514 {
515 const int unit = aGate + 1;
516 SCH_FRAME frame{ aGateSymbol.OriginX, aGateSymbol.OriginY };
517
518 for( const SOURCE_SHAPE_ITEM* item : TypedItems<const SOURCE_SHAPE_ITEM>( aGateSymbol.Shapes ) )
519 {
520 for( std::unique_ptr<SCH_SHAPE>& shape : ConvertShapeItem( *item, frame, LAYER_DEVICE, unit ) )
521 aSymbol.AddDrawItem( shape.release(), false );
522 }
523
524 for( const SOURCE_FREE_TEXT* text : TypedItems<const SOURCE_FREE_TEXT>( aGateSymbol.Texts ) )
525 aSymbol.AddDrawItem( ConvertFreeText( *text, frame, LAYER_DEVICE, unit ).release(), false );
526
527 for( const SOURCE_FREE_PAD* pad : TypedItems<const SOURCE_FREE_PAD>( aGateSymbol.Pads ) )
528 {
529 const int terminal = pad->Number - 1;
530 const GATE_TERMINAL gate = ResolveTerminal( aPcb, aGate, terminal );
531 wxString name;
532
533 if( aGateDef && terminal >= 0 && terminal < static_cast<int>( aGateDef->PinNames.size() ) )
534 name = aGateDef->PinNames[terminal];
535
536 // A symbol pad is only the connection point; the leg is part of the symbol's shapes
537 SCH_PIN* pin = new SCH_PIN( &aSymbol );
538 pin->SetPosition( frame.Map( pad->Position.X, pad->Position.Y ) );
539 pin->SetLength( 0 );
540 pin->SetName( ToKiCadMarkup( name ) );
541 pin->SetNumber( aGateDef ? gate.Number : wxString::Format( wxS( "%d" ), pad->Number ) );
542 pin->SetType( gate.NoConnect ? ELECTRICAL_PINTYPE::PT_NC : ELECTRICAL_PINTYPE::PT_PASSIVE );
543 pin->SetUnit( unit );
544
545 switch( NormalizeAngle( pad->Angle ) / 90000 )
546 {
547 case 1: pin->SetOrientation( PIN_ORIENTATION::PIN_UP ); break;
548 case 2: pin->SetOrientation( PIN_ORIENTATION::PIN_LEFT ); break;
549 case 3: pin->SetOrientation( PIN_ORIENTATION::PIN_DOWN ); break;
550 default: pin->SetOrientation( PIN_ORIENTATION::PIN_RIGHT ); break;
551 }
552
553 aSymbol.AddDrawItem( pin, false );
554
555 if( !aPinTexts )
556 continue;
557
558 const wxString number = aGateDef ? gate.Display : pin->GetNumber();
559
560 for( const SOURCE_VALUE_POSITION* pos : TypedItems<const SOURCE_VALUE_POSITION>( pad->ValuePositions ) )
561 {
562 if( pos->Displayed && ( pos->Types & VALUE_PIN_NAME ) )
563 addText( aSymbol, *pos, name, frame, unit );
564 else if( pos->Displayed && ( pos->Types & VALUE_PIN_NUMBER ) )
565 addText( aSymbol, *pos, number, frame, unit );
566 }
567
568 if( const SOURCE_PIN_NAME* old = dynamic_cast<const SOURCE_PIN_NAME*>( pad->OldPinName );
569 old && old->Visible )
570 addText( aSymbol, *old, name, frame, unit );
571
572 if( const SOURCE_PIN_NAME* old = dynamic_cast<const SOURCE_PIN_NAME*>( pad->OldPinNumber );
573 old && old->Visible )
574 addText( aSymbol, *old, number, frame, unit );
575 }
576 }
577
578
580 void mergeCommonPins( LIB_SYMBOL& aSymbol )
581 {
582 std::map<wxString, std::vector<SCH_PIN*>> byNumber;
583
584 for( SCH_PIN* pin : aSymbol.GetGraphicalPins() )
585 {
586 if( !pin->GetNumber().IsEmpty() )
587 byNumber[pin->GetNumber()].push_back( pin );
588 }
589
590 for( auto& [number, pins] : byNumber )
591 {
592 const SCH_PIN* first = pins.front();
593 bool alike = pins.size() > 1;
594
595 for( size_t i = 1; i < pins.size(); ++i )
596 {
597 alike &= pins[i]->GetPosition() == first->GetPosition()
598 && pins[i]->GetOrientation() == first->GetOrientation()
599 && pins[i]->GetName() == first->GetName() && pins[i]->GetType() == first->GetType()
600 && pins[i]->GetUnit() != first->GetUnit();
601 }
602
603 if( !alike )
604 continue;
605
606 pins.front()->SetUnit( 0 );
607
608 for( size_t i = 1; i < pins.size(); ++i )
609 aSymbol.RemoveDrawItem( pins[i] );
610 }
611 }
612
613
614 std::unique_ptr<LIB_SYMBOL> newSymbol( const LIB_ID& aId )
615 {
616 std::unique_ptr<LIB_SYMBOL> symbol = std::make_unique<LIB_SYMBOL>( aId.GetLibItemName().wx_str() );
617 symbol->SetLibId( aId );
618
619 // Pin names and numbers have their own stored positions, so they are added as texts
620 symbol->SetShowPinNames( false );
621 symbol->SetShowPinNumbers( false );
622 return symbol;
623 }
624
625} // namespace
626
627
628std::unique_ptr<LIB_SYMBOL> ConvertComponentSymbol( const SCH_COMPONENT_SOURCE& aSrc, const LIB_ID& aId )
629{
630 if( !aSrc.Schematic )
631 THROW_IO_ERROR( _( "Easy-PC component has no schematic part" ) );
632
633 std::vector<const SOURCE_GATE*> gateDefs;
634
635 if( const SOURCE_DESIGN_ARRAY* gates = dynamic_cast<const SOURCE_DESIGN_ARRAY*>( aSrc.Schematic->Gates ) )
636 {
637 for( const OBJECT* obj : gates->Items )
638 {
639 gateDefs.push_back( dynamic_cast<const SOURCE_GATE*>( obj ) );
640
641 if( !gateDefs.back() )
642 {
643 THROW_IO_ERROR( wxString::Format( _( "Easy-PC component '%s' gate %zu is empty" ), aSrc.Schematic->Name,
644 gateDefs.size() ) );
645 }
646 }
647 }
648
649 if( aSrc.Gates.size() != gateDefs.size() )
650 {
651 THROW_IO_ERROR( wxString::Format( _( "Easy-PC component '%s' has %zu gates but %zu gate symbols" ),
652 aSrc.Schematic->Name, gateDefs.size(), aSrc.Gates.size() ) );
653 }
654
655 std::unique_ptr<LIB_SYMBOL> symbol = newSymbol( aId );
656 symbol->SetUnitCount( std::max<int>( 1, static_cast<int>( aSrc.Gates.size() ) ), false );
657 symbol->GetReferenceField().SetText( aSrc.Schematic->DefaultReference );
658 symbol->GetValueField().SetText( ToKiCadMarkup( aSrc.Schematic->Name ) );
659 symbol->SetDescription( ToKiCadMarkup( aSrc.Schematic->Description ) );
660 symbol->GetFootprintField().SetText( aSrc.Footprint );
661 symbol->SetFPFilters( aSrc.FootprintFilters );
662
663 std::vector<const SOURCE_ATTRIBUTE*> attributes;
664
665 if( aSrc.Board )
667
668 // Fields sit where the first gate symbol present puts them
669 for( const SOURCE_SYMBOL* gateSymbol : aSrc.Gates )
670 {
671 if( gateSymbol )
672 {
673 placeFields( *symbol, *gateSymbol, SCH_FRAME{ gateSymbol->OriginX, gateSymbol->OriginY }, attributes );
674 break;
675 }
676 }
677
678 for( size_t g = 0; g < aSrc.Gates.size(); ++g )
679 {
680 if( aSrc.Gates[g] )
681 convertGate( *symbol, *aSrc.Gates[g], static_cast<int>( g ), aSrc.Board, gateDefs[g], aSrc.PinTexts );
682 }
683
684 mergeCommonPins( *symbol );
685
686 // Items are added unsorted, since LIB_SYMBOL sorts the whole list on every sorted insertion
687 symbol->GetDrawItems().sort();
688 return symbol;
689}
690
691
692std::unique_ptr<LIB_SYMBOL> ConvertSymbol( const SOURCE_SYMBOL& aSymbol, const LIB_ID& aId )
693{
694 std::unique_ptr<LIB_SYMBOL> symbol = newSymbol( aId );
695 symbol->GetValueField().SetText( ToKiCadMarkup( aSymbol.Name ) );
696
697 placeFields( *symbol, aSymbol, SCH_FRAME{ aSymbol.OriginX, aSymbol.OriginY }, {} );
698 convertGate( *symbol, aSymbol, 0, nullptr, nullptr, true );
699 symbol->GetDrawItems().sort();
700 return symbol;
701}
702
703} // namespace EASYPC
const char * name
constexpr EDA_IU_SCALE schIUScale
Definition base_units.h:130
constexpr double ARC_HIGH_DEF_MM
Definition base_units.h:135
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
bool CanMeasure() const
False for a TrueType style; the measuring calls throw IO_ERROR for one.
VECTOR2I UprightAnchor(const wxString &aText, const VECTOR2I &aAnchor, int32_t aAngle, bool aMirrored, int aHAlign, bool aKeepUpright) const
When aKeepUpright and UprightRotation turns the text, the anchor moves to the far end of the first li...
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition eda_text.h:94
virtual void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition eda_text.cpp:512
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition eda_text.h:118
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:556
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
Definition eda_text.cpp:378
virtual void SetVisible(bool aVisible)
Definition eda_text.cpp:347
void SetLineSpacing(double aLineSpacing)
Definition eda_text.cpp:504
virtual void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition eda_text.cpp:250
virtual void SetText(const wxString &aText)
Definition eda_text.cpp:236
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
Definition eda_text.cpp:268
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition eda_text.cpp:370
static FONT * GetFont(const wxString &aFontName=wxEmptyString, bool aBold=false, bool aItalic=false, const std::vector< wxString > *aEmbeddedFiles=nullptr, bool aForDrawingSheet=false)
Definition font.cpp:143
virtual double GetInterline(double aGlyphHeight, const METRICS &aFontMetrics) const =0
Compute the distance (interline) between 2 lines of text (for multiline texts).
static const METRICS & Default()
Definition font.cpp:48
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
const UTF8 & GetLibItemName() const
Definition lib_id.h:98
Define a library symbol object.
Definition lib_symbol.h:114
SCH_FIELD & GetDescriptionField()
Definition lib_symbol.h:463
SCH_FIELD & GetFootprintField()
Definition lib_symbol.h:455
SCH_FIELD & GetValueField()
Definition lib_symbol.h:447
void RemoveDrawItem(SCH_ITEM *aItem)
Remove draw aItem from list.
void AddField(SCH_FIELD *aField)
Add a field.
std::vector< SCH_PIN * > GetGraphicalPins(int aUnit=0, int aBodyStyle=0) const override
Graphical pins: Return schematic pin objects as drawn (unexpanded), filtered by unit/body.
void AddDrawItem(SCH_ITEM *aItem, bool aSort=true)
Add a new draw aItem to the draw object list and sort according to aSort.
SCH_FIELD & GetReferenceField()
Definition lib_symbol.h:451
void SetText(const wxString &aText) override
int GetUnit() const
Definition sch_item.h:243
const wxString & GetName() const
Definition sch_pin.cpp:527
PIN_ORIENTATION GetOrientation() const
Definition sch_pin.cpp:386
VECTOR2I GetPosition() const override
Definition sch_pin.cpp:378
ELECTRICAL_PINTYPE GetType() const
Definition sch_pin.cpp:435
wxString wx_str() const
Definition utf8.cpp:41
Placed instances and connectivity: component, symbol and pad instances, nets, nodes,...
Shapes, vertices and the free-standing drawn items.
Free pads, apart because they derive from a connectivity class.
Symbols, components and gates, embedded in designs or the single object of a library item stream.
Design items, attributes, styles, layers, net classes and spacings.
Schematic graphics, texts and symbols shared by the sheet builder and the symbol libraries,...
Easy-PC's text measurement, so text lands where Easy-PC draws it.
Design units of 100 nm and angles of thousandths of a degree, anticlockwise from +X with Y up.
#define _(s)
static constexpr EDA_ANGLE ANGLE_VERTICAL
Definition eda_angle.h:445
static constexpr EDA_ANGLE ANGLE_HORIZONTAL
Definition eda_angle.h:444
@ FILLED_SHAPE
Fill with object color.
Definition eda_fill.h:31
SHAPE_T
Definition eda_shape.h:54
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
SCH_LAYER_ID
Eeschema drawing layers.
Definition layer_ids.h:471
@ LAYER_DEVICE
Definition layer_ids.h:488
static std::vector< VECTOR2D > arcAsPolyline(const VECTOR2D &aStart, const VECTOR2D &aEnd, const VECTOR2D &aCentre, int32_t aAngle, bool aAnticlockwise)
Points after aStart of an arc span at ARC_HIGH_DEF, at most 16384 of them.
GATE_TERMINAL ResolveTerminal(const SOURCE_PCB_COMPONENT *aPcb, int aGate, int aTerminal)
Terminal aTerminal (0-based) of gate aGate; formats before 9000 map a bare pad number,...
std::vector< wxString > ExpandPinNumberList(const wxString &aList, wxString *aDisplay)
Expand a gate pin's component pin list by Easy-PC's pin list syntax: items separated by ,...
std::unique_ptr< SCH_TEXT > ConvertFreeText(const SOURCE_FREE_TEXT &aText, const SCH_FRAME &aFrame, SCH_LAYER_ID aLayer, int aUnit, bool aKeepUpright)
int32_t UprightRotation(int32_t aAngle, bool aKeepUpright)
With text kept upright, an angle above 135 and up to 305 degrees turns by 180.
constexpr double COORD_LIMIT
Half of KiCad's integer range, so shape code rotating or adding coordinates cannot overflow.
std::unique_ptr< LIB_SYMBOL > ConvertComponentSymbol(const SCH_COMPONENT_SOURCE &aSrc, const LIB_ID &aId)
One unit per gate in symbol-local coordinates, zero-length passive pins at the pads,...
static VECTOR2I mapD(const SCH_FRAME &aFrame, const VECTOR2D &aPt)
bool DrawsUpright(const SOURCE_TEXT_POSITION &aPos, bool aKeepUpright)
True when a design that keeps text upright turns aPos about the far end of its first line.
std::vector< std::unique_ptr< SCH_SHAPE > > ConvertShape(const SOURCE_SHAPE &aShape, const SCH_FRAME &aFrame, SCH_LAYER_ID aLayer, int aStrokeWidth, bool aFilled, int aUnit)
Outline of a shape: polylines, arcs and circles; a filled outline with arcs is one polygon.
std::unique_ptr< LIB_SYMBOL > ConvertSymbol(const SOURCE_SYMBOL &aSymbol, const LIB_ID &aId)
A .ssl symbol alone: one unit, pins numbered by pad number.
static VECTOR2D rotateAbout(const VECTOR2D &aPt, const VECTOR2D &aCentre, double aRadians)
std::vector< std::unique_ptr< SCH_SHAPE > > ConvertShapeItem(const SOURCE_SHAPE_ITEM &aItem, const SCH_FRAME &aFrame, SCH_LAYER_ID aLayer, int aUnit)
A shape item with its own line style and fill.
bool IsPadList(const wxString &aPcbSymbolPin)
Whether a gate pin's pad reference names several pads ("-1", the unmapped marker, does not)
std::vector< T * > TypedItems(const OBJECT *aList)
The items of ListItems that are a T, in order.
constexpr int32_t NormalizeAngle(int32_t aAngle)
Wrap an angle into [0, ANGLE_FULL_TURN)
wxString ToKiCadMarkup(const wxString &aText)
A displayed string in KiCad's markup.
static VECTOR2D arcCentre(const VECTOR2D &aStart, const VECTOR2D &aEnd, int32_t aAngle, bool aAnticlockwise)
Centre of an arc span from its ends and swept angle.
VECTOR2I TextSize(const SOURCE_TEXT_STYLE &aStyle)
KiCad text size for a source text style: its height as the width, its capital height as the height.
@ VALUE_ATTRIBUTE
the attribute the value position names
void ApplyTextPosition(EDA_TEXT &aText, const SOURCE_TEXT_POSITION &aPos, const SCH_FRAME &aFrame, bool aKeepUpright, const wxString &aMeasured)
Place any KiCad text as the source position says: anchor, angle, mirror, justification and style.
std::vector< const SOURCE_SEGMENT * > ShapeVertices(const SOURCE_SHAPE &aShape)
A shape's vertices in drawing order, following next references for formats 1 and 2.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
@ PT_NC
not connected (must be left open)
Definition pin_type.h:46
@ PT_PASSIVE
pin for passive symbols: must be connected, and can be connected to any pin.
Definition pin_type.h:39
@ PIN_UP
The pin extends upwards from the connection point: Probably on the bottom side of the symbol.
Definition pin_type.h:123
@ PIN_RIGHT
The pin extends rightwards from the connection point.
Definition pin_type.h:107
@ PIN_LEFT
The pin extends leftwards from the connection point: Probably on the right side of the symbol.
Definition pin_type.h:114
@ PIN_DOWN
The pin extends downwards from the connection: Probably on the top side of the symbol.
Definition pin_type.h:131
wxString EscapeStackedPinItem(const wxString &aPinNumber)
Escape the characters that carry structural meaning inside stacked pin notation ('[',...
What a package's gate map says about one gate terminal.
Root of every deserialized object.
Everything one component LIB_SYMBOL is made from.
std::vector< const SOURCE_SYMBOL * > Gates
symbol per gate; null leaves that unit empty
const SOURCE_SCM_COMPONENT * Schematic
const SOURCE_PCB_COMPONENT * Board
the package whose gate map numbers the pins
bool PinTexts
a placed part draws its pin texts from its pad instances
Design to KiCad mapping: X shifted, Y negated about the origin.
VECTOR2I Map(int64_t aX, int64_t aY) const
An array: owner tag, then a counted list of objects.
OBJECT * Attributes
attribute array
A pad of a symbol definition.
One gate's terminals to footprint pads.
OBJECT * Pins
gate pin array, format above 8999
std::vector< int32_t > PcbSymbolPins
older formats: pad number per terminal, -1 unmapped
A schematic symbol and its pin names.
The package half of a component.
OBJECT * GateMaps
one per schematic gate in order
Pin name and pin number definitions.
bool HasNextRef
the stream carried a next vertex (shape format below 3)
int32_t ArcAngle
swept angle, 0 for a straight span
bool IsClosed() const
The last vertex links back to the first (formats 1 and 2) or the closed byte is set (format 3)
std::vector< OBJECT * > Segments
the vertex list as read
Footprints and schematic symbols alike.
int32_t Height
the accessor is GetWidth but it is the text height
@ USER
The field ID hasn't been set yet; field is invalid.
KIBIS_PIN * pin
int radius
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
wxString result
Test unit parsing edge cases and error handling.
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
GR_TEXT_V_ALIGN_T
This is API surface mapped to common.types.VertialAlignment.
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_TOP
#define M_PI
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707