Eeschema: swap label rotations (and include fields)

This means that labels keep the position relative
to what they are connected to after the swap.

An attempt is made to map the fields of a label
into the position of a matching field (i.e. same name)
on the swapped-to label.

Move the SCH_FIELD rotation justification
handling to the SCH_FIELD class, so it's not just
SCH_LABEL that handles it.

https://gitlab.com/kicad/code/kicad/-/issues/18303
This commit is contained in:
John Beard 2024-06-29 20:53:10 +08:00
parent 6969b9d64f
commit bb100994a7
4 changed files with 144 additions and 40 deletions

View File

@ -1017,11 +1017,38 @@ bool SCH_FIELD::Replace( const EDA_SEARCH_DATA& aSearchData, void* aAuxData )
void SCH_FIELD::Rotate( const VECTOR2I& aCenter, bool aRotateCCW ) void SCH_FIELD::Rotate( const VECTOR2I& aCenter, bool aRotateCCW )
{ {
if( GetTextAngle().IsVertical() && GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
{
if( aRotateCCW )
SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
SetTextAngle( ANGLE_HORIZONTAL );
}
else if( GetTextAngle().IsVertical() && GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
{
if( aRotateCCW )
SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
SetTextAngle( ANGLE_HORIZONTAL );
}
else if( GetTextAngle().IsHorizontal() && GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
{
if( !aRotateCCW )
SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
SetTextAngle( ANGLE_VERTICAL );
}
else if( GetTextAngle().IsHorizontal() && GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
{
if( !aRotateCCW )
SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
SetTextAngle( ANGLE_VERTICAL );
}
VECTOR2I pt = GetPosition(); VECTOR2I pt = GetPosition();
RotatePoint( pt, aCenter, aRotateCCW ? ANGLE_90 : ANGLE_270 ); RotatePoint( pt, aCenter, aRotateCCW ? ANGLE_90 : ANGLE_270 );
SetPosition( pt ); SetPosition( pt );
SetTextAngle( GetTextAngle() != ANGLE_HORIZONTAL ? ANGLE_HORIZONTAL : ANGLE_VERTICAL );
} }

View File

@ -203,6 +203,12 @@ SPIN_STYLE SPIN_STYLE::MirrorY()
} }
unsigned SPIN_STYLE::CCWRotationsTo( const SPIN_STYLE& aOther ) const
{
return ( ( (int) m_spin - (int) aOther.m_spin ) % 4 + 4 ) % 4;
}
SCH_LABEL_BASE::SCH_LABEL_BASE( const VECTOR2I& aPos, const wxString& aText, KICAD_T aType ) : SCH_LABEL_BASE::SCH_LABEL_BASE( const VECTOR2I& aPos, const wxString& aText, KICAD_T aType ) :
SCH_TEXT( aPos, aText, LAYER_NOTES, aType ), SCH_TEXT( aPos, aText, LAYER_NOTES, aType ),
m_shape( L_UNSPECIFIED ), m_shape( L_UNSPECIFIED ),
@ -456,42 +462,7 @@ void SCH_LABEL_BASE::Rotate90( bool aClockwise )
{ {
for( SCH_FIELD& field : m_fields ) for( SCH_FIELD& field : m_fields )
{ {
if( field.GetTextAngle().IsVertical() field.Rotate( GetPosition(), !aClockwise );
&& field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
{
if( !aClockwise )
field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
field.SetTextAngle( ANGLE_HORIZONTAL );
}
else if( field.GetTextAngle().IsVertical()
&& field.GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
{
if( !aClockwise )
field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
field.SetTextAngle( ANGLE_HORIZONTAL );
}
else if( field.GetTextAngle().IsHorizontal()
&& field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
{
if( aClockwise )
field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
field.SetTextAngle( ANGLE_VERTICAL );
}
else if( field.GetTextAngle().IsHorizontal()
&& field.GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
{
if( aClockwise )
field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
field.SetTextAngle( ANGLE_VERTICAL );
}
VECTOR2I pos = field.GetTextPos();
RotatePoint( pos, GetPosition(), aClockwise ? -ANGLE_90 : ANGLE_90 );
field.SetTextPos( pos );
} }
} }
} }

View File

@ -81,6 +81,11 @@ public:
*/ */
SPIN_STYLE MirrorY(); SPIN_STYLE MirrorY();
/**
* Get CCW rotation needed to get to the given spin style.
*/
unsigned CCWRotationsTo( const SPIN_STYLE& aOther ) const;
private: private:
SPIN m_spin; SPIN m_spin;
}; };

View File

@ -1149,6 +1149,90 @@ const std::vector<KICAD_T> swappableItems = {
}; };
/**
* Swap the positions of the fields in the two lists, aAFields and aBFields,
* relative to their parent positions.
*
* If a field is in both lists, it will be swapped to the position of the
* matching field on the counterpart.
*
* If a field is in only one list, it will simply be rotated by aFallbackRotation
* (CW or CCW depending on which list it is in)
*/
static void swapFieldPositionsWithMatching( std::vector<SCH_FIELD>& aAFields,
std::vector<SCH_FIELD>& aBFields,
unsigned aFallbackRotationsCCW )
{
std::set<wxString> handledKeys;
const auto swapFieldTextProps = []( SCH_FIELD& aField, SCH_FIELD& bField )
{
const VECTOR2I aRelPos = aField.GetPosition() - aField.GetParentPosition();
const GR_TEXT_H_ALIGN_T aTextJustifyH = aField.GetHorizJustify();
const GR_TEXT_V_ALIGN_T aTextJustifyV = aField.GetVertJustify();
const EDA_ANGLE aTextAngle = aField.GetTextAngle();
const VECTOR2I bRelPos = bField.GetPosition() - bField.GetParentPosition();
const GR_TEXT_H_ALIGN_T bTextJustifyH = bField.GetHorizJustify();
const GR_TEXT_V_ALIGN_T bTextJustifyV = bField.GetVertJustify();
const EDA_ANGLE bTextAngle = bField.GetTextAngle();
aField.SetPosition( aField.GetParentPosition() + bRelPos );
aField.SetHorizJustify( bTextJustifyH );
aField.SetVertJustify( bTextJustifyV );
aField.SetTextAngle( bTextAngle );
bField.SetPosition( bField.GetParentPosition() + aRelPos );
bField.SetHorizJustify( aTextJustifyH );
bField.SetVertJustify( aTextJustifyV );
bField.SetTextAngle( aTextAngle );
};
for( SCH_FIELD& aField : aAFields )
{
const wxString name = aField.GetCanonicalName();
auto it = std::find_if( aBFields.begin(), aBFields.end(),
[name]( const SCH_FIELD& bField )
{
return bField.GetCanonicalName() == name;
} );
if( it != aBFields.end() )
{
// We have a field with the same key in both labels
SCH_FIELD& bField = *it;
swapFieldTextProps( aField, bField );
}
else
{
// We only have this field in A, so just rotate it
for( unsigned ii = 0; ii < aFallbackRotationsCCW; ii++ )
{
aField.Rotate( aField.GetParentPosition(), true );
}
}
// And keep track that we did this one
handledKeys.insert( name );
}
// Any fields in B that weren't in A weren't handled and need to be rotated
// in reverse
for( SCH_FIELD& bField : aBFields )
{
const wxString bName = bField.GetCanonicalName();
if( handledKeys.find( bName ) == handledKeys.end() )
{
for( unsigned ii = 0; ii < aFallbackRotationsCCW; ii++ )
{
bField.Rotate( bField.GetParentPosition(), false );
}
}
}
}
int SCH_EDIT_TOOL::Swap( const TOOL_EVENT& aEvent ) int SCH_EDIT_TOOL::Swap( const TOOL_EVENT& aEvent )
{ {
EE_SELECTION& selection = m_selectionTool->RequestSelection( swappableItems ); EE_SELECTION& selection = m_selectionTool->RequestSelection( swappableItems );
@ -1217,9 +1301,26 @@ int SCH_EDIT_TOOL::Swap( const TOOL_EVENT& aEvent )
case SCH_GLOBAL_LABEL_T: case SCH_GLOBAL_LABEL_T:
case SCH_HIER_LABEL_T: case SCH_HIER_LABEL_T:
case SCH_DIRECTIVE_LABEL_T: case SCH_DIRECTIVE_LABEL_T:
m_frame->AutoRotateItem( screen, a ); {
m_frame->AutoRotateItem( screen, b ); SCH_LABEL_BASE& aLabelBase = static_cast<SCH_LABEL_BASE&>( *a );
SCH_LABEL_BASE& bLabelBase = static_cast<SCH_LABEL_BASE&>( *b );
const SPIN_STYLE aSpinStyle = aLabelBase.GetSpinStyle();
const SPIN_STYLE bSpinStyle = bLabelBase.GetSpinStyle();
// First, swap the label orientations
aLabelBase.SetSpinStyle( bSpinStyle );
bLabelBase.SetSpinStyle( aSpinStyle );
// And swap the fields as best we can
std::vector<SCH_FIELD>& aFields = aLabelBase.GetFields();
std::vector<SCH_FIELD>& bFields = bLabelBase.GetFields();
const unsigned rotationsAtoB = aSpinStyle.CCWRotationsTo( bSpinStyle );
swapFieldPositionsWithMatching( aFields, bFields, rotationsAtoB );
break; break;
}
case SCH_SYMBOL_T: case SCH_SYMBOL_T:
{ {
SCH_SYMBOL* aSymbol = static_cast<SCH_SYMBOL*>( a ); SCH_SYMBOL* aSymbol = static_cast<SCH_SYMBOL*>( a );