56#include <wx/xml/xml.h>
57#include <wx/filename.h>
59#include <wx/wfstream.h>
60#include <wx/txtstrm.h>
98 ECOORD coord( aDistance, unit );
109 T ret = std::max( aMin, aValue );
110 return std::min( aMax, ret );
116static wxString
makeKey(
const wxString& aFirst,
const wxString& aSecond )
118 wxString key = aFirst +
'\x02' + aSecond;
166 wxXmlNode* child = aRules->GetChildren();
172 if( child->GetName() == wxT(
"param" ) )
174 const wxString&
name = child->GetAttribute( wxT(
"name" ) );
175 const wxString& value = child->GetAttribute( wxT(
"value" ) );
177 if(
name == wxT(
"psElongationLong" ) )
179 else if(
name == wxT(
"psElongationOffset" ) )
181 else if(
name == wxT(
"mvStopFrame" ) )
183 else if(
name == wxT(
"mvCreamFrame" ) )
185 else if(
name == wxT(
"mlMinStopFrame" ) )
187 else if(
name == wxT(
"mlMaxStopFrame" ) )
189 else if(
name == wxT(
"mlMinCreamFrame" ) )
191 else if(
name == wxT(
"mlMaxCreamFrame" ) )
193 else if(
name == wxT(
"srRoundness" ) )
195 else if(
name == wxT(
"srMinRoundness" ) )
197 else if(
name == wxT(
"srMaxRoundness" ) )
199 else if(
name == wxT(
"psTop" ) )
200 psTop = wxAtoi( value );
201 else if(
name == wxT(
"psBottom" ) )
203 else if(
name == wxT(
"psFirst" ) )
205 else if(
name == wxT(
"rvPadTop" ) )
207 else if(
name == wxT(
"rlMinPadTop" ) )
209 else if(
name == wxT(
"rlMaxPadTop" ) )
211 else if(
name == wxT(
"rvViaOuter" ) )
213 else if(
name == wxT(
"rlMinViaOuter" ) )
215 else if(
name == wxT(
"rlMaxViaOuter" ) )
217 else if(
name == wxT(
"mdWireWire" ) )
221 child = child->GetNext();
234 m_timestamp( wxDateTime::Now().GetValue().GetValue() )
236 using namespace std::placeholders;
278 wxFileInputStream input( aFileName );
289 wxTextInputStream
text( input );
291 for(
int i = 0; i < 8; i++ )
296 if(
text.ReadLine().Contains( wxS(
"<eagle" ) ) )
306 const unsigned PROGRESS_DELTA = 50;
327 return VECTOR2I( kz - aTextThickness, kz - aTextThickness );
332 const std::map<std::string, UTF8>* aProperties,
PROJECT* aProject )
345 m_board->SetFileName( aFileName );
348 unique_ptr<BOARD> deleter( aAppendToMe ?
nullptr :
m_board );
360 wxFileName fn = aFileName;
363 wxFFileInputStream stream( fn.GetFullPath() );
368 fn.GetFullPath() ) );
374 wxXmlDocument xmlDocument;
375 std::unique_ptr<wxXmlDocument> binDocument;
383 std::vector<uint8_t> bytes;
384 bytes.resize(
static_cast<size_t>( stream.GetLength() ) );
385 stream.Read( bytes.data(), bytes.size() );
387 if( stream.LastRead() != bytes.size() )
390 fn.GetFullPath() ) );
394 binDocument = binParser.
Parse( bytes );
395 doc = binDocument->GetRoot();
399 if( !xmlDocument.Load( stream ) )
402 fn.GetFullPath() ) );
405 doc = xmlDocument.GetRoot();
432 NETCLASS defaults( wxT(
"dummy" ) );
434 auto finishNetclass =
435 [&](
const std::shared_ptr<NETCLASS>& netclass )
439 if( !netclass->HasClearance() )
444 if( netclass->GetTrackWidth() == INT_MAX )
447 if( netclass->GetViaDiameter() == INT_MAX )
450 if( netclass->GetViaDrill() == INT_MAX )
454 std::shared_ptr<NET_SETTINGS>& netSettings = bds.
m_NetSettings;
456 finishNetclass( netSettings->GetDefaultNetclass() );
458 for(
const auto& [
name, netclass] : netSettings->GetNetclasses() )
459 finishNetclass( netclass );
461 m_board->m_LegacyNetclassesLoaded =
true;
462 m_board->m_LegacyDesignSettingsLoaded =
true;
468 fn.SetExt( wxT(
"kicad_dru" ) );
470 wxFile rulesFile( fn.GetFullPath(), wxFile::write );
475 wxASSERT(
m_xpath->Contents().size() == 0 );
479 wxString errmsg = exc.what();
481 errmsg += wxT(
"\n@ " );
491 LSET enabledLayers =
m_board->GetDesignSettings().GetEnabledLayers();
493 for(
const auto& [eagleLayerName, layer] :
m_layer_map )
496 enabledLayers.
set( layer );
499 m_board->GetDesignSettings().SetEnabledLayers( enabledLayers );
510 std::vector<FOOTPRINT*> retval;
513 retval.push_back(
static_cast<FOOTPRINT*
>( footprint->Clone() ) );
549 wxXmlNode* drawing =
MapChildren( aDoc )[
"drawing"];
552 wxXmlNode* board = drawingChildren[
"board"];
555 auto count_children =
556 [
this]( wxXmlNode* aNode )
560 wxXmlNode* child = aNode->GetChildren();
565 child = child->GetNext();
570 wxXmlNode* designrules = boardChildren[
"designrules"];
571 wxXmlNode* layers = drawingChildren[
"layers"];
572 wxXmlNode* plain = boardChildren[
"plain"];
573 wxXmlNode* classes = boardChildren[
"classes"];
574 wxXmlNode* signals = boardChildren[
"signals"];
575 wxXmlNode* libs = boardChildren[
"libraries"];
576 wxXmlNode* elems = boardChildren[
"elements"];
583 count_children( designrules );
584 count_children( layers );
585 count_children( plain );
586 count_children( signals );
587 count_children( elems );
592 libs = libs->GetNext();
596 libs = boardChildren[
"libraries"];
599 m_xpath->push(
"eagle.drawing" );
638 m_xpath->push(
"designrules" );
657 wxXmlNode* layerNode = aLayers->GetChildren();
664 ELAYER elayer( layerNode );
670 cu.push_back( elayer );
672 layerNode = layerNode->GetNext();
676 int ki_layer_count = 0;
678 for(
EITER it = cu.begin(); it != cu.end(); ++it, ++ki_layer_count )
680 if( ki_layer_count == 0 )
684 else if( ki_layer_count ==
int( cu.size()-1 ) )
698 m_board->SetCopperLayerCount( cu.size() );
700 for(
EITER it = cu.begin(); it != cu.end(); ++it )
707 m_board->SetLayerName( layer, it->name );
717#define DIMENSION_PRECISION DIM_PRECISION::X_XX
728 wxXmlNode* gr = aGraphics->GetChildren();
735 wxString grName = gr->GetName();
737 if( grName == wxT(
"wire" ) )
754 width =
m_board->GetDesignSettings().GetLineThickness( layer );
780 else if( grName == wxT(
"text" ) )
821 if( degrees > 90 && degrees <= 270 )
846 offset.
y = -signY * (int) textbox.
GetHeight();
858 offset.
x = signX * (int) textbox.
GetWidth();
864 offset.
x = -signX * (int) textbox.
GetWidth();
923 if( pcbtxt->
IsMirrored() && degrees < -90 && degrees >= -270 )
941 else if( grName == wxT(
"circle" ) )
960 int outlineRadius =
radius + ( width / 2 );
966 VECTOR2I rotatedPoint( outlineRadius, 0 );
974 int innerRadius =
radius - ( width / 2 );
980 VECTOR2I rotatedPoint( innerRadius, 0 );
1007 else if( grName == wxT(
"rectangle" ) )
1027 const int outlineIdx = -1;
1046 else if( grName == wxT(
"hole" ) )
1056 footprint->
SetReference( wxString::Format( wxT(
"UNK_HOLE_%d" ), hole_count ) );
1059 LIB_ID fpid( wxEmptyString, wxString::Format( wxT(
"dummyfp%d" ), hole_count ) );
1066 else if( grName == wxT(
"frame" ) )
1070 else if( grName == wxT(
"polygon" ) )
1076 else if( grName == wxT(
"dimension" ) )
1110 dimension->
SetEnd( pt2 );
1145 int newY = ( pt1.
y + pt2.
y ) / 2;
1151 int newX = ( pt1.
x + pt2.
x ) / 2;
1162 dimension->
SetEnd( pt2 );
1172 if(
abs( pt1.
x - pt2.
x ) < 50000 )
1174 int offset = pt3.
x - pt1.
x;
1181 else if(
abs( pt1.
y - pt2.
y ) < 50000 )
1183 int offset = pt3.
y - pt1.
y;
1216 wxString urn = aLib->GetAttribute(
"urn" );
1225 if( !urn.IsEmpty() )
1226 libraryUrn.
Parse( urn );
1229 wxXmlNode* packages =
MapChildren( aLib )[
"packages"];
1248 libKey += wxS(
"_" ) + libraryUrn.
assetId;
1256 wxXmlNode*
package = packages->GetChildren();
1262 m_xpath->push(
"package",
"name" );
1264 wxString pack_ref = package->GetAttribute(
"name" );
1268 m_xpath->Value( pack_ref.ToUTF8() );
1270 wxString key = aLibName ?
makeKey( libKey, pack_ref ) : pack_ref;
1275 auto r =
m_templates.insert( { key, footprint } );
1279 wxString lib = aLibName ? *aLibName :
m_lib_path;
1280 const wxString& pkg = pack_ref;
1282 wxString emsg = wxString::Format(
_(
"<package> '%s' duplicated in <library> '%s'" ),
1290 package = package->GetNext();
1302 m_xpath->push(
"libraries.library",
"name" );
1305 wxXmlNode*
library = aLibs->GetChildren();
1309 const wxString& lib_name =
library->GetAttribute(
"name" );
1311 m_xpath->Value( lib_name.c_str() );
1325 m_xpath->push(
"elements.element",
"name" );
1329 bool refanceNamePresetInPackageLayout;
1330 bool valueNamePresetInPackageLayout;
1333 wxXmlNode* element = aElements->GetChildren();
1339 if( element->GetName() != wxT(
"element" ) )
1342 element = element->GetNext();
1349 EATTR* nameAttr =
nullptr;
1350 EATTR* valueAttr =
nullptr;
1367 wxString emsg = wxString::Format(
_(
"No '%s' package in library '%s'." ),
1384 const ENET* enet = &ni->second;
1389 refanceNamePresetInPackageLayout =
true;
1390 valueNamePresetInPackageLayout =
true;
1397 refanceNamePresetInPackageLayout =
false;
1401 if( footprint->
GetValue().size() == 0 )
1404 valueNamePresetInPackageLayout =
false;
1407 wxString reference = e.
name;
1413 if( reference.find_first_not_of(
"0123456789" ) != 0 )
1414 reference.Prepend(
"UNK" );
1418 if( reference.find_first_not_of(
"#" ) != 0 )
1419 reference.Prepend(
"UNK" );
1422 if( reference.find_last_not_of(
"0123456789" ) == (reference.Length()-1) )
1423 reference.Append(
"0" );
1431 if( valueNamePresetInPackageLayout )
1434 if( refanceNamePresetInPackageLayout )
1444 m_xpath->push(
"attribute",
"name" );
1454 wxXmlNode* attribute = element->GetChildren();
1458 if( attribute->GetName() != wxT(
"attribute" ) )
1460 attribute = attribute->GetNext();
1464 EATTR a( attribute );
1466 if( a.
name == wxT(
"NAME" ) )
1479 nameAttr->
name = reference;
1481 if( refanceNamePresetInPackageLayout )
1488 if( refanceNamePresetInPackageLayout )
1497 if( refanceNamePresetInPackageLayout )
1500 nameAttr->
name = nameAttr->
name + wxT(
" = " ) + e.
name;
1511 if( refanceNamePresetInPackageLayout )
1521 else if( a.
name == wxT(
"VALUE" ) )
1535 if( valueNamePresetInPackageLayout )
1541 if( valueNamePresetInPackageLayout )
1544 footprint->
SetValue( wxT(
"VALUE" ) );
1548 if( valueNamePresetInPackageLayout )
1562 if( valueNamePresetInPackageLayout )
1574 attribute = attribute->GetNext();
1583 element = element->GetNext();
1600 wxLogMessage( wxString::Format(
_(
"Ignoring a polygon since Eagle layer '%s' (%d) was not mapped" ),
1607 std::unique_ptr<ZONE> zone = std::make_unique<ZONE>(
m_board );
1610 zone->SetLayer( layer );
1615 wxXmlNode* vertex = aPolyNode->GetChildren();
1616 std::vector<EVERTEX> vertices;
1624 if( vertex->GetName() == wxT(
"vertex" ) )
1625 vertices.emplace_back( vertex );
1627 vertex = vertex->GetNext();
1637 if( vertices.size() < 3 )
1639 wxLogMessage( wxString::Format(
_(
"Skipping a polygon on layer '%s' (%d): less than 3 vertices" ),
1645 vertices.push_back( vertices[0] );
1650 for(
size_t i = 0; i < vertices.size() - 1; i++ )
1668 double delta_angle = angle / segCount;
1670 for(
double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta_angle ); a -= delta_angle )
1685 wxLogMessage( wxString::Format(
_(
"Skipping a polygon on layer '%s' (%d): outline count is not 1" ),
1692 zone->AddPolygon( polygon.
COutline( 0 ) );
1697 zone->SetIsRuleArea(
true );
1698 zone->SetDoNotAllowVias(
false );
1699 zone->SetDoNotAllowTracks(
false );
1700 zone->SetDoNotAllowPads(
false );
1701 zone->SetDoNotAllowFootprints(
false );
1702 zone->SetDoNotAllowZoneFills(
true );
1712 zone->SetHatchOrientation(
ANGLE_0 );
1723 zone->SetLocalClearance( 1 );
1740 zone->SetAssignedPriority( rank );
1742 ZONE* zonePtr = zone.release();
1750 const EATTR* aNameAttr,
const EATTR* aValueAttr )
1771 const EATTR* aAttr )
1777 const EATTR& a = *aAttr;
1796 int textThickness =
KiROUND( fontz.
y * ratio / 100.0 );
1826 if( degrees == 90 || degrees == 0 || spin )
1830 else if( degrees == 180 )
1835 else if( degrees == 270 )
1838 degrees =
sign * 90;
1842 degrees = 90 - (
sign * degrees);
1899 if( aFPText->
IsMirrored() && degrees < -90 && degrees >= -270 )
1936 std::unique_ptr<FOOTPRINT> m = std::make_unique<FOOTPRINT>(
m_board );
1939 fpID.
Parse( aPkgName,
true );
1943 wxXmlNode* packageItem = aPackage->GetChildren();
1948 m.get()->Value().SetLayer( layer );
1950 while( packageItem )
1952 const wxString& itemName = packageItem->GetName();
1954 if( itemName == wxT(
"description" ) )
1957 m->SetLibDescription( descr );
1959 else if( itemName == wxT(
"wire" ) )
1961 else if( itemName == wxT(
"pad" ) )
1963 else if( itemName == wxT(
"text" ) )
1965 else if( itemName == wxT(
"rectangle" ) )
1967 else if( itemName == wxT(
"polygon" ) )
1969 else if( itemName == wxT(
"circle" ) )
1971 else if( itemName == wxT(
"hole" ) )
1973 else if( itemName == wxT(
"smd" ) )
1976 packageItem = packageItem->GetNext();
1993 wxLogMessage( wxString::Format(
_(
"Ignoring a wire since Eagle layer '%s' (%d) was not mapped" ),
2052 aFootprint->
Add( dwg );
2063 std::unique_ptr<PAD>
pad = std::make_unique<PAD>( aFootprint );
2073 pad->SetDrillSize(
VECTOR2I( eagleDrillz, eagleDrillz ) );
2125 double drillz =
pad->GetDrillSize().x;
2126 double annulus = drillz *
m_rules->rvPadTop;
2128 int diameter =
KiROUND( drillz + 2 * annulus );
2136 sz.
x = ( sz.
x * ( 100 +
m_rules->psElongationLong ) ) / 100;
2141 int offset =
KiROUND( ( sz.
x - sz.
y ) / 2.0 );
2152 if(
pad->GetSizeX() > 0 &&
pad->GetSizeY() > 0 &&
pad->HasHole() )
2154 aFootprint->
Add(
pad.release() );
2161 wxLogError(
_(
"Invalid zero-sized pad ignored in\nfile: %s" ),
m_board->GetFileName() );
2163 wxLogError(
_(
"Invalid zero-sized pad ignored in\nfile: %s" ), fileName.GetFullName() );
2175 wxLogMessage( wxString::Format(
_(
"Ignoring a text since Eagle layer '%s' (%d) was not mapped" ),
2183 if( t.
text.Upper() == wxT(
">NAME" ) && aFootprint->
GetReference().IsEmpty() )
2187 textItem->
SetText( wxT(
"REF**" ) );
2189 else if( t.
text.Upper() == wxT(
">VALUE" ) && aFootprint->
GetValue().IsEmpty() )
2191 textItem = &aFootprint->
Value();
2197 textItem =
new PCB_TEXT( aFootprint );
2198 aFootprint->
Add( textItem );
2286 ZONE* zone =
new ZONE( aFootprint );
2291 const int outlineIdx = -1;
2313 wxLogMessage( wxString::Format(
_(
"Ignoring a rectangle since Eagle layer '%s' (%d) was not mapped" ),
2321 aFootprint->
Add( dwg );
2327 std::vector<VECTOR2I> pts;
2332 pts.push_back( start );
2335 pts.push_back(
end );
2352 std::vector<VECTOR2I> pts;
2355 wxXmlNode* vertex = aTree->GetChildren();
2356 std::vector<EVERTEX> vertices;
2364 if( vertex->GetName() == wxT(
"vertex" ) )
2365 vertices.emplace_back( vertex );
2367 vertex = vertex->GetNext();
2370 vertices.push_back( vertices[0] );
2372 for(
size_t i = 0; i < vertices.size() - 1; i++ )
2395 double delta = angle / segCount;
2397 for(
double a = end_angle + angle; fabs( a - end_angle ) > fabs(
delta ); a -=
delta )
2412 ZONE* zone =
new ZONE( aFootprint );
2428 wxLogMessage( wxString::Format(
_(
"Ignoring a polygon since Eagle layer '%s' (%d) was not mapped" ),
2436 aFootprint->
Add( dwg );
2462 ZONE* zone =
new ZONE( aFootprint );
2469 int outlineRadius =
radius + ( width / 2 );
2475 VECTOR2I rotatedPoint( outlineRadius, 0 );
2483 int innerRadius =
radius - ( width / 2 );
2489 VECTOR2I rotatedPoint( innerRadius, 0 );
2504 wxLogMessage( wxString::Format(
_(
"Ignoring a circle since Eagle layer '%s' (%d) was not mapped" ),
2520 aFootprint->
Add( gr );
2523 switch( (
int) layer )
2565 pad->SetPosition( padpos );
2574 pad->SetDrillSize( sz );
2598 pad->SetLayer( layer );
2604 pad->SetLayerSet( front );
2605 else if( layer ==
B_Cu )
2606 pad->SetLayerSet( back );
2608 int minPadSize = std::min( padSize.
x, padSize.
y );
2612 (
int) ( minPadSize *
m_rules->srRoundness ),
2613 m_rules->srMaxRoundness * 2 );
2617 double roundRatio = (double) roundRadius / minPadSize / 2.0;
2621 roundRatio = std::fmax( *e.
roundness / 200.0, roundRatio );
2634 (
int) (
m_rules->mvCreamFrame * minPadSize ),
2641 pad->SetLayerSet(
pad->GetLayerSet().set(
F_Mask,
false ) );
2642 else if( layer ==
B_Cu )
2643 pad->SetLayerSet(
pad->GetLayerSet().set(
B_Mask,
false ) );
2650 pad->SetLayerSet(
pad->GetLayerSet().set(
F_Paste,
false ) );
2651 else if( layer ==
B_Cu )
2652 pad->SetLayerSet(
pad->GetLayerSet().set(
B_Paste,
false ) );
2667 (
int) (
m_rules->mvStopFrame * std::min( padSize.
x, padSize.
y ) ),
2675 wxCHECK( footprint, );
2685 footprint->SetParent(
nullptr );
2701 m_xpath->push(
"classes.class",
"number" );
2703 std::vector<ECLASS> eClasses;
2704 wxXmlNode* classNode = aClasses->GetChildren();
2710 ECLASS eClass( classNode );
2711 std::shared_ptr<NETCLASS> netclass;
2713 if( eClass.
name.CmpNoCase( wxT(
"default" ) ) == 0 )
2723 netclass->SetTrackWidth( INT_MAX );
2724 netclass->SetViaDiameter( INT_MAX );
2725 netclass->SetViaDrill( INT_MAX );
2727 eClasses.emplace_back( eClass );
2731 auto clearanceToDefaultIt = eClass.
clearanceMap.find( wxT(
"0" ) );
2733 if( clearanceToDefaultIt != eClass.
clearanceMap.end() )
2735 netclass->SetClearance( clearanceToDefaultIt->second.ToPcbUnits() );
2739 classNode = classNode->GetNext();
2744 for(
ECLASS& eClass : eClasses )
2746 for(
const auto& [className, pt] : eClass.clearanceMap )
2749 if( className == wxT(
"0" ) )
2755 rule.Printf( wxT(
"(rule \"class %s:%s\"\n"
2756 " (condition \"A.NetClass == '%s' && B.NetClass == '%s'\")\n"
2757 " (constraint clearance (min %smm)))\n" ),
2784 m_xpath->push(
"signals.signal",
"name" );
2787 wxXmlNode* net = aSignals->GetChildren();
2793 bool sawPad =
false;
2797 const wxString& netName =
escapeName( net->GetAttribute(
"name" ) );
2799 std::shared_ptr<NETCLASS> netclass;
2801 if( net->HasAttribute(
"class" ) )
2803 auto netclassIt =
m_classMap.find( net->GetAttribute(
"class" ) );
2809 netclass = netclassIt->second;
2815 m_xpath->Value( netName.c_str() );
2818 wxXmlNode* netItem = net->GetChildren();
2823 const wxString& itemName = netItem->GetName();
2825 if( itemName == wxT(
"wire" ) )
2842 if( netclass && width < netclass->GetTrackWidth() )
2843 netclass->SetTrackWidth( width );
2853 double radiusA = ( mid -
center ).EuclideanNorm();
2854 double radiusB = ( otherMid -
center ).EuclideanNorm();
2857 std::swap( mid, otherMid );
2890 else if( itemName == wxT(
"via" ) )
2902 && layer_front_most != layer_back_most )
2916 double annulus = drillz *
m_rules->rvViaOuter;
2918 kidiam =
KiROUND( drillz + 2 * annulus );
2922 via->SetDrill( drillz );
2928 if( !v.
diam || via_width <= via->GetDrill() )
2930 double annular_width = ( via_width -
via->GetDrill() ) / 2.0;
2940 if( netclass && kidiam < netclass->GetViaDiameter() )
2941 netclass->SetViaDiameter( kidiam );
2946 if( netclass && drillz < netclass->GetViaDrill() )
2947 netclass->SetViaDrill( drillz );
2952 if( layer_front_most ==
F_Cu && layer_back_most ==
B_Cu )
2956 else if( layer_front_most ==
F_Cu || layer_back_most ==
B_Cu )
2967 via->SetLayerPair( layer_front_most, layer_back_most );
2968 via->SetPosition( pos );
2971 via->SetNetCode( netCode );
2977 else if( itemName == wxT(
"contactref" ) )
2979 m_xpath->push(
"contactref" );
2982 const wxString& reference = netItem->GetAttribute(
"element" );
2983 const wxString&
pad = netItem->GetAttribute(
"pad" );
2993 else if( itemName == wxT(
"polygon" ) )
3000 zones.push_back( zone );
3002 if( !zone->GetIsRuleArea() )
3003 zone->SetNetCode( netCode );
3009 netItem = netItem->GetNext();
3012 if( zones.size() && !sawPad )
3016 for(
ZONE* zone : zones )
3026 net = net->GetNext();
3034 const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
3036 std::map<wxString, PCB_LAYER_ID> layer_map;
3041 layer_map.emplace( layer.Name, layerId );
3050 std::vector<INPUT_LAYER_DESC> inputDescs;
3052 for (
const std::pair<const int, ELAYER>& layerPair :
m_eagleLayers )
3054 const ELAYER& eLayer = layerPair.second;
3065 inputDescs.push_back( layerDesc );
3086 bool aIsLibraryCache )
const
3095 if( copperLayer >= 0 )
3096 copperLayers[copperLayer] =
true;
3103 bool required =
false;
3104 LSET permittedLayers;
3106 permittedLayers.
set();
3109 switch( aEagleLayer )
3207 if( aIsLibraryCache )
3215 return {
PCB_LAYER_ID( kiLayer ), permittedLayers, required };
3221 static const wxString
unknown(
"unknown" );
3229 static const int unknown = -1;
3242 if(
auto it =
m_props->find(
"page_width" ); it !=
m_props->end() )
3243 page_width = it->second;
3245 if(
auto it =
m_props->find(
"page_height" ); it !=
m_props->end() )
3246 page_height = it->second;
3248 if( !page_width.
empty() && !page_height.
empty() )
3252 int w = atoi( page_width.
c_str() );
3253 int h = atoi( page_height.
c_str() );
3255 int desired_x = ( w - bbbox.
GetWidth() ) / 2;
3256 int desired_y = ( h - bbbox.
GetHeight() ) / 2;
3267 if( aPath.IsEmpty() )
3268 return wxDateTime::Now().GetValue().GetValue();
3270 wxFileName fn( aPath );
3272 if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
3273 return fn.GetModificationTime().GetValue().GetValue();
3302 string filename = (
const char*) aLibPath.char_str( wxConvFile );
3305 wxFileName fn( filename );
3306 wxFFileInputStream stream( fn.GetFullPath() );
3307 wxXmlDocument xmlDocument;
3309 if( !stream.IsOk() || !xmlDocument.Load( stream ) )
3310 THROW_IO_ERROR( wxString::Format(
_(
"Unable to read file '%s'." ), fn.GetFullPath() ) );
3312 doc = xmlDocument.GetRoot();
3314 wxXmlNode* drawing =
MapChildren( doc )[
"drawing"];
3320 m_xpath->push(
"eagle.drawing.layers" );
3321 wxXmlNode* layers = drawingChildren[
"layers"];
3326 m_xpath->push(
"eagle.drawing.library" );
3327 wxXmlNode*
library = drawingChildren[
"library"];
3361 bool aBestEfforts,
const std::map<std::string, UTF8>* aProperties )
3365 init( aProperties );
3373 errorMsg = ioe.
What();
3380 aFootprintNames.Add(
name );
3382 if( !errorMsg.IsEmpty() && !aBestEfforts )
3388 bool aKeepUUID,
const std::map<std::string, UTF8>* aProperties )
3390 init( aProperties );
3399 copy->SetParent(
nullptr );
3406 int minLayerCount = 2;
3408 std::map<wxString, PCB_LAYER_ID>::const_iterator it;
3419 if( ( ordinal + 2 ) > minLayerCount )
3420 minLayerCount = ordinal + 2;
3426 if( ( minLayerCount % 2 ) != 0 )
3429 return minLayerCount;
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Returns # of elements in an array.
constexpr int ARC_HIGH_DEF
constexpr EDA_IU_SCALE pcbIUScale
#define DEFAULT_SILK_LINE_WIDTH
#define DEFAULT_EDGE_WIDTH
#define DEFAULT_LINE_WIDTH
#define DEFAULT_COURTYARD_WIDTH
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
BASE_SET & set(size_t pos)
virtual bool SetNetCode(int aNetCode, bool aNoAssert)
Set net using a net code.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Container for design settings for a BOARD object.
std::shared_ptr< NET_SETTINGS > m_NetSettings
VECTOR2I GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
int m_ViasMinAnnularWidth
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.
FOOTPRINT * GetParentFootprint() const
Information pertinent to a Pcbnew printed circuit board.
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
constexpr coord_type GetY() const
constexpr size_type GetWidth() const
constexpr coord_type GetX() const
constexpr size_type GetHeight() const
Read-only parser for the pre-v6 binary Eagle .brd format.
std::unique_ptr< wxXmlDocument > Parse(const std::vector< uint8_t > &aBytes)
Parse a binary Eagle board into an XML DOM compatible with the XML walker.
static bool IsBinaryEagle(wxInputStream &aStream)
Probe the first two bytes for the binary magic.
void SetCenter(const VECTOR2I &aCenter)
SHAPE_POLY_SET & GetPolyShape()
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 SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
const EDA_ANGLE & GetTextAngle() const
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
void SetTextPos(const VECTOR2I &aPoint)
void SetMirrored(bool isMirrored)
void SetVertJustify(GR_TEXT_V_ALIGN_T aType)
GR_TEXT_H_ALIGN_T GetHorizJustify() const
virtual void SetVisible(bool aVisible)
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
void SetKeepUpright(bool aKeepUpright)
GR_TEXT_V_ALIGN_T GetVertJustify() const
virtual void SetText(const wxString &aText)
virtual void SetTextAngle(const EDA_ANGLE &aAngle)
VECTOR2I GetTextSize() const
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
RAII class to set and restore the fontconfig reporter.
virtual bool CanReadLibrary(const wxString &aFileName) const
Checks if this IO object can read the specified library file/directory.
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
virtual void RegisterCallback(LAYER_MAPPING_HANDLER aLayerMappingHandler)
Register a different handler to be called when mapping of input layers to KiCad layers occurs.
LAYER_MAPPING_HANDLER m_layer_mapping_handler
Callback to get layer mapping.
A logical library item identifier and consists of various portions much like a URI.
int Parse(const UTF8 &aId, bool aFix=false)
Parse LIB_ID with the information from aId.
const UTF8 & GetLibItemName() const
static LOAD_INFO_REPORTER & GetInstance()
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.
A collection of nets and the parameters used to route or test these nets.
int GetViaDiameter() const
int GetTrackWidth() const
Handle the data for a net.
void SetNetClass(const std::shared_ptr< NETCLASS > &aNetClass)
static const int UNCONNECTED
Constant that holds the "unconnected net" number (typically 0) all items "connected" to this net are ...
void SetNetclassPatternAssignment(const wxString &pattern, const wxString &netclass)
Sets a netclass pattern assignment Calling this method will reset the effective netclass calculation ...
std::shared_ptr< NETCLASS > GetDefaultNetclass()
Gets the default netclass for the project.
void SetNetclass(const wxString &netclassName, std::shared_ptr< NETCLASS > &netclass)
Sets the given netclass Calling user is responsible for resetting the effective netclass calculation ...
T & Get()
Return a reference to the value of the attribute assuming it is available.
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
void SetNumber(const wxString &aNumber)
Set the pad number (note that it can be alphanumeric, such as the array reference "AA12").
void SetLocalSolderMaskMargin(std::optional< int > aMargin)
void SetLocalZoneConnection(ZONE_CONNECTION aType)
void SetPosition(const VECTOR2I &aPos) override
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
void SetPosition(const VECTOR2I &aPos) override
void SetMid(const VECTOR2I &aMid)
void SetUnits(EDA_UNITS aUnits)
void SetLineThickness(int aWidth)
virtual void SetEnd(const VECTOR2I &aPoint)
void SetPrecision(DIM_PRECISION aPrecision)
virtual void SetStart(const VECTOR2I &aPoint)
void SetOverrideText(const wxString &aValue)
For better understanding of the points that make a dimension:
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
A leader is a dimension-like object pointing to a specific point.
A radial dimension indicates either the radius or diameter of an arc or circle.
std::vector< ELAYER > ELAYERS
void packageSMD(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
Handles common pad properties.
void loadPlain(wxXmlNode *aPlain)
int m_min_trace
smallest trace we find on Load(), in BIU.
std::map< wxString, PCB_LAYER_ID > m_layer_map
Map of Eagle layers to KiCad layers.
void orientFPText(FOOTPRINT *aFootprint, const EELEMENT &e, PCB_TEXT *aFPText, const EATTR *aAttr)
VECTOR2I kicad_fontsize(const ECOORD &d, int aTextThickness) const
create a font size (fontz) from an eagle font size scalar and KiCad font thickness
std::map< wxString, PCB_LAYER_ID > DefaultLayerMappingCallback(const std::vector< INPUT_LAYER_DESC > &aInputLayerDescriptionVector)
Return the automapped layers.
bool checkHeader(const wxString &aFileName) const
void loadElements(wxXmlNode *aElements)
FOOTPRINT * makeFootprint(wxXmlNode *aPackage, const wxString &aPkgName)
Create a FOOTPRINT from an Eagle package.
int m_min_hole
smallest diameter hole we find on Load(), in BIU.
std::vector< FOOTPRINT * > GetImportedCachedLibraryFootprints() override
Return a container with the cached library footprints generated in the last call to Load.
XPATH * m_xpath
keeps track of what we are working on within XML document during a Load().
unsigned m_totalCount
for progress reporting
void cacheLib(const wxString &aLibraryPath)
This PLUGIN only caches one footprint library, this determines which one.
int m_hole_count
generates unique footprint names from eagle "hole"s.
std::map< wxString, FOOTPRINT * > m_templates
is part of a FOOTPRINT factory that operates using copy construction.
std::map< wxString, int > m_eagleLayersIds
Eagle layer ids stored by layer name.
void mapEagleLayersToKicad(bool aIsLibraryCache=false)
Generate mapping between Eagle and KiCad layers.
std::map< wxString, std::shared_ptr< NETCLASS > > m_classMap
std::tuple< PCB_LAYER_ID, LSET, bool > defaultKicadLayer(int aEagleLayer, bool aIsLibraryCache=false) const
Get the default KiCad layer corresponding to an Eagle layer of the board, a set of sensible layer map...
BOARD * LoadBoard(const wxString &aFileName, BOARD *aAppendToMe, const std::map< std::string, UTF8 > *aProperties=nullptr, PROJECT *aProject=nullptr) override
Load information from some input file format that this PCB_IO implementation knows about into either ...
void loadLibraries(wxXmlNode *aLibs)
void init(const std::map< std::string, UTF8 > *aProperties)
initialize PLUGIN like a constructor would, and futz with fresh BOARD if needed.
FOOTPRINT * FootprintLoad(const wxString &aLibraryPath, const wxString &aFootprintName, bool aKeepUUID=false, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Load a footprint having aFootprintName from the aLibraryPath containing a library format that this PC...
int eagle_layer_id(const wxString &aLayerName) const
Get Eagle layer number by its name.
void loadAllSections(wxXmlNode *aDocument)
bool CanReadFootprint(const wxString &aFileName) const override
Checks if this PCB_IO can read a footprint from specified file or directory.
void packageWire(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
bool CanReadBoard(const wxString &aFileName) const override
Checks if this PCB_IO can read the specified board file.
const wxString & eagle_layer_name(int aLayer) const
Get Eagle layer name by its number.
ELAYERS::const_iterator EITER
void packageRectangle(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
int kicad_y(const ECOORD &y) const
Convert an Eagle distance to a KiCad distance.
void loadClasses(wxXmlNode *aClasses)
std::map< int, ELAYER > m_eagleLayers
Eagle layer data stored by layer number.
NET_MAP m_pads_to_nets
net list
void packageText(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
int getMinimumCopperLayerCount() const
Determines the minimum copper layer stackup count that includes all mapped layers.
void packageHole(FOOTPRINT *aFootprint, wxXmlNode *aTree, bool aCenter) const
void packagePolygon(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
void centerBoard()
move the BOARD into the center of the page
unsigned m_lastProgressCount
int m_cu_map[17]
map eagle to KiCad, cu layers only.
ZONE * loadPolygon(wxXmlNode *aPolyNode)
Load a copper or keepout polygon and adds it to the board.
int m_min_via
smallest via we find on Load(), in BIU.
bool CanReadLibrary(const wxString &aFileName) const override
Checks if this IO object can read the specified library file/directory.
void packageCircle(FOOTPRINT *aFootprint, wxXmlNode *aTree) const
void loadLibrary(wxXmlNode *aLib, const wxString *aLibName)
Load the Eagle "library" XML element, which can occur either under a "libraries" element (if a *....
void packagePad(FOOTPRINT *aFootprint, wxXmlNode *aTree)
void loadSignals(wxXmlNode *aSignals)
ERULES * m_rules
Eagle design rules.
PCB_LAYER_ID kicad_layer(int aLayer) const
Convert an Eagle layer to a KiCad layer.
void orientFootprintAndText(FOOTPRINT *aFootprint, const EELEMENT &e, const EATTR *aNameAttr, const EATTR *aValueAttr)
long long GetLibraryTimestamp(const wxString &aLibraryPath) const override
Generate a timestamp representing all the files in the library (including the library directory).
int m_min_annulus
smallest via annulus we find on Load(), in BIU.
void loadLayerDefs(wxXmlNode *aLayers)
void FootprintEnumerate(wxArrayString &aFootprintNames, const wxString &aLibraryPath, bool aBestEfforts, const std::map< std::string, UTF8 > *aProperties=nullptr) override
Return a list of footprint names contained within the library at aLibraryPath.
void setKeepoutSettingsToZone(ZONE *aZone, int aLayer) const
void transferPad(const EPAD_COMMON &aEaglePad, PAD *aPad) const
Deletes the footprint templates list.
int kicad_x(const ECOORD &x) const
void loadDesignRules(wxXmlNode *aDesignRules)
PROGRESS_REPORTER * m_progressReporter
optional; may be nullptr
BOARD * m_board
The board BOARD being worked on, no ownership here.
virtual bool CanReadBoard(const wxString &aFileName) const
Checks if this PCB_IO can read the specified board file.
PCB_IO(const wxString &aName)
const std::map< std::string, UTF8 > * m_props
Properties passed via Save() or Load(), no ownership, may be NULL.
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle) override
Rotate this object.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void SetStroke(const STROKE_PARAMS &aStroke) override
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
virtual void SetPosition(const VECTOR2I &aPos) override
void SetEnd(const VECTOR2I &aEnd)
void SetPosition(const VECTOR2I &aPos) override
virtual void SetWidth(int aWidth)
Container for project specific data.
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.
Represent a set of closed polygons.
int AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
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.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Simple container to manage line stroke parameters.
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
const char * c_str() const
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Keep track of what we are working on within a PTREE.
Handle a list of polygons defining a copper zone.
void SetDoNotAllowPads(bool aEnable)
void SetBorderDisplayStyle(ZONE_BORDER_DISPLAY_STYLE aBorderHatchStyle, int aBorderHatchPitch, bool aRebuilBorderdHatch)
Set all hatch parameters for the zone.
virtual void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
SHAPE_POLY_SET * Outline()
void NewHole()
Create a new hole on the zone; i.e., a new contour on the zone's outline.
bool SetNetCode(int aNetCode, bool aNoAssert) override
Override that clamps the netcode to 0 when this zone is in copper-thieving fill mode.
void SetIsRuleArea(bool aEnable)
void SetDoNotAllowTracks(bool aEnable)
void Rotate(const VECTOR2I &aCentre, const EDA_ANGLE &aAngle) override
Rotate the outlines.
void SetLayerSet(const LSET &aLayerSet) override
void SetDoNotAllowVias(bool aEnable)
void SetDoNotAllowFootprints(bool aEnable)
bool AppendCorner(VECTOR2I aPosition, int aHoleIdx, bool aAllowDuplication=false)
Add a new corner to the zone outline (to the main outline or a hole)
void SetDoNotAllowZoneFills(bool aEnable)
static int GetDefaultHatchPitch()
@ ALLOW_ACUTE_CORNERS
just inflate the polygon. Acute angles create spikes
NODE_MAP MapChildren(wxXmlNode *aCurrentNode)
Provide an easy access to the children of an XML node via their names.
wxString escapeName(const wxString &aNetName)
Translates Eagle special characters to their counterparts in KiCad.
wxString interpretText(const wxString &aText)
Interprets special characters in Eagle text and converts them to KiCAD notation.
VECTOR2I ConvertArcCenter(const VECTOR2I &aStart, const VECTOR2I &aEnd, double aAngle)
Convert an Eagle curve end to a KiCad center for S_ARC.
wxString convertDescription(wxString aDescr)
Converts Eagle's HTML description into KiCad description format.
OPTIONAL_XML_ATTRIBUTE< wxString > opt_wxString
std::unordered_map< wxString, wxXmlNode * > NODE_MAP
static constexpr EDA_ANGLE ANGLE_0
static constexpr EDA_ANGLE FULL_CIRCLE
static constexpr EDA_ANGLE ANGLE_360
#define IGNORE_PARENT_GROUP
a few functions useful in geometry calculations.
int GetArcToSegmentCount(int aRadius, int aErrorMax, const EDA_ANGLE &aArcAngle)
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
PCB_LAYER_ID BoardLayerFromLegacyId(int aLegacyId)
Retrieve a layer ID from an integer converted from a legacy (pre-V9) enum value.
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
size_t CopperLayerToOrdinal(PCB_LAYER_ID aLayer)
Converts KiCad copper layer enum to an ordinal between the front and back layers.
PCB_LAYER_ID
A quick note on layer IDs:
@ TOP_BOTTOM
Flip top to bottom (around the X axis)
KICOMMON_API wxString StringFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=false, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Return the string from aValue according to aUnits (inch, mm ...) for display.
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)
static wxString makeKey(const wxString &aFirst, const wxString &aSecond)
Assemble a two part key as a simple concatenation of aFirst and aSecond parts, using a separator.
#define DIMENSION_PRECISION
static int parseEagle(const wxString &aDistance)
Parse an eagle distance which is either mm, or mils if there is "mil" suffix.
static T eagleClamp(T aMin, T aValue, T aMax)
std::vector< ZONE * > ZONES
NET_MAP::const_iterator NET_MAP_CITER
bool ReplaceIllegalFileNameChars(std::string &aName, int aReplaceChar)
Checks aName for illegal file name characters.
wxString UnescapeHTML(const wxString &aString)
Return a new wxString unescaped from HTML format.
Parse an Eagle "attribute" XML element.
std::map< wxString, ECOORD > clearanceMap
long long int value
Value expressed in nanometers.
opt_wxString dimensionType
Structure holding common properties for through-hole and SMD pads.
Eagle polygon, without vertices which are parsed as needed.
static const int max_priority
Eagle XML rectangle in binary.
subset of eagle.drawing.board.designrules in the XML document
int psBottom
Shape of the bottom pads.
double mvStopFrame
solderpaste mask, expressed as percentage of the smaller pad/via dimension
double srRoundness
corner rounding ratio for SMD pads (percentage)
double rlMinViaOuter
minimum copper annulus on via
int mlMaxCreamFrame
solder paste mask, maximum size (Eagle mils, here nanometers)
int mlMinCreamFrame
solder paste mask, minimum size (Eagle mils, here nanometers)
int psTop
Shape of the top pads.
double rlMaxViaOuter
maximum copper annulus on via
void parse(wxXmlNode *aRules, std::function< void()> aCheckpoint)
percent over 100%.
double mdWireWire
wire to wire spacing I presume.
double rvPadTop
top pad size as percent of drill size
double rvViaOuter
copper annulus is this percent of via hole
double rlMinPadTop
minimum copper annulus on through hole pads
int psElongationOffset
the offset of the hole within the "long" pad.
int mlMinStopFrame
solder mask, minimum size (Eagle mils, here nanometers)
int srMinRoundness
corner rounding radius, maximum size (Eagle mils, here nanometers)
int mlMaxStopFrame
solder mask, maximum size (Eagle mils, here nanometers)
double rlMaxPadTop
maximum copper annulus on through hole pads
int psFirst
Shape of the first pads.
Container that parses Eagle library file "urn" definitions.
bool IsValid() const
Check if the string passed to the ctor was a valid Eagle urn.
void Parse(const wxString &aUrn)
wxString assetId
The unique asset identifier for the asset type.
int layer_back_most
< extent
opt_double curve
range is -359.9..359.9
Implement a simple wrapper around runtime_error to isolate the errors thrown by the Eagle XML parser.
wxString result
Test unit parsing edge cases and error handling.
const VECTOR2I CalcArcMid(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aMinArcAngle=true)
Return the middle point of an arc, half-way between aStart and aEnd.
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.
double DEG2RAD(double deg)
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
constexpr int sign(T val)
VECTOR2< int32_t > VECTOR2I
ZONE_BORDER_DISPLAY_STYLE
Zone border styles.
@ THERMAL
Use thermal relief for pads.
@ FULL
pads are covered by copper
#define ZONE_THICKNESS_MIN_VALUE_MM