From 866c0698738463cf216826725654b0ea53f74616 Mon Sep 17 00:00:00 2001 From: Roberto Fernandez Bautista Date: Mon, 31 Aug 2020 23:09:29 +0100 Subject: [PATCH] CADSTAR PCB Archive Importer: Load TEMPLATEs, COPPERs and NETs (including Tracks and Vias) --- .../cadstar/cadstar_archive_parser.cpp | 15 +- .../plugins/cadstar/cadstar_archive_parser.h | 9 + .../cadstar/cadstar_pcb_archive_loader.cpp | 857 +++++++++++++++--- .../cadstar/cadstar_pcb_archive_loader.h | 108 ++- .../cadstar/cadstar_pcb_archive_parser.cpp | 19 +- .../cadstar/cadstar_pcb_archive_parser.h | 100 +- .../cadstar/cadstar_pcb_archive_plugin.cpp | 22 + 7 files changed, 974 insertions(+), 156 deletions(-) diff --git a/common/plugins/cadstar/cadstar_archive_parser.cpp b/common/plugins/cadstar/cadstar_archive_parser.cpp index 6ecf034a94..a6a54f2075 100644 --- a/common/plugins/cadstar/cadstar_archive_parser.cpp +++ b/common/plugins/cadstar/cadstar_archive_parser.cpp @@ -47,6 +47,17 @@ void CADSTAR_ARCHIVE_PARSER::POINT::Parse( XNODE* aNode ) } + + +void CADSTAR_ARCHIVE_PARSER::LONGPOINT::Parse( XNODE* aNode ) +{ + wxASSERT( aNode->GetName() == wxT( "PT" ) ); + + x = GetXmlAttributeIDLong( aNode, 0 ); + y = GetXmlAttributeIDLong( aNode, 1 ); +} + + bool CADSTAR_ARCHIVE_PARSER::VERTEX::IsVertex( XNODE* aNode ) { wxString aNodeName = aNode->GetName(); @@ -241,7 +252,7 @@ long CADSTAR_ARCHIVE_PARSER::GetXmlAttributeIDLong( XNODE* aNode, unsigned int a void CADSTAR_ARCHIVE_PARSER::CheckNoChildNodes( XNODE* aNode ) { - if( aNode->GetChildren() ) + if( aNode && aNode->GetChildren() ) { THROW_UNKNOWN_NODE_IO_ERROR( aNode->GetChildren()->GetName(), aNode->GetName() ); } @@ -250,7 +261,7 @@ void CADSTAR_ARCHIVE_PARSER::CheckNoChildNodes( XNODE* aNode ) void CADSTAR_ARCHIVE_PARSER::CheckNoNextNodes( XNODE* aNode ) { - if( aNode->GetNext() ) + if( aNode && aNode->GetNext() ) { THROW_UNKNOWN_NODE_IO_ERROR( aNode->GetNext()->GetName(), aNode->GetParent()->GetName() ); } diff --git a/common/plugins/cadstar/cadstar_archive_parser.h b/common/plugins/cadstar/cadstar_archive_parser.h index 11430f98ea..8c6359ecc9 100644 --- a/common/plugins/cadstar/cadstar_archive_parser.h +++ b/common/plugins/cadstar/cadstar_archive_parser.h @@ -82,6 +82,15 @@ public: void Parse( XNODE* aNode ); }; + + struct LONGPOINT + { + long x = UNDEFINED_VALUE; + long y = UNDEFINED_VALUE; + + void Parse( XNODE* aNode ); + }; + enum class VERTEX_TYPE { diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp index 32fcfb5380..2e5758ca90 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.cpp @@ -26,47 +26,53 @@ #include #include // KEY_COPPER, KEY_CORE, KEY_PREPREG -#include // DRAWSEGMENT -#include // std::numeric_limits -#include - #include #include +#include // DRAWSEGMENT #include #include #include #include #include #include +#include + +#include // std::numeric_limits void CADSTAR_PCB_ARCHIVE_LOADER::Load( ::BOARD* aBoard ) { mBoard = aBoard; Parse(); - wxPoint designSize = - Assignments.Technology.DesignArea.first - Assignments.Technology.DesignArea.second; + LONGPOINT designLimit = Assignments.Technology.DesignLimit; //Note: can't use getKiCadPoint() due wxPoint being int - need long long to make the check - long long designSizeXkicad = (long long) designSize.x * KiCadUnitMultiplier; - long long designSizeYkicad = (long long) designSize.y * KiCadUnitMultiplier; - long long maxDesignSizekicad = (long long) std::numeric_limits::max() - + std::abs( std::numeric_limits::min() ); + long long designSizeXkicad = (long long) designLimit.x * KiCadUnitMultiplier; + long long designSizeYkicad = (long long) designLimit.y * KiCadUnitMultiplier; + + // Max size limited by the positive dimention of wxPoint + long long maxDesignSizekicad = (long long) std::numeric_limits::max(); if( designSizeXkicad > maxDesignSizekicad || designSizeYkicad > maxDesignSizekicad ) THROW_IO_ERROR( wxString::Format( _( "The design is too large and cannot be imported into KiCad. \n" "Please reduce the maximum design size in CADSTAR by navigating to: \n" "Design Tab -> Properties -> Design Options -> Maximum Design Size. \n" - "Current Design size: %d, %d micrometers. \n" - "Maximum permitted design size: %d, %d micrometers.\n" ), - designSizeXkicad / 1000, designSizeYkicad / 1000, maxDesignSizekicad / 1000, - maxDesignSizekicad / 1000 ) ); + "Current Design size: %.2f, %.2f milimetres. \n" + "Maximum permitted design size: %.2f, %.2f milimetres.\n" ), + (double) designSizeXkicad / 1E6, (double) designSizeYkicad / 1E6, + (double) maxDesignSizekicad / 1E6, (double) maxDesignSizekicad / 1E6 ) ); mDesignCenter = ( Assignments.Technology.DesignArea.first + Assignments.Technology.DesignArea.second ) / 2; + if( Layout.NetSynch == NETSYNCH::WARNING ) + wxLogWarning( + _( "The selected file indicates that nets might be out of synchronisation " + "with the schematic. It is recommended that you carry out an 'Align Nets' " + "procedure in CADSTAR and re-import, to avoid inconsistencies between the " + "PCB and the schematic. " ) ); loadBoardStackup(); loadComponentLibrary(); @@ -74,6 +80,9 @@ void CADSTAR_PCB_ARCHIVE_LOADER::Load( ::BOARD* aBoard ) loadFigures(); loadAreas(); loadComponents(); + loadTemplates(); + loadCoppers(); + loadNets(); //TODO: process all other items @@ -166,6 +175,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadBoardStackup() kicadLayerID = getKiCadCopperLayerID( ++numElecAndPowerLayers ); kicadLayerType = BOARD_STACKUP_ITEM_TYPE::BS_ITEM_TYPE_COPPER; layerTypeName = KEY_COPPER; + mPowerPlaneLayers.push_back( curLayer.ID ); //we will need to add a Copper zone break; case LAYER_TYPE::CONSTRUCTION: @@ -427,7 +437,8 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadLibraryPads( const SYMDEF& aComponent, MODU PADCODE csPadcode = getPadCode( csPad.PadCodeID ); D_PAD* pad = new D_PAD( aModule ); - aModule->Add( pad, ADD_MODE::APPEND ); + aModule->Add( pad, ADD_MODE::INSERT ); // insert so that we get correct behaviour when finding pads + // in the module by PAD_ID - see loadNets() switch( csPad.Side ) { @@ -460,7 +471,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadLibraryPads( const SYMDEF& aComponent, MODU csPad.Identifier ); pad->SetPos0( getKiCadPoint( csPad.Position ) - aModule->GetPosition() ); - pad->SetOrientation( getKiCadAngle( csPad.OrientAngle ) ); + pad->SetOrientation( getAngleTenthDegree( csPad.OrientAngle ) ); switch( csPadcode.Shape.ShapeType ) { @@ -544,7 +555,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadLibraryPads( const SYMDEF& aComponent, MODU if( csPadcode.ReliefWidth != UNDEFINED_VALUE ) pad->SetThermalWidth( getKiCadLength( csPadcode.ReliefWidth ) ); - pad->SetOrientation( pad->GetOrientation() + getKiCadAngle( csPadcode.Shape.OrientAngle ) ); + pad->SetOrientation( pad->GetOrientation() + getAngleTenthDegree( csPadcode.Shape.OrientAngle ) ); if( csPadcode.DrillDiameter != UNDEFINED_VALUE ) { @@ -665,7 +676,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadComponents() m->SetValue( wxEmptyString ); m->SetPosition( getKiCadPoint( comp.Origin ) ); - m->SetOrientation( getKiCadAngle( comp.OrientAngle ) ); + m->SetOrientation( getAngleTenthDegree( comp.OrientAngle ) ); m->SetReference( comp.Name ); if( comp.Mirror ) @@ -676,11 +687,305 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadComponents() loadComponentAttributes( comp, m ); m->SetDescription( getPart( comp.PartID ).Definition.Name ); + + mComponentMap.insert( { comp.ID, m } ); } } -void CADSTAR_PCB_ARCHIVE_LOADER::loadComponentAttributes( const COMPONENT& aComponent, MODULE* aModule ) +void CADSTAR_PCB_ARCHIVE_LOADER::loadTemplates() +{ + for( std::pair tempPair : Layout.Templates ) + { + TEMPLATE& csTemplate = tempPair.second; + + ZONE_CONTAINER* zone = getZoneFromCadstarShape( + csTemplate.Shape, getLineThickness( csTemplate.LineCodeID ) ); + + mBoard->Add( zone, ADD_MODE::APPEND ); + + zone->SetZoneName( csTemplate.Name ); + zone->SetLayer( getKiCadLayer( csTemplate.LayerID ) ); + + if( !csTemplate.NetID.IsEmpty() ) + zone->SetNet( getKiCadNet( csTemplate.NetID ) ); + + if( csTemplate.Pouring.AllowInNoRouting ) + wxLogError( wxString::Format( + _( "The CADSTAR template '%s' has the setting 'Allow in No Routing Areas' " + "enabled. This setting has no KiCad equivalent, so it has been ignored." ), + csTemplate.Name ) ); + + if( csTemplate.Pouring.BoxIsolatedPins ) + wxLogError( wxString::Format( + _( "The CADSTAR template '%s' has the setting 'Box Isolated Pins'" + "enabled. This setting has no KiCad equivalent, so it has been ignored." ), + csTemplate.Name ) ); + + if( csTemplate.Pouring.AutomaticRepour ) + wxLogWarning( wxString::Format( + _( "The CADSTAR template '%s' has the setting 'Automatic Repour'" + "enabled. This setting has no KiCad equivalent, so it has been ignored." ), + csTemplate.Name ) ); + + // Sliver width has different behaviour to KiCad Zone's minimum thickness + // In Cadstar 'Sliver width' has to be greater than the Copper thickness, whereas in + // Kicad it is the opposite. + if( csTemplate.Pouring.SliverWidth != 0 ) + wxLogError( wxString::Format( + _( "The CADSTAR template '%s' has a non-zero value defined for the " + "'Sliver Width' setting. There is no KiCad equivalent for " + "this, so this setting was ignored." ), + csTemplate.Name ) ); + + + if( csTemplate.Pouring.MinIsolatedCopper != csTemplate.Pouring.MinDisjointCopper ) + wxLogError( wxString::Format( + _( "The CADSTAR template '%s' has different settings for 'Retain Poured Copper " + "- Disjoint' and 'Retain Poured Copper - Isolated'. KiCad does not " + "distinguish between these two settings. The setting for disjoint copper " + "has been applied as the minimum island area of the KiCad Zone." ), + csTemplate.Name ) ); + + if( csTemplate.Pouring.MinDisjointCopper < 0 ) + zone->SetMinIslandArea( -1 ); + else + zone->SetMinIslandArea( + (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper ) + * (long long) getKiCadLength( csTemplate.Pouring.MinDisjointCopper ) ); + + zone->SetZoneClearance( getKiCadLength( csTemplate.Pouring.AdditionalIsolation ) ); + + if( csTemplate.Pouring.FillType == TEMPLATE::POURING::COPPER_FILL_TYPE::HATCHED ) + { + zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN ); + zone->SetHatchGap( getKiCadHatchCodeGap( csTemplate.Pouring.HatchCodeID ) ); + zone->SetHatchThickness( getKiCadHatchCodeThickness( csTemplate.Pouring.HatchCodeID ) ); + zone->SetHatchOrientation( getHatchCodeAngleDegrees( csTemplate.Pouring.HatchCodeID ) ); + } + else + { + zone->SetFillMode( ZONE_FILL_MODE::POLYGONS ); + } + + if( csTemplate.Pouring.ThermalReliefOnPads != csTemplate.Pouring.ThermalReliefOnVias + || csTemplate.Pouring.ThermalReliefPadsAngle + != csTemplate.Pouring.ThermalReliefViasAngle ) + wxLogWarning( wxString::Format( + _( "The CADSTAR template '%s' has different settings for thermal relief " + "in pads and vias. KiCad only supports one single setting for both. The " + "setting for pads has been applied." ), + csTemplate.Name ) ); + + if( csTemplate.Pouring.ThermalReliefOnPads ) + { + zone->SetThermalReliefGap( getKiCadLength( csTemplate.Pouring.ClearanceWidth ) ); + zone->SetThermalReliefCopperBridge( getKiCadLength( + getCopperCode( csTemplate.Pouring.ReliefCopperCodeID ).CopperWidth ) ); + zone->SetPadConnection( ZONE_CONNECTION::THERMAL ); + } + else + zone->SetPadConnection( ZONE_CONNECTION::FULL ); + } + + //Now create power plane layers: + for( LAYER_ID layer : mPowerPlaneLayers ) + { + wxASSERT( Assignments.Layerdefs.Layers.find( layer ) != Assignments.Layerdefs.Layers.end() ); + + //The net name will equal the layer name + wxString powerPlaneLayerName = Assignments.Layerdefs.Layers.at( layer ).Name; + NET_ID netid = wxEmptyString; + + for( std::pair netPair : Layout.Nets ) + { + NET net = netPair.second; + + if( net.Name == powerPlaneLayerName ) + { + netid = net.ID; + break; + } + } + + if( netid.IsEmpty() ) + { + wxLogError( wxString::Format( + _( "The CADSTAR layer '%s' is defined as a power plane layer. However no " + "net with such name exists. The layer has been loaded but no copper zone " + "was created." ), + powerPlaneLayerName ) ); + } + else + { + for( std::pair boardPair : Layout.Boards ) + { + //create a zone in each board shape + BOARD& board = boardPair.second; + int defaultLineThicknesss = + mBoard->GetDesignSettings().GetLineThickness( PCB_LAYER_ID::Edge_Cuts ); + ZONE_CONTAINER* zone = + getZoneFromCadstarShape( board.Shape, defaultLineThicknesss ); + + mBoard->Add( zone, ADD_MODE::APPEND ); + + zone->SetZoneName( powerPlaneLayerName ); + zone->SetLayer( getKiCadLayer( layer ) ); + zone->SetFillMode( ZONE_FILL_MODE::POLYGONS ); + zone->SetPadConnection( ZONE_CONNECTION::FULL ); + zone->SetMinIslandArea( -1 ); + zone->SetNet( getKiCadNet( netid ) ); + } + } + + + + } + +} + + +void CADSTAR_PCB_ARCHIVE_LOADER::loadCoppers() +{ + for( std::pair copPair : Layout.Coppers ) + { + COPPER& csCopper = copPair.second; + + if( !csCopper.PouredTemplateID.IsEmpty() ) + continue; //ignore copper related to a template as we've already loaded it! + + // For now we are going to load coppers to a KiCad zone however this isn't perfect + //TODO: Load onto a graphical polygon with a net (when KiCad has this feature) + + if( !mDoneCopperWarning ) + { + wxLogWarning( + _( "The CADSTAR design contains COPPER elements, which have no direct KiCad " + "equivalent. These have been imported as a KiCad Zone if solid or hatch " + "filled, or as a KiCad Track if the shape was an unfilled outline (open or " + "closed)." ) ); + mDoneCopperWarning = true; + } + + + if( csCopper.Shape.Type == SHAPE_TYPE::OPENSHAPE + || csCopper.Shape.Type == SHAPE_TYPE::OUTLINE ) + { + std::vector outlineSegments = + getDrawSegmentsFromVertices( csCopper.Shape.Vertices ); + + std::vector outlineTracks = makeTracksFromDrawsegments( outlineSegments, mBoard, + getKiCadNet(csCopper.NetRef.NetID) , getKiCadLayer( csCopper.LayerID ), + getKiCadLength( getCopperCode( csCopper.CopperCodeID ).CopperWidth ) ); + + //cleanup + for( DRAWSEGMENT* ds : outlineSegments ) + { + if( ds ) + delete ds; + } + + for( CUTOUT cutout : csCopper.Shape.Cutouts ) + { + std::vector cutoutSeg = + getDrawSegmentsFromVertices( cutout.Vertices ); + + std::vector cutoutTracks = makeTracksFromDrawsegments( cutoutSeg, mBoard, + getKiCadNet( csCopper.NetRef.NetID ), getKiCadLayer( csCopper.LayerID ), + getKiCadLength( getCopperCode( csCopper.CopperCodeID ).CopperWidth ) ); + + //cleanup + for( DRAWSEGMENT* ds : cutoutSeg ) + { + if( ds ) + delete ds; + } + } + } + else + { + ZONE_CONTAINER* zone = getZoneFromCadstarShape( csCopper.Shape, + getKiCadLength( getCopperCode( csCopper.CopperCodeID ).CopperWidth ) ); + + mBoard->Add( zone, ADD_MODE::APPEND ); + + zone->SetZoneName( csCopper.ID ); + zone->SetLayer( getKiCadLayer( csCopper.LayerID ) ); + + if( csCopper.Shape.Type == SHAPE_TYPE::HATCHED ) + { + zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN ); + zone->SetHatchGap( getKiCadHatchCodeGap( csCopper.Shape.HatchCodeID ) ); + zone->SetHatchThickness( getKiCadHatchCodeThickness( csCopper.Shape.HatchCodeID ) ); + zone->SetHatchOrientation( getHatchCodeAngleDegrees( csCopper.Shape.HatchCodeID ) ); + } + else + { + zone->SetFillMode( ZONE_FILL_MODE::POLYGONS ); + } + + zone->SetPadConnection( ZONE_CONNECTION::FULL ); + zone->SetNet( getKiCadNet( csCopper.NetRef.NetID ) ); + } + } +} + +void CADSTAR_PCB_ARCHIVE_LOADER::loadNets() +{ + for( std::pair netPair : Layout.Nets ) + { + NET net = netPair.second; + wxString netname = net.Name; + + if( netname.IsEmpty() ) + netname = "$" + net.SignalNum; + + for( NET::CONNECTION connection : net.Connections ) + { + if( !connection.Unrouted ) + loadNetTracks( net.ID, connection.Route ); + + //TODO: all other elements + } + + for( std::pair viaPair : net.Vias ) + { + NET::VIA via = viaPair.second; + loadNetVia( net.ID, via ); + } + + for( std::pair pinPair : net.Pins ) + { + NET::PIN pin = pinPair.second; + MODULE* m = getModuleFromCadstarID( pin.ComponentID ); + + if( m == nullptr ) + { + wxLogWarning( wxString::Format( + _( "The net '%s' references component ID '%s' which does not exist. " + "This has been ignored," ), + netname, pin.ComponentID ) ); + } + else if( ( pin.PadID - (long) 1 ) > m->Pads().size() ) + { + wxLogWarning( wxString::Format( + _( "The net '%s' references non-existent pad index '%d' in component '%s'. " + "This has been ignored." ), + netname, pin.PadID, m->GetReference() ) ); + } + else + { + // The below works because we have added the pads in the correct order to the module and + // it so happens that PAD_ID in Cadstar is a sequential, numerical ID + m->Pads().at( pin.PadID - (long) 1 )->SetNet( getKiCadNet( net.ID ) ); + } + } + } +} + + +void CADSTAR_PCB_ARCHIVE_LOADER::loadComponentAttributes( + const COMPONENT& aComponent, MODULE* aModule ) { for( std::pair attrPair : aComponent.AttributeValues ) { @@ -715,9 +1020,88 @@ void CADSTAR_PCB_ARCHIVE_LOADER::loadComponentAttributes( const COMPONENT& aComp } -void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarShape( const SHAPE& aCadstarShape, const PCB_LAYER_ID& aKiCadLayer, - const LINECODE_ID& aCadstarLinecodeID, const wxString& aShapeName, - BOARD_ITEM_CONTAINER* aContainer ) +void CADSTAR_PCB_ARCHIVE_LOADER::loadNetTracks( + const NET_ID& aCadstarNetID, const NET::ROUTE& aCadstarRoute ) +{ + std::vector dsVector; + + POINT prevEnd = aCadstarRoute.StartPoint; + + for( const NET::ROUTE_VERTEX& v : aCadstarRoute.RouteVertices ) + { + DRAWSEGMENT* ds = getDrawSegmentFromVertex( prevEnd, v.Vertex ); + ds->SetLayer( getKiCadLayer( aCadstarRoute.LayerID ) ); + ds->SetWidth( getKiCadLength( v.RouteWidth ) ); + dsVector.push_back( ds ); + prevEnd = v.Vertex.End; + } + + //Todo add real netcode to the tracks + std::vector tracks = + makeTracksFromDrawsegments( dsVector, mBoard, getKiCadNet( aCadstarNetID ) ); + + //cleanup + for( DRAWSEGMENT* ds : dsVector ) + { + if( ds ) + delete ds; + } +} + + +void CADSTAR_PCB_ARCHIVE_LOADER::loadNetVia( + const NET_ID& aCadstarNetID, const NET::VIA& aCadstarVia ) +{ + VIA* via = new VIA( mBoard ); + mBoard->Add( via, ADD_MODE::APPEND ); + + VIACODE csViaCode = getViaCode( aCadstarVia.ViaCodeID ); + LAYERPAIR csLayerPair = getLayerPair( aCadstarVia.LayerPairID ); + + via->SetPosition( getKiCadPoint( aCadstarVia.Location ) ); + via->SetDrill( getKiCadLength( csViaCode.DrillDiameter ) ); + via->SetLocked( aCadstarVia.Fixed ); + + if( csViaCode.Shape.ShapeType != PAD_SHAPE_TYPE::CIRCLE ) + wxLogError( wxString::Format( + _( "The CADSTAR via code '%s' has different shape from a circle defined. " + "KiCad only supports circular vias so this via type has been changed to " + "be a via with circular shape of %.2f mm diameter." ), + csViaCode.Name, + (double) ( (double) getKiCadLength( csViaCode.Shape.Size ) / 1E6 ) ) ); + + via->SetWidth( getKiCadLength( csViaCode.Shape.Size ) ); + + bool start_layer_outside = + csLayerPair.PhysicalLayerStart == 1 + || csLayerPair.PhysicalLayerStart == Assignments.Technology.MaxPhysicalLayer; + bool end_layer_outside = + csLayerPair.PhysicalLayerEnd == 1 + || csLayerPair.PhysicalLayerEnd == Assignments.Technology.MaxPhysicalLayer; + + if( start_layer_outside && end_layer_outside ) + { + via->SetViaType( VIATYPE::THROUGH ); + } + else if( ( !start_layer_outside ) && ( !end_layer_outside ) ) + { + via->SetViaType( VIATYPE::BLIND_BURIED ); + } + else + { + via->SetViaType( VIATYPE::MICROVIA ); + } + + via->SetLayerPair( getKiCadCopperLayerID( csLayerPair.PhysicalLayerStart ), + getKiCadCopperLayerID( csLayerPair.PhysicalLayerEnd ) ); + via->SetNet( getKiCadNet( aCadstarNetID ) ); + ///todo add netcode to the via +} + + +void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarShape( const SHAPE& aCadstarShape, + const PCB_LAYER_ID& aKiCadLayer, const LINECODE_ID& aCadstarLinecodeID, + const wxString& aShapeName, BOARD_ITEM_CONTAINER* aContainer ) { int lineThickness = getLineThickness( aCadstarLinecodeID ); @@ -772,9 +1156,9 @@ void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarCutoutsAsSegments( const std::vector } -void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarVerticesAsSegments( const std::vector& aCadstarVertices, - const PCB_LAYER_ID& aKiCadLayer, const int& aLineThickness, - BOARD_ITEM_CONTAINER* aContainer ) +void CADSTAR_PCB_ARCHIVE_LOADER::drawCadstarVerticesAsSegments( + const std::vector& aCadstarVertices, const PCB_LAYER_ID& aKiCadLayer, + const int& aLineThickness, BOARD_ITEM_CONTAINER* aContainer ) { std::vector drawSegments = getDrawSegmentsFromVertices( aCadstarVertices, aContainer ); @@ -799,76 +1183,12 @@ std::vector CADSTAR_PCB_ARCHIVE_LOADER::getDrawSegmentsFromVertice return drawSegments; const VERTEX* prev = &aCadstarVertices.at( 0 ); // first one should always be a point vertex - double arcStartAngle, arcEndAngle, arcAngle; - bool cw = false; + const VERTEX* cur; for( size_t i = 1; i < aCadstarVertices.size(); i++ ) { - const VERTEX* cur = &aCadstarVertices[i]; - DRAWSEGMENT* ds; - cw = false; - - wxPoint startPoint = getKiCadPoint( prev->End ); - wxPoint endPoint = getKiCadPoint( cur->End ); - wxPoint centerPoint; - - if( cur->Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE - || cur->Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE ) - centerPoint = ( startPoint + endPoint ) / 2; - else - centerPoint = getKiCadPoint( cur->Center ); - - switch( cur->Type ) - { - - case VERTEX_TYPE::POINT: - - if( isModule( aContainer ) ) - ds = new EDGE_MODULE( (MODULE*) aContainer, STROKE_T::S_SEGMENT ); - else - { - ds = new DRAWSEGMENT( aContainer ); - ds->SetShape( STROKE_T::S_SEGMENT ); - } - - ds->SetStart( startPoint ); - ds->SetEnd( endPoint ); - break; - - case VERTEX_TYPE::CLOCKWISE_SEMICIRCLE: - case VERTEX_TYPE::CLOCKWISE_ARC: - cw = true; - case VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE: - case VERTEX_TYPE::ANTICLOCKWISE_ARC: - - if( isModule( aContainer ) ) - ds = new EDGE_MODULE( (MODULE*) aContainer, STROKE_T::S_ARC ); - else - { - ds = new DRAWSEGMENT( aContainer ); - ds->SetShape( STROKE_T::S_ARC ); - } - - ds->SetArcStart( startPoint ); - ds->SetCenter( centerPoint ); - - arcStartAngle = getPolarAngle( startPoint - centerPoint ); - arcEndAngle = getPolarAngle( endPoint - centerPoint ); - arcAngle = arcEndAngle - arcStartAngle; - //TODO: detect if we are supposed to draw a circle instead (i.e. two SEMICIRCLEs - // with opposite start/end points and same centre point) - - if( cw ) - ds->SetAngle( NormalizeAnglePos( arcAngle ) ); - else - ds->SetAngle( NormalizeAngleNeg( arcAngle ) ); - break; - } - - if( isModule( aContainer ) && ds != nullptr ) - ( (EDGE_MODULE*) ds )->SetLocalCoord(); - - drawSegments.push_back( ds ); + cur = &aCadstarVertices.at( i ); + drawSegments.push_back( getDrawSegmentFromVertex( prev->End, *cur, aContainer ) ); prev = cur; } @@ -876,6 +1196,77 @@ std::vector CADSTAR_PCB_ARCHIVE_LOADER::getDrawSegmentsFromVertice } +DRAWSEGMENT* CADSTAR_PCB_ARCHIVE_LOADER::getDrawSegmentFromVertex( const POINT& aCadstarStartPoint, + const VERTEX& aCadstarVertex, BOARD_ITEM_CONTAINER* aContainer ) +{ + DRAWSEGMENT* ds; + bool cw = false; + double arcStartAngle, arcEndAngle, arcAngle; + + wxPoint startPoint = getKiCadPoint( aCadstarStartPoint ); + wxPoint endPoint = getKiCadPoint( aCadstarVertex.End ); + wxPoint centerPoint; + + if( aCadstarVertex.Type == VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE + || aCadstarVertex.Type == VERTEX_TYPE::CLOCKWISE_SEMICIRCLE ) + centerPoint = ( startPoint + endPoint ) / 2; + else + centerPoint = getKiCadPoint( aCadstarVertex.Center ); + + switch( aCadstarVertex.Type ) + { + + case VERTEX_TYPE::POINT: + + if( isModule( aContainer ) ) + ds = new EDGE_MODULE( (MODULE*) aContainer, STROKE_T::S_SEGMENT ); + else + { + ds = new DRAWSEGMENT( aContainer ); + ds->SetShape( STROKE_T::S_SEGMENT ); + } + + ds->SetStart( startPoint ); + ds->SetEnd( endPoint ); + break; + + case VERTEX_TYPE::CLOCKWISE_SEMICIRCLE: + case VERTEX_TYPE::CLOCKWISE_ARC: + cw = true; + case VERTEX_TYPE::ANTICLOCKWISE_SEMICIRCLE: + case VERTEX_TYPE::ANTICLOCKWISE_ARC: + + if( isModule( aContainer ) ) + ds = new EDGE_MODULE( (MODULE*) aContainer, STROKE_T::S_ARC ); + else + { + ds = new DRAWSEGMENT( aContainer ); + ds->SetShape( STROKE_T::S_ARC ); + } + + ds->SetArcStart( startPoint ); + ds->SetCenter( centerPoint ); + + arcStartAngle = getPolarAngle( startPoint - centerPoint ); + arcEndAngle = getPolarAngle( endPoint - centerPoint ); + arcAngle = arcEndAngle - arcStartAngle; + //TODO: detect if we are supposed to draw a circle instead (i.e. two SEMICIRCLEs + // with opposite start/end points and same centre point) + + if( cw ) + ds->SetAngle( NormalizeAnglePos( arcAngle ) ); + else + ds->SetAngle( NormalizeAngleNeg( arcAngle ) ); + break; + } + + if( isModule( aContainer ) && ds != nullptr ) + ( (EDGE_MODULE*) ds )->SetLocalCoord(); + + return ds; +} + + ZONE_CONTAINER* CADSTAR_PCB_ARCHIVE_LOADER::getZoneFromCadstarShape( const SHAPE& aCadstarShape, const int& aLineThickness ) { @@ -992,6 +1383,73 @@ SHAPE_LINE_CHAIN CADSTAR_PCB_ARCHIVE_LOADER::getLineChainFromDrawsegments( } +std::vector CADSTAR_PCB_ARCHIVE_LOADER::makeTracksFromDrawsegments( + const std::vector aDrawsegments, BOARD_ITEM_CONTAINER* aParentContainer, + NETINFO_ITEM* aNet, const PCB_LAYER_ID& aLayerOverride, int aWidthOverride ) +{ + std::vector tracks; + + for( DRAWSEGMENT* ds : aDrawsegments ) + { + TRACK* track; + + switch( ds->GetShape() ) + { + case STROKE_T::S_ARC: + if( ds->GetClass() == wxT( "MGRAPHIC" ) ) + { + EDGE_MODULE* em = (EDGE_MODULE*) ds; + SHAPE_ARC arc( em->GetStart0(), em->GetEnd0(), (double) em->GetAngle() / 10.0 ); + track = new ARC( aParentContainer, &arc ); + } + else + { + SHAPE_ARC arc( ds->GetCenter(), ds->GetArcStart(), (double) ds->GetAngle() / 10.0 ); + track = new ARC( aParentContainer, &arc ); + } + break; + case STROKE_T::S_SEGMENT: + if( ds->GetClass() == wxT( "MGRAPHIC" ) ) + { + EDGE_MODULE* em = (EDGE_MODULE*) ds; + track = new TRACK( aParentContainer ); + track->SetStart( em->GetStart0() ); + track->SetEnd( em->GetEnd0() ); + } + else + { + track = new TRACK( aParentContainer ); + track->SetStart( ds->GetStart() ); + track->SetEnd( ds->GetEnd() ); + } + break; + + default: + wxASSERT_MSG( true, "Drawsegment type is unexpected. Ignored." ); + continue; + } + + if( aWidthOverride == -1 ) + track->SetWidth( ds->GetWidth() ); + else + track->SetWidth( aWidthOverride ); + + if( aLayerOverride == PCB_LAYER_ID::UNDEFINED_LAYER ) + track->SetLayer( ds->GetLayer() ); + else + track->SetLayer( aLayerOverride ); + + if( aNet != nullptr ) + track->SetNet( aNet ); + + tracks.push_back( track ); + aParentContainer->Add( track, ADD_MODE::APPEND ); + } + + return tracks; +} + + void CADSTAR_PCB_ARCHIVE_LOADER::addAttribute( const ATTRIBUTE_LOCATION& aCadstarAttrLoc, const ATTRIBUTE_ID& aCadstarAttributeID, MODULE* aModule, const wxString& aAttributeValue ) { @@ -1044,7 +1502,7 @@ void CADSTAR_PCB_ARCHIVE_LOADER::addAttribute( const ATTRIBUTE_LOCATION& aCadsta txt->SetPos0( rotatedTextPos ); txt->SetLayer( getKiCadLayer( aCadstarAttrLoc.LayerID ) ); txt->SetMirrored( aCadstarAttrLoc.Mirror ); - txt->SetTextAngle( getKiCadAngle( aCadstarAttrLoc.OrientAngle ) - aModule->GetOrientation() ); + txt->SetTextAngle( getAngleTenthDegree( aCadstarAttrLoc.OrientAngle ) - aModule->GetOrientation() ); TEXTCODE tc = getTextCode( aCadstarAttrLoc.TextCodeID ); @@ -1109,7 +1567,6 @@ void CADSTAR_PCB_ARCHIVE_LOADER::addAttribute( const ATTRIBUTE_LOCATION& aCadsta int CADSTAR_PCB_ARCHIVE_LOADER::getLineThickness( const LINECODE_ID& aCadstarLineCodeID ) { - wxCHECK( Assignments.Codedefs.LineCodes.find( aCadstarLineCodeID ) != Assignments.Codedefs.LineCodes.end(), mBoard->GetDesignSettings().GetLineThickness( PCB_LAYER_ID::Edge_Cuts ) ); @@ -1118,7 +1575,19 @@ int CADSTAR_PCB_ARCHIVE_LOADER::getLineThickness( const LINECODE_ID& aCadstarLin } -CADSTAR_PCB_ARCHIVE_LOADER::TEXTCODE CADSTAR_PCB_ARCHIVE_LOADER::getTextCode( const TEXTCODE_ID& aCadstarTextCodeID ) +CADSTAR_PCB_ARCHIVE_LOADER::COPPERCODE CADSTAR_PCB_ARCHIVE_LOADER::getCopperCode( + const COPPERCODE_ID& aCadstaCopperCodeID ) +{ + wxCHECK( Assignments.Codedefs.CopperCodes.find( aCadstaCopperCodeID ) + != Assignments.Codedefs.CopperCodes.end(), + COPPERCODE() ); + + return Assignments.Codedefs.CopperCodes.at( aCadstaCopperCodeID ); +} + + +CADSTAR_PCB_ARCHIVE_LOADER::TEXTCODE CADSTAR_PCB_ARCHIVE_LOADER::getTextCode( + const TEXTCODE_ID& aCadstarTextCodeID ) { wxCHECK( Assignments.Codedefs.TextCodes.find( aCadstarTextCodeID ) != Assignments.Codedefs.TextCodes.end(), @@ -1128,7 +1597,8 @@ CADSTAR_PCB_ARCHIVE_LOADER::TEXTCODE CADSTAR_PCB_ARCHIVE_LOADER::getTextCode( co } -CADSTAR_PCB_ARCHIVE_LOADER::PADCODE CADSTAR_PCB_ARCHIVE_LOADER::getPadCode( const PADCODE_ID& aCadstarPadCodeID ) +CADSTAR_PCB_ARCHIVE_LOADER::PADCODE CADSTAR_PCB_ARCHIVE_LOADER::getPadCode( + const PADCODE_ID& aCadstarPadCodeID ) { wxCHECK( Assignments.Codedefs.PadCodes.find( aCadstarPadCodeID ) != Assignments.Codedefs.PadCodes.end(), @@ -1138,6 +1608,28 @@ CADSTAR_PCB_ARCHIVE_LOADER::PADCODE CADSTAR_PCB_ARCHIVE_LOADER::getPadCode( cons } +CADSTAR_PCB_ARCHIVE_LOADER::VIACODE CADSTAR_PCB_ARCHIVE_LOADER::getViaCode( + const VIACODE_ID& aCadstarViaCodeID ) +{ + wxCHECK( Assignments.Codedefs.ViaCodes.find( aCadstarViaCodeID ) + != Assignments.Codedefs.ViaCodes.end(), + VIACODE() ); + + return Assignments.Codedefs.ViaCodes.at( aCadstarViaCodeID ); +} + + +CADSTAR_PCB_ARCHIVE_LOADER::LAYERPAIR CADSTAR_PCB_ARCHIVE_LOADER::getLayerPair( + const LAYERPAIR_ID& aCadstarLayerPairID ) +{ + wxCHECK( Assignments.Codedefs.LayerPairs.find( aCadstarLayerPairID ) + != Assignments.Codedefs.LayerPairs.end(), + LAYERPAIR() ); + + return Assignments.Codedefs.LayerPairs.at( aCadstarLayerPairID ); +} + + wxString CADSTAR_PCB_ARCHIVE_LOADER::getAttributeName( const ATTRIBUTE_ID& aCadstarAttributeID ) { wxCHECK( Assignments.Codedefs.AttributeNames.find( aCadstarAttributeID ) @@ -1149,7 +1641,7 @@ wxString CADSTAR_PCB_ARCHIVE_LOADER::getAttributeName( const ATTRIBUTE_ID& aCads wxString CADSTAR_PCB_ARCHIVE_LOADER::getAttributeValue( const ATTRIBUTE_ID& aCadstarAttributeID, - const std::map& aCadstarAttributeMap ) + const std::map& aCadstarAttributeMap ) { wxCHECK( aCadstarAttributeMap.find( aCadstarAttributeID ) != aCadstarAttributeMap.end(), wxEmptyString ); @@ -1158,7 +1650,8 @@ wxString CADSTAR_PCB_ARCHIVE_LOADER::getAttributeValue( const ATTRIBUTE_ID& aCad } -CADSTAR_PCB_ARCHIVE_LOADER::PART CADSTAR_PCB_ARCHIVE_LOADER::getPart( const PART_ID& aCadstarPartID ) +CADSTAR_PCB_ARCHIVE_LOADER::PART CADSTAR_PCB_ARCHIVE_LOADER::getPart( + const PART_ID& aCadstarPartID ) { wxCHECK( Parts.PartDefinitions.find( aCadstarPartID ) != Parts.PartDefinitions.end(), PART() ); @@ -1166,6 +1659,127 @@ CADSTAR_PCB_ARCHIVE_LOADER::PART CADSTAR_PCB_ARCHIVE_LOADER::getPart( const PART } +CADSTAR_PCB_ARCHIVE_LOADER::HATCHCODE CADSTAR_PCB_ARCHIVE_LOADER::getHatchCode( + const HATCHCODE_ID& aCadstarHatchcodeID ) +{ + wxCHECK( Assignments.Codedefs.HatchCodes.find( aCadstarHatchcodeID ) + != Assignments.Codedefs.HatchCodes.end(), + HATCHCODE() ); + + return Assignments.Codedefs.HatchCodes.at( aCadstarHatchcodeID ); +} + + +double CADSTAR_PCB_ARCHIVE_LOADER::getHatchCodeAngleDegrees( const HATCHCODE_ID& aCadstarHatchcodeID ) +{ + checkAndLogHatchCode( aCadstarHatchcodeID ); + HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID ); + + if( hcode.Hatches.size() < 1 ) + return mBoard->GetDesignSettings().GetDefaultZoneSettings().m_HatchOrientation; + else + return getAngleDegrees( hcode.Hatches.at( 0 ).OrientAngle ); +} + + +int CADSTAR_PCB_ARCHIVE_LOADER::getKiCadHatchCodeThickness( + const HATCHCODE_ID& aCadstarHatchcodeID ) +{ + checkAndLogHatchCode( aCadstarHatchcodeID ); + HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID ); + + if( hcode.Hatches.size() < 1 ) + return mBoard->GetDesignSettings().GetDefaultZoneSettings().m_HatchThickness; + else + return getKiCadLength( hcode.Hatches.at( 0 ).LineWidth ); +} + + +int CADSTAR_PCB_ARCHIVE_LOADER::getKiCadHatchCodeGap( const HATCHCODE_ID& aCadstarHatchcodeID ) +{ + checkAndLogHatchCode( aCadstarHatchcodeID ); + HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID ); + + if( hcode.Hatches.size() < 1 ) + return mBoard->GetDesignSettings().GetDefaultZoneSettings().m_HatchGap; + else + return getKiCadLength( hcode.Hatches.at( 0 ).Step ); +} + + +void CADSTAR_PCB_ARCHIVE_LOADER::checkAndLogHatchCode( const HATCHCODE_ID& aCadstarHatchcodeID ) +{ + if( mHatchcodesTested.find( aCadstarHatchcodeID ) != mHatchcodesTested.end() ) + { + return; //already checked + } + else + { + HATCHCODE hcode = getHatchCode( aCadstarHatchcodeID ); + + if( hcode.Hatches.size() != 2 ) + { + wxLogWarning( wxString::Format( + _( "The CADSTAR Hatching code '%s' has %d hatches defined. " + "KiCad only supports 2 hatches (crosshatching) 90 degrees apart. " + "The imported hatching is crosshatched." ), + hcode.Name, (int) hcode.Hatches.size() ) ); + } + else + { + if( hcode.Hatches.at( 0 ).LineWidth != hcode.Hatches.at( 1 ).LineWidth ) + { + wxLogWarning( wxString::Format( + _( "The CADSTAR Hatching code '%s' has different line widths for each " + "hatch. KiCad only supports one width for the haching. The imported " + "hatching uses the width defined in the first hatch definition, i.e. " + "%.2f mm." ), + hcode.Name, + (double) ((double) getKiCadLength( hcode.Hatches.at( 0 ).LineWidth ) ) / 1E6 ) ); + } + + if( hcode.Hatches.at( 0 ).Step != hcode.Hatches.at( 1 ).Step ) + { + wxLogWarning( wxString::Format( + _( "The CADSTAR Hatching code '%s' has different step sizes for each " + "hatch. KiCad only supports one step size for the haching. The imported " + "hatching uses the step size defined in the first hatching definition, " + "i.e. %.2f mm." ), + hcode.Name, + (double) ( (double) getKiCadLength( hcode.Hatches.at( 0 ).Step ) ) + / 1E6 ) ); + } + + if( abs( hcode.Hatches.at( 0 ).OrientAngle - hcode.Hatches.at( 1 ).OrientAngle ) + != 90000 ) + { + wxLogWarning( wxString::Format( + _( "The hatches in CADSTAR Hatching code '%s' have an angle " + "difference of %.1f degrees. KiCad only supports hatching 90 " + "degrees apart. The imported hatching has two hatches 90 " + "degrees apart, oriented %.1f degrees from horizontal." ), + hcode.Name, + getAngleDegrees( abs( hcode.Hatches.at( 0 ).OrientAngle + - hcode.Hatches.at( 1 ).OrientAngle ) ), + getAngleDegrees( hcode.Hatches.at( 0 ).OrientAngle ) ) ); + } + } + + mHatchcodesTested.insert( aCadstarHatchcodeID ); + } +} + + +MODULE* CADSTAR_PCB_ARCHIVE_LOADER::getModuleFromCadstarID( + const COMPONENT_ID& aCadstarComponentID ) +{ + if( mComponentMap.find( aCadstarComponentID ) == mComponentMap.end() ) + return nullptr; + else + return mComponentMap.at( aCadstarComponentID ); +} + + wxPoint CADSTAR_PCB_ARCHIVE_LOADER::getKiCadPoint( wxPoint aCadstarPoint ) { wxPoint retval; @@ -1184,8 +1798,36 @@ double CADSTAR_PCB_ARCHIVE_LOADER::getPolarAngle( wxPoint aPoint ) } +NETINFO_ITEM* CADSTAR_PCB_ARCHIVE_LOADER::getKiCadNet( const NET_ID& aCadstarNetID ) +{ + if( aCadstarNetID.IsEmpty() ) + return nullptr; + else if( mNetMap.find( aCadstarNetID ) != mNetMap.end() ) + { + return mNetMap.at(aCadstarNetID); + } + else + { + wxCHECK( Layout.Nets.find( aCadstarNetID ) != Layout.Nets.end(), nullptr ); + + NET csNet = Layout.Nets.at( aCadstarNetID ); + + NETINFO_ITEM* netInfo = new NETINFO_ITEM( mBoard, csNet.Name, ++mNumNets ); + mBoard->Add( netInfo, ADD_MODE::APPEND ); + //todo also add the Netclass + + return netInfo; + } + + return nullptr; +} + + PCB_LAYER_ID CADSTAR_PCB_ARCHIVE_LOADER::getKiCadCopperLayerID( unsigned int aLayerNum ) { + if(aLayerNum == Assignments.Technology.MaxPhysicalLayer) + return PCB_LAYER_ID::B_Cu; + switch( aLayerNum ) { // clang-format off @@ -1229,6 +1871,10 @@ PCB_LAYER_ID CADSTAR_PCB_ARCHIVE_LOADER::getKiCadCopperLayerID( unsigned int aLa bool CADSTAR_PCB_ARCHIVE_LOADER::isLayerSet( const LAYER_ID& aCadstarLayerID ) { + wxCHECK( Assignments.Layerdefs.Layers.find( aCadstarLayerID ) + != Assignments.Layerdefs.Layers.end(), + false ); + LAYER& layer = Assignments.Layerdefs.Layers.at( aCadstarLayerID ); switch( layer.Type ) @@ -1248,10 +1894,17 @@ bool CADSTAR_PCB_ARCHIVE_LOADER::isLayerSet( const LAYER_ID& aCadstarLayerID ) PCB_LAYER_ID CADSTAR_PCB_ARCHIVE_LOADER::getKiCadLayer( const LAYER_ID& aCadstarLayerID ) { - if( mLayermap.find( aCadstarLayerID ) == mLayermap.end() ) - return PCB_LAYER_ID::UNDEFINED_LAYER; //Possibly should be an ASSERT? - else - return mLayermap[aCadstarLayerID]; + if( Assignments.Layerdefs.Layers.find( aCadstarLayerID ) != Assignments.Layerdefs.Layers.end() ) + { + if( Assignments.Layerdefs.Layers.at( aCadstarLayerID ).Type == LAYER_TYPE::NOLAYER ) + //The "no layer" is common for CADSTAR documentation symbols + //map it to undefined layer for later processing + return PCB_LAYER_ID::UNDEFINED_LAYER; + } + + wxCHECK( mLayermap.find( aCadstarLayerID ) != mLayermap.end(), PCB_LAYER_ID::UNDEFINED_LAYER ); + + return mLayermap.at(aCadstarLayerID); } diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.h b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.h index e247b52e31..c2aaa20c6a 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.h +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_loader.h @@ -28,19 +28,33 @@ #include #include +#include class BOARD; class CADSTAR_PCB_ARCHIVE_LOADER : public CADSTAR_PCB_ARCHIVE_PARSER { public: - explicit CADSTAR_PCB_ARCHIVE_LOADER( wxString aFilename ) : CADSTAR_PCB_ARCHIVE_PARSER( aFilename ) + explicit CADSTAR_PCB_ARCHIVE_LOADER( wxString aFilename ) + : CADSTAR_PCB_ARCHIVE_PARSER( aFilename ) { - mBoard = nullptr; - mDesignCenter.x = 0; - mDesignCenter.y = 0; + mBoard = nullptr; + mDesignCenter.x = 0; + mDesignCenter.y = 0; + mDoneCopperWarning = false; + mNumNets = 0; } + ~CADSTAR_PCB_ARCHIVE_LOADER() + { + for( std::pair libItem : mLibraryMap ) + { + MODULE* mod = libItem.second; + + if( mod ) + delete mod; + } + } /** * @brief Loads a CADSTAR PCB Archive file into the KiCad BOARD object given @@ -54,12 +68,27 @@ private: ///< Populated by loadBoardStackup(). std::map mLibraryMap; ///< Map between Cadstar and KiCad ///< components in the library. Populated - ///< by loadComponentLibrary(). + ///< by loadComponentLibrary(). Owns the + ///< MODULE objects. + std::map mComponentMap; ///< Map between Cadstar and KiCad + ///< components on the board. Does NOT own + ///< the MODULE objects (these should have + ///< been loaded to mBoard). + std::map mNetMap; ///< Map between Cadstar and KiCad Nets std::map mCopperLayers; ///< Map of CADSTAR Physical layers to ///< CADSTAR Layer IDs + std::vector mPowerPlaneLayers; ///< List of layers that are marked as + ///< power plane in CADSTAR. This is used + ///< by "loadtemplates" wxPoint mDesignCenter; ///< Used for calculating the required ///< offset to apply to the Cadstar design ///< so that it fits in KiCad canvas + std::set mHatchcodesTested; ///< Used by checkAndLogHatchCode() to + ///< avoid multiple duplicate warnings + bool mDoneCopperWarning; ///< Used by loadCoppers() to avoid + ///< multiple duplicate warnings + int mNumNets; ///< Number of nets loaded so far + // Functions for loading individual elements: void loadBoardStackup(); @@ -68,6 +97,9 @@ private: void loadFigures(); void loadAreas(); void loadComponents(); + void loadTemplates(); + void loadCoppers(); + void loadNets(); // Helper functions for loading: void logBoardStackupWarning( @@ -75,6 +107,8 @@ private: void loadLibraryFigures( const SYMDEF& aComponent, MODULE* aModule ); void loadLibraryPads( const SYMDEF& aComponent, MODULE* aModule ); void loadComponentAttributes( const COMPONENT& aComponent, MODULE* aModule ); + void loadNetTracks( const NET_ID& aCadstarNetID, const NET::ROUTE& aCadstarRoute ); + void loadNetVia( const NET_ID& aCadstarNetID, const NET::VIA& aCadstarVia ); /** * @brief @@ -114,7 +148,7 @@ private: /** * @brief Returns a vector of pointers to DRAWSEGMENT objects. Caller owns the objects. - * @param aCadstarVertices * + * @param aCadstarVertices * @param aContainer to draw on (e.g. mBoard). Can be nullptr. * @return */ @@ -122,6 +156,15 @@ private: const std::vector& aCadstarVertices, BOARD_ITEM_CONTAINER* aContainer = nullptr ); + /** + * @brief Returns a pointer to a DRAWSEGMENT object. Caller owns the object. + * @param aCadstarStartPoint + * @param aCadstarVertex + * @param aContainer to draw on (e.g. mBoard). Can be nullptr. + * @return + */ + DRAWSEGMENT* getDrawSegmentFromVertex( const POINT& aCadstarStartPoint, + const VERTEX& aCadstarVertex, BOARD_ITEM_CONTAINER* aContainer = nullptr ); /** * @brief @@ -150,6 +193,22 @@ private: */ SHAPE_LINE_CHAIN getLineChainFromDrawsegments( const std::vector aDrawSegments ); + /** + * @brief Returns a vector of pointers to TRACK/ARC objects. Caller owns the objects + * @param aDrawsegments + * @param aParentContainer sets this as the parent of each TRACK object and Add()s it to the parent + * @param aNet sets all the tracks to this net, unless nullptr + * @param aLayerOverride Sets all tracks to this layer, or, if it is UNDEFINED_LAYER, uses the layers + * in the DrawSegments + * @param aWidthOverride Sets all tracks to this width, or, if it is UNDEFINED_LAYER, uses the width + * in the DrawSegments + * @return + */ + std::vector makeTracksFromDrawsegments( const std::vector aDrawsegments, + BOARD_ITEM_CONTAINER* aParentContainer, NETINFO_ITEM* aNet = nullptr, + const PCB_LAYER_ID& aLayerOverride = PCB_LAYER_ID::UNDEFINED_LAYER, + int aWidthOverride = -1 ); + /** * @brief Adds a CADSTAR Attribute to a KiCad module * @param aCadstarAttrLoc @@ -170,11 +229,11 @@ private: int getLineThickness( const LINECODE_ID& aCadstarLineCodeID ); - TEXTCODE getTextCode( const TEXTCODE_ID& aCadstarTextCodeID ); - - - PADCODE getPadCode( const PADCODE_ID& aCadstarPadCodeID ); - + COPPERCODE getCopperCode( const COPPERCODE_ID& aCadstaCopperCodeID ); + TEXTCODE getTextCode( const TEXTCODE_ID& aCadstarTextCodeID ); + PADCODE getPadCode( const PADCODE_ID& aCadstarPadCodeID ); + VIACODE getViaCode( const VIACODE_ID& aCadstarViaCodeID ); + LAYERPAIR getLayerPair( const LAYERPAIR_ID& aCadstarLayerPairID ); wxString getAttributeName( const ATTRIBUTE_ID& aCadstarAttributeID ); @@ -184,6 +243,13 @@ private: PART getPart( const PART_ID& aCadstarPartID ); + HATCHCODE getHatchCode( const HATCHCODE_ID& aCadstarHatchcodeID ); + void checkAndLogHatchCode( const HATCHCODE_ID& aCadstarHatchcodeID ); + MODULE* getModuleFromCadstarID( const COMPONENT_ID& aCadstarComponentID ); + double getHatchCodeAngleDegrees( const HATCHCODE_ID& aCadstarHatchcodeID ); + int getKiCadHatchCodeThickness( const HATCHCODE_ID& aCadstarHatchcodeID ); + int getKiCadHatchCodeGap( const HATCHCODE_ID& aCadstarHatchcodeID ); + /** * @brief Scales, offsets and inverts y axis to make the point usable directly in KiCad * @param aCadstarPoint @@ -206,11 +272,21 @@ private: * @param aCadstarAngle * @return */ - double getKiCadAngle( const long long& aCadstarAngle ) + double getAngleTenthDegree( const long long& aCadstarAngle ) { return (double) aCadstarAngle / 100.0; } + /** + * @brief + * @param aCadstarAngle + * @return + */ + double getAngleDegrees( const long long& aCadstarAngle ) + { + return (double) aCadstarAngle / 1000.0; + } + /** * @brief * @param aPoint @@ -218,6 +294,14 @@ private: */ double getPolarAngle( wxPoint aPoint ); + /** + * @brief Searches mNetMap and returns the NETINFO_ITEM pointer if exists. Otherwise + * creates a new one and adds it to mBoard. + * @param aCadstarNetID + * @return + */ + NETINFO_ITEM* getKiCadNet( const NET_ID& aCadstarNetID ); + /** * @brief diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_parser.cpp b/pcbnew/plugins/cadstar/cadstar_pcb_archive_parser.cpp index 9ca37f0457..9a10330a3b 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_parser.cpp +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_parser.cpp @@ -268,9 +268,9 @@ void CADSTAR_PCB_ARCHIVE_PARSER::RULESET::Parse( XNODE* aNode ) AreaViaCodeID = GetXmlAttributeIDString( cNode, 0 ); else if( nodeName == wxT( "SPACINGCODE" ) ) { - SPACINGCODE scode; - scode.Parse( cNode ); - SpacingCodes.push_back( scode ); + SPACINGCODE spacingcode; + spacingcode.Parse( cNode ); + SpacingCodes.insert( std::make_pair( spacingcode.ID, spacingcode ) ); } else THROW_UNKNOWN_NODE_IO_ERROR( nodeName, aNode->GetName() ); @@ -322,7 +322,7 @@ void CADSTAR_PCB_ARCHIVE_PARSER::CODEDEFS::Parse( XNODE* aNode ) { SPACINGCODE spacingcode; spacingcode.Parse( cNode ); - SpacingCodes.push_back( spacingcode ); + SpacingCodes.insert( std::make_pair( spacingcode.ID, spacingcode ) ); } else if( nodeName == wxT( "RULESET" ) ) { @@ -883,7 +883,7 @@ void CADSTAR_PCB_ARCHIVE_PARSER::SPACINGCODE::Parse( XNODE* aNode ) { wxASSERT( aNode->GetName() == wxT( "SPACINGCODE" ) ); - Code = GetXmlAttributeIDString( aNode, 0 ); + ID = GetXmlAttributeIDString( aNode, 0 ); Spacing = GetXmlAttributeIDLong( aNode, 1 ); XNODE* cNode = aNode->GetChildren(); @@ -1390,8 +1390,7 @@ void CADSTAR_PCB_ARCHIVE_PARSER::TECHNOLOGY_SECTION::Parse( XNODE* aNode ) ViaGrid = GetXmlAttributeIDLong( cNode, 0 ); else if( cNodeName == wxT( "DESIGNORIGIN" ) ) { - std::vector pts = ParseAllChildPoints( cNode, true, 1 ); - DesignOrigin = pts[0]; + DesignOrigin.Parse( cNode->GetChildren() ); } else if( cNodeName == wxT( "DESIGNAREA" ) ) { @@ -1400,13 +1399,11 @@ void CADSTAR_PCB_ARCHIVE_PARSER::TECHNOLOGY_SECTION::Parse( XNODE* aNode ) } else if( cNodeName == wxT( "DESIGNREF" ) ) { - std::vector pts = ParseAllChildPoints( cNode, true, 1 ); - DesignRef = pts[0]; + DesignOrigin.Parse( cNode->GetChildren() ); } else if( cNodeName == wxT( "DESIGNLIMIT" ) ) { - std::vector pts = ParseAllChildPoints( cNode, true, 1 ); - DesignLimit = pts[0]; + DesignLimit.Parse( cNode->GetChildren() ); } else if( cNodeName == wxT( "BACKOFFJCTS" ) ) BackOffJunctions = true; diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_parser.h b/pcbnew/plugins/cadstar/cadstar_pcb_archive_parser.h index 9e9ec20f99..2a3d151820 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_parser.h +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_parser.h @@ -26,9 +26,8 @@ #ifndef CADSTAR_PCB_ARCHIVE_PARSER_H_ #define CADSTAR_PCB_ARCHIVE_PARSER_H_ -#include -#include #include +#include #include @@ -39,10 +38,6 @@ #define UNDEFINED_MATERIAL_ID ( MATERIAL_ID ) wxEmptyString #define UNDEFINED_PHYSICAL_LAYER ( PHYSICAL_LAYER_ID ) - 1 -/** - * Default spacing class for all nets - */ -#define NO_SPACE_CLASS_ID ( SPACING_CLASS_ID ) wxT( "NO_SPACE_CLASS" ) /** * Component Name Attribute ID - typically used for placement of designators on silk screen. @@ -86,6 +81,7 @@ public: typedef wxString COPPERCODE_ID; typedef wxString PADCODE_ID; typedef wxString VIACODE_ID; + typedef wxString SPACINGCODE_ID; typedef wxString LAYERPAIR_ID; typedef wxString ATTRIBUTE_ID; typedef wxString NETCLASS_ID; @@ -394,7 +390,45 @@ public: void Parse( XNODE* aNode ); }; - wxString Code; //TODO convert to an enum class containing all valid spacings + /** + * @brief Possible spacing rules: + * - A_A = Component Placement to Component Placement + * - C_B = Copper to Board + * - C_C = Copper to Copper + * - H_H = Hole to Hole + * - OT_P = Optimal Route to Pad (Optional Rule) + * - OT_T = Optimal Route to Route (Optional Rule) + * - OT_V = Optimal Route to Via (Optional Rule) + * - P_B = Pad to Board + * - P_C = Pad to Copper + * - P_P = Pad to Pad + * - P_S = Pad to SMD pad (Optional Rule) + * - P_V = Pad to Via + * - T_B = Route to Board outline + * - T_C = Route to Copper + * - T_P = Route to Pad + * - T_T = Route to Route + * - T_S = Route to SMD Pad (Optional Rule) + * - T_V = Route to Via + * - S_B = SMD Pad to Board (Optional Rule) + * - S_C = SMD Pad to Copper (Optional Rule) + * - S_S = SMD Pad to SMD Pad (Optional Rule) + * - L_B = Test Land to Board + * - L_O = Test Land to Component + * - L_L = Test Land to Test Land + * - V_B = Via to Board + * - V_C = Via to Copper + * - V_S = Via to SMD Pad (Optional Rule) + * - V_V = Via to Via + * + * Other design rules are in: + * TECHNOLOGY->MAXMITER = Maximum Mitre (This parameter is not actually checked in Cadstar) + * TECHNOLOGY->MINMITER = Minimum Mitre (This parameter is not actually checked in Cadstar) + * TECHNOLOGY->MINUNNECKED = Minimum Thicker Track Length + * TECHNOLOGY->MINNECKED = Minimum Thinner Track Length + * TECHNOLOGY->MINROUTEWIDTH = Thin Route Width + */ + SPACINGCODE_ID ID; long Spacing; std::vector Reassigns; ///< Can have different spacings on differnt layers @@ -720,23 +754,24 @@ public: ///< will be used when inserting new vias within an area that ///< has been assigned this rule set. ("VIACODEREF") - std::vector SpacingCodes; ///< Overrides these spacing rules in the specific - ///< area. + std::map + SpacingCodes; ///< Overrides these spacing rules in the specific + ///< area. void Parse( XNODE* aNode ); }; struct CODEDEFS { - std::map LineCodes; - std::map HatchCodes; - std::map TextCodes; - std::map RouteCodes; - std::map CopperCodes; - std::vector SpacingCodes; ///< Spacing Design Rules - std::map Rulesets; ///< Used for area design rules - std::map PadCodes; - std::map ViaCodes; + std::map LineCodes; + std::map HatchCodes; + std::map TextCodes; + std::map RouteCodes; + std::map CopperCodes; + std::map SpacingCodes; ///< Spacing Design Rules + std::map Rulesets; ///< Used for area design rules + std::map PadCodes; + std::map ViaCodes; std::map LayerPairs; ///< Default vias to use between pairs of layers @@ -790,10 +825,10 @@ public: long TrackGrid; ///< Grid for Routes (equal X and Y steps) long ViaGrid; ///< Grid for Vias (equal X and Y steps) - POINT DesignOrigin; + LONGPOINT DesignOrigin; std::pair DesignArea; - POINT DesignRef; ///< Appears to be 0,0 always - POINT DesignLimit; + LONGPOINT DesignRef; ///< Appears to be 0,0 always + LONGPOINT DesignLimit; bool BackOffJunctions = false; bool BackOffWidthChange = false; @@ -1064,9 +1099,9 @@ public: { // The default alignment for TEXT_LOCATION (when "NO_ALIGNMENT" is selected) is // Bottom left, matching CADSTAR's default behaviour - Alignment= ALIGNMENT::BOTTOMLEFT; + Alignment = ALIGNMENT::BOTTOMLEFT; } - ATTRIBUTE_ID AttributeID; + ATTRIBUTE_ID AttributeID; void Parse( XNODE* aNode ); }; @@ -1850,13 +1885,20 @@ public: ///< aperture etc ...) used on a plotting machine" ///< (param1) - long ClearanceWidth; ///< (param2) - long SliverWidth; ///< (param3) - long AdditionalIsolation; ///< (param4) - long ThermalReliefPadsAngle; ///< Disabled when !ThermalReliefOnPads (param5) + long ClearanceWidth; ///< Specifies the space around pads when pouring + ///< (i.e. Thermal relief clearance) + long SliverWidth; ///< Minimum width of copper that may be created + long AdditionalIsolation; ///< This is the gap to apply in routes and pads + ///< in addition to the existing pad-to-copper or + ///< route-to-copper spacing (see SPACINGCODE.ID) + long ThermalReliefPadsAngle; ///< Orientation for the thermal reliefs. Disabled when !ThermalReliefOnPads (param5) long ThermalReliefViasAngle; ///< Disabled when !ThermalReliefOnVias (param6) - long MinIsolatedCopper = UNDEFINED_VALUE; ///< Disabled when UNDEFINED_VALUE (param7) - long MinDisjointCopper = UNDEFINED_VALUE; ///< Disabled when UNDEFINED_VALUE (param8) + long MinIsolatedCopper = UNDEFINED_VALUE; ///< The value is the length of one side of + ///< a notional square. Disabled when + ///< UNDEFINED_VALUE + long MinDisjointCopper = UNDEFINED_VALUE; ///< The value is the length of one side of + ///< a notional square. Disabled when + ///< UNDEFINED_VALUE bool ThermalReliefOnPads = true; ///< false when subnode "NOPINRELIEF" is present bool ThermalReliefOnVias = true; ///< false when subnode "NOVIARELIEF" is present diff --git a/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.cpp b/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.cpp index dc246046b8..30292e3b9c 100644 --- a/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.cpp +++ b/pcbnew/plugins/cadstar/cadstar_pcb_archive_plugin.cpp @@ -27,6 +27,7 @@ #include #include #include +#include CADSTAR_PCB_ARCHIVE_PLUGIN::CADSTAR_PCB_ARCHIVE_PLUGIN() @@ -62,5 +63,26 @@ BOARD* CADSTAR_PCB_ARCHIVE_PLUGIN::Load( CADSTAR_PCB_ARCHIVE_LOADER tempPCB( aFileName ); tempPCB.Load( m_board ); + //center the board: + if( aProperties ) + { + UTF8 page_width; + UTF8 page_height; + + if( aProperties->Value( "page_width", &page_width ) + && aProperties->Value( "page_height", &page_height ) ) + { + EDA_RECT bbbox = m_board->GetBoardEdgesBoundingBox(); + + int w = atoi( page_width.c_str() ); + int h = atoi( page_height.c_str() ); + + int desired_x = ( w - bbbox.GetWidth() ) / 2; + int desired_y = ( h - bbbox.GetHeight() ) / 2; + + m_board->Move( wxPoint( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() ) ); + } + } + return m_board; }