43#include <wx/filename.h>
44#include <wx/wfstream.h>
46#include <wx/strconv.h>
47#include <wx/fontenc.h>
87 uint16_t v =
static_cast<uint16_t
>(
m_pos[0] )
88 | (
static_cast<uint16_t
>(
m_pos[1] ) << 8 );
99 uint32_t v =
static_cast<uint32_t
>(
m_pos[0] )
100 | (
static_cast<uint32_t
>(
m_pos[1] ) << 8 )
101 | (
static_cast<uint32_t
>(
m_pos[2] ) << 16 )
102 | (
static_cast<uint32_t
>(
m_pos[3] ) << 24 );
118 std::memcpy( &v, &bits,
sizeof( v ) );
128 uint64_t bits =
static_cast<uint64_t
>(
m_pos[0] )
129 | (
static_cast<uint64_t
>(
m_pos[1] ) << 8 )
130 | (
static_cast<uint64_t
>(
m_pos[2] ) << 16 )
131 | (
static_cast<uint64_t
>(
m_pos[3] ) << 24 )
132 | (
static_cast<uint64_t
>(
m_pos[4] ) << 32 )
133 | (
static_cast<uint64_t
>(
m_pos[5] ) << 40 )
134 | (
static_cast<uint64_t
>(
m_pos[6] ) << 48 )
135 | (
static_cast<uint64_t
>(
m_pos[7] ) << 56 );
139 std::memcpy( &v, &bits,
sizeof( v ) );
147 size_t len = std::min( rawLen, aMaxLen );
152 std::string s(
reinterpret_cast<const char*
>(
m_pos ), len );
168 std::string s(
reinterpret_cast<const char*
>(
m_pos ), len );
189 wxFFileInputStream stream( aFileName );
192 THROW_IO_ERROR( wxString::Format(
_(
"Cannot open file '%s'" ), aFileName ) );
194 size_t fileSize = stream.GetLength();
197 THROW_IO_ERROR( wxString::Format(
_(
"File '%s' is too small to be a Sprint Layout file" ), aFileName ) );
200 stream.Read(
m_buffer.data(), fileSize );
202 if( stream.LastRead() != fileSize )
203 THROW_IO_ERROR( wxString::Format(
_(
"Failed to read file '%s'" ), aFileName ) );
215 if(
m_fileData.version > 6 || magic1 != 0x33 || magic2 != 0xAA || magic3 != 0xFF )
220 if( numBoards == 0 || numBoards > 100 )
225 for( uint32_t b = 0; b < numBoards; b++ )
229 uint32_t numConnections = 0;
231 for(
auto& obj :
m_fileData.boards[b].objects )
238 for( uint32_t c = 0; c < numConnections; c++ )
243 skip( connCount *
sizeof( uint32_t ) );
265 for(
int i = 0; i < 7; i++ )
302 aBoard.
objects.resize( numObjects );
304 for( uint32_t i = 0; i < numObjects; i++ )
328 THROW_IO_ERROR( wxString::Format(
_(
"Unknown object type %d in Sprint Layout file" ),
367 aObj.
groups.resize( groupCount );
369 for( uint32_t i = 0; i < groupCount; i++ )
384 THROW_IO_ERROR(
_(
"Too many text children in Sprint Layout object" ) );
388 for( uint32_t i = 0; i < childCount; i++ )
417 aObj.
points.resize( pointCount );
419 for( uint32_t i = 0; i < pointCount; i++ )
443 switch( aSprintLayer )
452 default:
return F_Cu;
465 nm =
static_cast<double>( aValue ) * 100.0;
467 nm =
static_cast<double>( aValue ) * 10000.0;
469 if( nm > std::numeric_limits<int>::max() || nm < std::numeric_limits<int>::min() )
470 THROW_IO_ERROR(
_(
"Coordinate value out of range in Sprint Layout file" ) );
485 static wxCSConv convCP1251( wxFONTENCODING_CP1251 );
488 return wxEmptyString;
490 wxString ret = wxString::FromUTF8( aStr );
492 if( ret.empty() && convCP1251.IsOk() )
493 ret = wxString( aStr.c_str(), convCP1251 );
505 std::unique_ptr<BOARD> board = std::make_unique<BOARD>();
509 bool hasInnerLayers =
false;
511 for(
const auto& obj : boardData.
objects )
515 hasInnerLayers =
true;
521 board->SetCopperLayerCount( 4 );
523 board->SetCopperLayerCount( 2 );
526 std::map<uint16_t, FOOTPRINT*> componentMap;
527 std::vector<std::vector<VECTOR2I>> outlineSegments;
531 if( aObj.component_id == 0 )
534 auto it = componentMap.find( aObj.component_id );
536 if( it != componentMap.end() )
547 fp->
SetReference( wxString::Format( wxS(
"U%d" ), aObj.component_id ) );
550 fd->SetVisible(
false );
555 if( !aObj.component.comment.empty() )
558 if( !aObj.component.package.empty() )
567 componentMap[aObj.component_id] = fp;
573 for(
const auto& obj : boardData.
objects )
576 getOrCreateComponentFootprint( obj );
580 for(
const auto& obj : boardData.
objects )
584 if(
FOOTPRINT* fp = getOrCreateComponentFootprint( obj ) )
616 for(
FOOTPRINT* fp : board->Footprints() )
618 BOX2I fpBbox = fp->GetBoundingHull().BBox();
621 fp->SetPosition(
anchor );
625 fp->MoveAnchorPosition( anchorShift );
628 for(
const auto& [ componentId, fp ] : componentMap )
630 wxString fpKey = wxString::Format( wxS(
"SprintLayout_%s" ), fp->GetReference() );
633 aFootprintMap[fpKey] = std::unique_ptr<FOOTPRINT>( fpCopy );
636 buildOutline( board.get(), outlineSegments, boardData );
645 } groundPlaneMap[] = {
652 for(
const auto& gp : groundPlaneMap )
660 if( w <= 0 || h <= 0 )
663 ZONE* zone =
new ZONE( board.get() );
666 zone->
SetZoneName( wxString::Format( wxS(
"GND_PLANE_%s" ),
667 board->GetLayerName( gp.layer ) ) );
685 BOX2I bbox = board->ComputeBoundingBox(
true );
692 for(
FOOTPRINT* fp : board->Footprints() )
693 fp->Move( centerOffset );
695 for(
ZONE* zone : board->Zones() )
696 zone->Move( centerOffset );
699 item->Move( centerOffset );
702 return board.release();
708 BOARD* board = aContainer ? aContainer->
GetBoard() :
nullptr;
716 static_cast<int>( board->
Footprints().size() ) ) );
719 aContainer->
Add( fp );
732 double cx = 0, cy = 0;
734 for(
const auto& pt : aObj.
points )
740 cx /=
static_cast<double>( aObj.
points.size() );
741 cy /=
static_cast<double>( aObj.
points.size() );
742 pos =
sprintToKicadPos(
static_cast<float>( cx ),
static_cast<float>( cy ) );
749 pad->SetPosition( pos );
778 VECTOR2I padSize( outerDia, outerDia );
779 VECTOR2I drillSize( drillDia, drillDia );
799 pad->SetDrillSize( drillSize );
839 else if( thStyle == 2 )
877 pad->Padstack().FrontOuterLayers().has_solder_mask =
false;
878 pad->Padstack().BackOuterLayers().has_solder_mask =
false;
885 pad->SetLocalClearance( std::optional<int>(
clearance ) );
892 pad->SetLocalThermalSpokeWidthOverride( std::optional<int>( spokeWidth ) );
910 pad->SetNumber( wxString::Format( wxS(
"%d" ),
911 static_cast<int>( fp->
Pads().size() + 1 ) ) );
918 std::vector<std::vector<VECTOR2I>>& aOutlineSegments )
920 if( aObj.
points.size() < 2 )
927 std::vector<VECTOR2I> segment;
929 for(
const auto& pt : aObj.
points )
932 aOutlineSegments.push_back( std::move( segment ) );
941 for(
size_t i = 0; i + 1 < aObj.
points.size(); i++ )
949 aContainer->
Add( shape );
955 std::vector<std::vector<VECTOR2I>>& aOutlineSegments )
957 if( aObj.
points.size() < 2 )
960 BOARD* board = aContainer ? aContainer->
GetBoard() :
nullptr;
965 std::vector<VECTOR2I> segment;
967 for(
const auto& pt : aObj.
points )
970 aOutlineSegments.push_back( std::move( segment ) );
974 bool isFilled = ( aObj.
filled != 0 );
975 bool isCutout = ( aObj.
keepout != 0 );
980 ZONE* zone =
new ZONE( aContainer );
992 for(
const auto& pt : aObj.
points )
999 aContainer->
Add( zone );
1004 ZONE* zone =
new ZONE( aContainer );
1012 for(
const auto& pt : aObj.
points )
1034 aContainer->
Add( zone );
1036 else if( isFilled && aObj.
points.size() >= 3 )
1048 for(
const auto& pt : aObj.
points )
1055 aContainer->
Add( shape );
1065 for(
size_t i = 0; i + 1 < aObj.
points.size(); i++ )
1073 aContainer->
Add( shape );
1080 std::vector<std::vector<VECTOR2I>>& aOutlineSegments )
1095 int32_t endAngle =
static_cast<int32_t
>( aObj.
line_width );
1097 bool isFullCircle = ( startAngle == 0 && endAngle == 0 )
1098 || ( endAngle - startAngle >= 360000 )
1099 || ( startAngle == endAngle );
1104 std::vector<VECTOR2I> segment;
1108 for(
int i = 0; i <= 24; i++ )
1110 double angle = (
static_cast<double>( i ) / 24.0 ) * 2.0 *
M_PI;
1111 int px =
center.x +
static_cast<int>( std::cos( angle ) * kiRadius );
1112 int py =
center.y -
static_cast<int>( std::sin( angle ) * kiRadius );
1113 segment.emplace_back( px, py );
1118 int32_t sa = startAngle;
1119 int32_t ea = endAngle;
1124 for( int32_t a = sa; a <= ea; a += 15000 )
1126 double rad = (
static_cast<double>( a ) / 1000.0 ) *
M_PI / 180.0;
1127 int px =
center.x +
static_cast<int>( std::cos( rad ) * kiRadius );
1128 int py =
center.y -
static_cast<int>( std::sin( rad ) * kiRadius );
1129 segment.emplace_back( px, py );
1132 double endRad = (
static_cast<double>( ea ) / 1000.0 ) *
M_PI / 180.0;
1133 int epx =
center.x +
static_cast<int>( std::cos( endRad ) * kiRadius );
1134 int epy =
center.y -
static_cast<int>( std::sin( endRad ) * kiRadius );
1135 segment.emplace_back( epx, epy );
1138 aOutlineSegments.push_back( std::move( segment ) );
1158 double startRad = (
static_cast<double>( startAngle ) / 1000.0 ) *
M_PI / 180.0;
1159 int sx =
center.x +
static_cast<int>( std::cos( startRad ) * kiRadius );
1160 int sy =
center.y -
static_cast<int>( std::sin( startRad ) * kiRadius );
1163 int32_t ea = endAngle;
1165 if( ea <= startAngle )
1169 double arcAngle = -
static_cast<double>( ea - startAngle ) / 1000.0;
1173 aContainer->
Add( shape );
1202 if( aObj.
text.empty() )
1209 text->SetLayer( layer );
1213 text->SetKeepUpright(
false );
1217 text->SetTextPos( pos );
1224 double widthScale = 0.5 + 0.5 * aObj.
line_width;
1225 text->SetTextSize(
VECTOR2I( height * widthScale, height ) );
1227 double thicknessScale = 0.06 + 0.05 * aObj.
inner;
1228 int thickness = height * thicknessScale;
1230 if( thickness <= 0 )
1231 thickness = std::max( 1, height / 8 );
1233 text->SetTextThickness( thickness );
1237 text->SetMirrored(
true );
1249 static const int PROXIMITY_DELTA = 100;
1258 for(
size_t iterations = 0; iterations < aOutlineSegments.size(); iterations++ )
1260 bool joined =
false;
1262 for(
auto& seg : aOutlineSegments )
1264 if( seg.size() < 2 )
1267 for(
auto& other : aOutlineSegments )
1269 if( &seg == &other || other.empty() )
1272 bool frontMatch = closeEnough( seg.back(), other.front(), PROXIMITY_DELTA );
1273 bool backMatch = !frontMatch
1274 && closeEnough( seg.back(), other.back(), PROXIMITY_DELTA );
1278 std::reverse( other.begin(), other.end() );
1285 if( seg.back() == other.front() )
1286 seg.insert( seg.end(), other.begin() + 1, other.end() );
1288 seg.insert( seg.end(), other.begin(), other.end() );
1300 bool hasOutline =
false;
1302 for(
const auto& seg : aOutlineSegments )
1304 if( seg.size() < 2 )
1309 for(
size_t i = 0; i + 1 < seg.size(); i++ )
1316 shape->
SetEnd( seg[i + 1] );
1317 aBoard->
Add( shape );
1327 if( w > 0 && h > 0 )
1336 for(
int i = 0; i < 4; i++ )
1343 shape->
SetEnd( corners[( i + 1 ) % 4] );
1344 aBoard->
Add( shape );
constexpr EDA_IU_SCALE pcbIUScale
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
Abstract interface for BOARD_ITEMs capable of storing other items inside.
virtual void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false)=0
Adds an item to the container.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
Information pertinent to a Pcbnew printed circuit board.
void Add(BOARD_ITEM *aItem, ADD_MODE aMode=ADD_MODE::INSERT, bool aSkipConnectivity=false) override
Removes an item from the container.
NETINFO_ITEM * FindNet(int aNetcode) const
Search for a net with the given netcode.
const FOOTPRINTS & Footprints() const
constexpr size_type GetWidth() const
constexpr const Vec GetCenter() const
constexpr size_type GetHeight() const
virtual void SetParent(EDA_ITEM *aParent)
void SetCenter(const VECTOR2I &aCenter)
void SetPolyShape(const SHAPE_POLY_SET &aShape)
virtual void SetFilled(bool aFlag)
void SetStart(const VECTOR2I &aStart)
void SetShape(SHAPE_T aShape)
void SetEnd(const VECTOR2I &aEnd)
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
void SetWidth(int aWidth)
virtual void SetVisible(bool aVisible)
virtual void SetText(const wxString &aText)
LSET is a set of PCB_LAYER_IDs.
static LSET AllCuMask(int aCuLayerCount)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Handle the data for a net.
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
static LSET PTHMask()
layer set for a through hole pad
static LSET SMDMask()
layer set for a SMD pad on Front layer
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Represent a set of closed polygons.
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.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
wxString convertString(const std::string &aStr) const
SPRINT_LAYOUT::FILE_DATA m_fileData
void processText(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj)
PCB_LAYER_ID mapLayer(uint8_t aSprintLayer) const
std::string readFixedString(size_t aMaxLen)
bool Parse(const wxString &aFileName)
void buildOutline(BOARD *aBoard, std::vector< std::vector< VECTOR2I > > &aOutlineSegments, const SPRINT_LAYOUT::BOARD_DATA &aBoardData)
void processCircle(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj, std::vector< std::vector< VECTOR2I > > &aOutlineSegments)
void parseObject(SPRINT_LAYOUT::OBJECT &aObject, bool aIsTextChild=false)
BOARD * CreateBoard(std::map< wxString, std::unique_ptr< FOOTPRINT > > &aFootprintMap, size_t aBoardIndex=0)
void parseBoardHeader(SPRINT_LAYOUT::BOARD_DATA &aBoard)
void processLine(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj, std::vector< std::vector< VECTOR2I > > &aOutlineSegments)
void processPoly(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj, std::vector< std::vector< VECTOR2I > > &aOutlineSegments)
VECTOR2I sprintToKicadPos(float aX, float aY) const
int sprintToKicadCoord(float aValue) const
std::vector< uint8_t > m_buffer
std::string readVarString()
void processPad(BOARD_ITEM_CONTAINER *aContainer, const SPRINT_LAYOUT::OBJECT &aObj)
Handle a list of polygons defining a copper zone.
void SetDoNotAllowPads(bool aEnable)
void SetLocalClearance(std::optional< int > aClearance)
void AddPolygon(std::vector< VECTOR2I > &aPolygon)
Add a polygon to the zone outline.
void SetThermalReliefSpokeWidth(int aThermalReliefSpokeWidth)
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetIsRuleArea(bool aEnable)
void SetDoNotAllowTracks(bool aEnable)
void SetDoNotAllowVias(bool aEnable)
void SetThermalReliefGap(int aThermalReliefGap)
void SetDoNotAllowFootprints(bool aEnable)
void SetDoNotAllowZoneFills(bool aEnable)
void SetAssignedPriority(unsigned aPriority)
void SetZoneName(const wxString &aName)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
PCB_LAYER_ID
A quick note on layer IDs:
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
@ NPTH
like PAD_PTH, but not plated mechanical use only, no connection allowed
@ SMD
Smd pad, appears on the solder paste layer (default)
@ PTH
Plated through hole pad.
static constexpr uint32_t MAX_CHILDREN
static constexpr uint32_t MAX_GROUPS
static constexpr uint32_t MAX_OBJECTS
static constexpr uint32_t MAX_POINTS
std::vector< OBJECT > objects
std::vector< POINT > points
std::vector< OBJECT > text_children
std::vector< uint32_t > groups
@ DESCRIPTION
Field Description of part, i.e. "1/4W 1% Metal Film Resistor".
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.
VECTOR2< int32_t > VECTOR2I
@ THERMAL
Use thermal relief for pads.
@ NONE
Pads are not covered.
@ FULL
pads are covered by copper