/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2023 Alex Shvartzkop * Copyright The KiCad Developers, see AUTHORS.txt for contributors. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include namespace KIGFX { class VIEW; class RENDER_SETTINGS; } enum LENGTH_TUNING_MODE { SINGLE, DIFF_PAIR, DIFF_PAIR_SKEW }; class TUNING_STATUS_VIEW_ITEM : public EDA_ITEM { public: TUNING_STATUS_VIEW_ITEM( PCB_BASE_EDIT_FRAME* aFrame ); wxString GetClass() const override; #if defined(DEBUG) void Show( int nestLevel, std::ostream& os ) const override; #endif VECTOR2I GetPosition() const override; void SetPosition( const VECTOR2I& aPos ) override; void SetMinMax( const double aMin, const double aMax ); void ClearMinMax(); void SetCurrent( const double aCurrent, const wxString& aLabel ); void SetIsTimeDomain( const bool aIsTimeDomain ); const BOX2I ViewBBox() const override; std::vector ViewGetLayers() const override; void ViewDraw( int aLayer, KIGFX::VIEW* aView ) const override; protected: EDA_DRAW_FRAME* m_frame; VECTOR2I m_pos; double m_min; double m_max; double m_current; wxString m_currentLabel; wxString m_currentText; wxString m_minText; wxString m_maxText; bool m_isTimeDomain; }; class PCB_TUNING_PATTERN : public PCB_GENERATOR { public: static const wxString GENERATOR_TYPE; static const wxString DISPLAY_NAME; PCB_TUNING_PATTERN( BOARD_ITEM* aParent = nullptr, PCB_LAYER_ID aLayer = F_Cu, LENGTH_TUNING_MODE aMode = LENGTH_TUNING_MODE::SINGLE ); wxString GetGeneratorType() const override { return wxS( "tuning_pattern" ); } wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const override { return _( "Tuning Pattern" ); } wxString GetFriendlyName() const override { return _( "Tuning Pattern" ); } wxString GetPluralName() const override { return _( "Tuning Patterns" ); } wxString GetCommitMessage() const override { return _( "Edit Tuning Pattern" ); } BITMAPS GetMenuImage() const override { switch( m_tuningMode ) { case SINGLE: return BITMAPS::ps_tune_length; break; case DIFF_PAIR: return BITMAPS::ps_diff_pair_tune_length; break; case DIFF_PAIR_SKEW: return BITMAPS::ps_diff_pair_tune_phase; break; } return BITMAPS::unknown; } static PCB_TUNING_PATTERN* CreateNew( GENERATOR_TOOL* aTool, PCB_BASE_EDIT_FRAME* aFrame, BOARD_CONNECTED_ITEM* aStartItem, LENGTH_TUNING_MODE aMode ); void EditStart( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit ) override; bool Update( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit ) override; void EditFinish( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit ) override; void EditCancel( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit ) override; void Remove( GENERATOR_TOOL* aTool, BOARD* aBoard, BOARD_COMMIT* aCommit ) override; bool MakeEditPoints( EDIT_POINTS& points ) const override; bool UpdateFromEditPoints( EDIT_POINTS& aEditPoints ) override; bool UpdateEditPoints( EDIT_POINTS& aEditPoints ) override; void Move( const VECTOR2I& aMoveVector ) override { m_origin += aMoveVector; m_end += aMoveVector; if( !this->HasFlag( IN_EDIT ) ) { PCB_GROUP::Move( aMoveVector ); if( m_baseLine ) m_baseLine->Move( aMoveVector ); if( m_baseLineCoupled ) m_baseLineCoupled->Move( aMoveVector ); } } void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override { if( !this->HasFlag( IN_EDIT ) ) { PCB_GENERATOR::Rotate( aRotCentre, aAngle ); RotatePoint( m_end, aRotCentre, aAngle ); if( m_baseLine ) m_baseLine->Rotate( aAngle, aRotCentre ); if( m_baseLineCoupled ) m_baseLineCoupled->Rotate( aAngle, aRotCentre ); } } void Flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override { if( !this->HasFlag( IN_EDIT ) ) { PCB_GENERATOR::Flip( aCentre, aFlipDirection ); baseMirror( aCentre, aFlipDirection ); } } void Mirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) override { if( !this->HasFlag( IN_EDIT ) ) { PCB_GENERATOR::Mirror( aCentre, aFlipDirection ); baseMirror( aCentre, aFlipDirection ); } } void SetLayer( PCB_LAYER_ID aLayer ) override { PCB_GENERATOR::SetLayer( aLayer ); for( BOARD_ITEM* item : GetBoardItems() ) item->SetLayer( aLayer ); } PCB_LAYER_ID GetLayer() const override { return PCB_GENERATOR::GetLayer(); } const BOX2I GetBoundingBox() const override { return getOutline().BBox(); } std::vector ViewGetLayers() const override { return { LAYER_ANCHOR, GetLayer() }; } bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override { return getOutline().Collide( aPosition, aAccuracy ); } bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const override { BOX2I sel = aRect; if ( aAccuracy ) sel.Inflate( aAccuracy ); if( aContained ) return sel.Contains( GetBoundingBox() ); return sel.Intersects( GetBoundingBox() ); } bool HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const override { return KIGEOM::ShapeHitTest( aPoly, getOutline(), aContained ); } const BOX2I ViewBBox() const override { return GetBoundingBox(); } EDA_ITEM* Clone() const override { return new PCB_TUNING_PATTERN( *this ); } void ViewDraw( int aLayer, KIGFX::VIEW* aView ) const override final; const VECTOR2I& GetEnd() const { return m_end; } void SetEnd( const VECTOR2I& aValue ) { m_end = aValue; } int GetEndX() const { return m_end.x; } void SetEndX( int aValue ) { m_end.x = aValue; } int GetEndY() const { return m_end.y; } void SetEndY( int aValue ) { m_end.y = aValue; } int GetWidth() const { for( BOARD_ITEM* item : GetBoardItems() ) if( PCB_TRACK* track = dynamic_cast( item ) ) return track->GetWidth(); return m_trackWidth; } void SetWidth( int aValue ) { m_trackWidth = aValue; for( BOARD_ITEM* item : GetBoardItems() ) if( PCB_TRACK* track = dynamic_cast( item ) ) track->SetWidth( aValue ); } int GetNetCode() const { for( BOARD_ITEM* item : GetBoardItems() ) if( BOARD_CONNECTED_ITEM* bci = dynamic_cast( item ) ) return bci->GetNetCode(); return 0; } void SetNetCode( int aNetCode ) { if( BOARD* board = GetBoard() ) { if( NETINFO_ITEM* net = board->FindNet( aNetCode ) ) m_lastNetName = net->GetNetname(); else m_lastNetName.clear(); } for( BOARD_ITEM* item : GetBoardItems() ) if( BOARD_CONNECTED_ITEM* bci = dynamic_cast( item ) ) bci->SetNetCode( aNetCode ); } bool HasSolderMask() const { for( BOARD_ITEM* item : GetBoardItems() ) if( PCB_TRACK* track = dynamic_cast( item ) ) return track->HasSolderMask(); return true; } void SetHasSolderMask( bool aVal ) { for( BOARD_ITEM* item : GetBoardItems() ) if( PCB_TRACK* track = dynamic_cast( item ) ) track->SetHasSolderMask( aVal ); } std::optional GetLocalSolderMaskMargin() const { for( BOARD_ITEM* item : GetBoardItems() ) if( PCB_TRACK* track = dynamic_cast( item ) ) return track->GetLocalSolderMaskMargin(); return std::optional(); } void SetLocalSolderMaskMargin( std::optional aMargin ) { for( BOARD_ITEM* item : GetBoardItems() ) if( PCB_TRACK* track = dynamic_cast( item ) ) track->SetLocalSolderMaskMargin( aMargin ); } LENGTH_TUNING_MODE GetTuningMode() const { return m_tuningMode; } PNS::ROUTER_MODE GetPNSMode() { switch( m_tuningMode ) { case LENGTH_TUNING_MODE::SINGLE: return PNS::PNS_MODE_TUNE_SINGLE; case LENGTH_TUNING_MODE::DIFF_PAIR: return PNS::PNS_MODE_TUNE_DIFF_PAIR; case LENGTH_TUNING_MODE::DIFF_PAIR_SKEW: return PNS::PNS_MODE_TUNE_DIFF_PAIR_SKEW; default: return PNS::PNS_MODE_TUNE_SINGLE; } } PNS::MEANDER_SETTINGS& GetSettings() { return m_settings; } int GetMinAmplitude() const { return m_settings.m_minAmplitude; } void SetMinAmplitude( int aValue ) { aValue = std::max( aValue, 0 ); m_settings.m_minAmplitude = aValue; if( m_settings.m_maxAmplitude < m_settings.m_minAmplitude ) m_settings.m_maxAmplitude = m_settings.m_minAmplitude; } int GetMaxAmplitude() const { return m_settings.m_maxAmplitude; } void SetMaxAmplitude( int aValue ) { aValue = std::max( aValue, 0 ); m_settings.m_maxAmplitude = aValue; if( m_settings.m_maxAmplitude < m_settings.m_minAmplitude ) m_settings.m_minAmplitude = m_settings.m_maxAmplitude; } // Update the initial side one time at EditStart based on m_end. void UpdateSideFromEnd() { m_updateSideFromEnd = true; } PNS::MEANDER_SIDE GetInitialSide() const { return m_settings.m_initialSide; } void SetInitialSide( PNS::MEANDER_SIDE aValue ) { m_settings.m_initialSide = aValue; } int GetSpacing() const { return m_settings.m_spacing; } void SetSpacing( int aValue ) { m_settings.m_spacing = aValue; } std::optional GetTargetLength() const { if( m_settings.m_targetLength.Opt() == PNS::MEANDER_SETTINGS::LENGTH_UNCONSTRAINED ) return std::optional(); else return m_settings.m_targetLength.Opt(); } void SetTargetLength( std::optional aValue ) { m_settings.m_isTimeDomain = false; if( aValue.has_value() ) m_settings.SetTargetLength( aValue.value() ); else m_settings.SetTargetLength( PNS::MEANDER_SETTINGS::LENGTH_UNCONSTRAINED ); } std::optional GetTargetDelay() const { if( m_settings.m_targetLengthDelay.Opt() == PNS::MEANDER_SETTINGS::DELAY_UNCONSTRAINED ) return std::optional(); else return m_settings.m_targetLengthDelay.Opt(); } void SetTargetDelay( std::optional aValue ) { m_settings.m_isTimeDomain = true; if( aValue.has_value() ) m_settings.SetTargetLengthDelay( aValue.value() ); else m_settings.SetTargetLengthDelay( PNS::MEANDER_SETTINGS::DELAY_UNCONSTRAINED ); } int GetTargetSkew() const { return m_settings.m_targetSkew.Opt(); } void SetTargetSkew( int aValue ) { m_settings.SetTargetSkew( aValue ); } int GetTargetSkewDelay() const { return m_settings.m_targetSkewDelay.Opt(); } void SetTargetSkewDelay( int aValue ) { m_settings.SetTargetSkewDelay( aValue ); } bool GetOverrideCustomRules() const { return m_settings.m_overrideCustomRules; } void SetOverrideCustomRules( bool aOverride ) { m_settings.m_overrideCustomRules = aOverride; } int GetCornerRadiusPercentage() const { return m_settings.m_cornerRadiusPercentage; } void SetCornerRadiusPercentage( int aValue ) { m_settings.m_cornerRadiusPercentage = aValue; } bool IsSingleSided() const { return m_settings.m_singleSided; } void SetSingleSided( bool aValue ) { m_settings.m_singleSided = aValue; } bool IsRounded() const { return m_settings.m_cornerStyle == PNS::MEANDER_STYLE_ROUND; } void SetRounded( bool aFlag ) { m_settings.m_cornerStyle = aFlag ? PNS::MEANDER_STYLE_ROUND : PNS::MEANDER_STYLE_CHAMFER; } std::vector> GetRowData() override { std::vector> data = PCB_GENERATOR::GetRowData(); data.emplace_back( _HKI( "Net" ), m_lastNetName ); data.emplace_back( _HKI( "Tuning" ), m_tuningInfo ); return data; } const STRING_ANY_MAP GetProperties() const override; void SetProperties( const STRING_ANY_MAP& aProps ) override; void ShowPropertiesDialog( PCB_BASE_EDIT_FRAME* aEditFrame ) override; std::vector GetPreviewItems( GENERATOR_TOOL* aTool, PCB_BASE_EDIT_FRAME* aFrame, bool aStatusItemsOnly = false ) override; void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList ) override; protected: void swapData( BOARD_ITEM* aImage ) override { wxASSERT( aImage->Type() == PCB_GENERATOR_T ); std::swap( *this, *static_cast( aImage ) ); } bool recoverBaseline( PNS::ROUTER* aRouter ); bool baselineValid(); bool initBaseLine( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard, VECTOR2I& aStart, VECTOR2I& aEnd, NETINFO_ITEM* aNet, std::optional& aBaseLine ); bool initBaseLines( PNS::ROUTER* aRouter, int aPNSLayer, BOARD* aBoard ); bool removeToBaseline( PNS::ROUTER* aRouter, int aPNSLayer, SHAPE_LINE_CHAIN& aBaseLine ); bool resetToBaseline( GENERATOR_TOOL* aTool, int aPNSLayer, SHAPE_LINE_CHAIN& aBaseLine, bool aPrimary ); SHAPE_LINE_CHAIN getOutline() const; void baseMirror( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection ) { PCB_GENERATOR::baseMirror( aCentre, aFlipDirection ); if( m_baseLine ) { m_baseLine->Mirror( aCentre, aFlipDirection ); m_origin = m_baseLine->CPoint( 0 ); m_end = m_baseLine->CLastPoint(); } if( m_baseLineCoupled ) m_baseLineCoupled->Mirror( aCentre, aFlipDirection ); if( m_settings.m_initialSide == PNS::MEANDER_SIDE_RIGHT ) m_settings.m_initialSide = PNS::MEANDER_SIDE_LEFT; else m_settings.m_initialSide = PNS::MEANDER_SIDE_RIGHT; } protected: VECTOR2I m_end; PNS::MEANDER_SETTINGS m_settings; std::optional m_baseLine; std::optional m_baseLineCoupled; int m_trackWidth; int m_diffPairGap; LENGTH_TUNING_MODE m_tuningMode; wxString m_lastNetName; wxString m_tuningInfo; PNS::MEANDER_PLACER_BASE::TUNING_STATUS m_tuningStatus; bool m_updateSideFromEnd; };