← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Eeschema: make field autoplace grid flexible with field height

 

#6377 changed a bit in the vicinity of this patch, so here's an updated one to preempt any merge conflicts.

On Sun, Dec 13, 2015 at 12:53:53PM -0500, Chris Pavlina wrote:
> Okay, here's a patch to make the autoplacement grid for fields flexible, 
> so fields are positioned sanely when they have different heights. Just 
> finished this an hour ago, as opposed to the main autoplacement patch 
> which got months of testing, so somebody else might want to try this 
> out.
> 
> I'm going to prepare one more patch, to handle a slightly quirky field 
> placement in a specific arrangement with a long component symbol 
> sandwiched between wires. That should be it.
> 
> --
> Chris

> commit b8fdf66034c0028ae33a3d372937279919427916
> Author: Chris Pavlina <cpavlin1@xxxxxxxxxxxxxx>
> Date:   Sat Dec 12 16:29:19 2015 -0500
> 
>     Make vertical autoplacement grid flexible
> 
> diff --git a/eeschema/autoplace_fields.cpp b/eeschema/autoplace_fields.cpp
> index 001127a..5849309 100644
> --- a/eeschema/autoplace_fields.cpp
> +++ b/eeschema/autoplace_fields.cpp
> @@ -63,9 +63,11 @@
>  #include <vector>
>  #include <algorithm>
>  
> -#define FIELD_V_SPACING 100
> +#define FIELD_PADDING 10            // arbitrarily chosen for aesthetics
> +#define FIELD_PADDING_ALIGNED 18    // aligns 50 mil text to a 100 mil grid
> +#define WIRE_V_SPACING 100
>  #define HPADDING 25
> -#define VPADDING 50
> +#define VPADDING 25
>  
>  /**
>   * Function round_n
> @@ -127,7 +129,7 @@ public:
>          Kiface().KifaceSettings()->Read( AUTOPLACE_ALIGN_KEY, &m_align_to_grid, false );
>  
>          m_comp_bbox = m_component->GetBodyBoundingBox();
> -        m_fbox_size = ComputeFBoxSize();
> +        m_fbox_size = ComputeFBoxSize( /* aDynamic */ true );
>  
>          m_power_symbol = ! m_component->IsInNetlist();
>  
> @@ -143,17 +145,16 @@ public:
>       */
>      void DoAutoplace( bool aManual )
>      {
> +        bool force_wire_spacing = false;
>          SIDE field_side = choose_side_for_fields( aManual );
>          wxPoint fbox_pos = field_box_placement( field_side );
>          EDA_RECT field_box( fbox_pos, m_fbox_size );
>  
>          if( aManual )
> -        {
> -            fbox_pos = fit_fields_between_wires( field_box, field_side );
> -            field_box.SetOrigin( fbox_pos );
> -        }
> +            force_wire_spacing = fit_fields_between_wires( &field_box, field_side );
>  
>          // Move the fields
> +        int last_y_coord = field_box.GetTop();
>          for( int field_idx = 0; field_idx < m_fields.size(); ++field_idx )
>          {
>              SCH_FIELD* field = m_fields[field_idx];
> @@ -163,7 +164,7 @@ public:
>  
>              wxPoint pos(
>                  field_horiz_placement( field, field_box ),
> -                field_box.GetY() + (FIELD_V_SPACING * field_idx) );
> +                field_vert_placement( field, field_box, &last_y_coord, !force_wire_spacing ) );
>  
>              if( m_align_to_grid )
>              {
> @@ -179,29 +180,40 @@ public:
>  protected:
>      /**
>       * Compute and return the size of the fields' bounding box.
> +     * @param aDynamic - if true, use dynamic spacing
>       */
> -    wxSize ComputeFBoxSize()
> +    wxSize ComputeFBoxSize( bool aDynamic )
>      {
>          int max_field_width = 0;
> +        int total_height = 0;
> +
>          BOOST_FOREACH( SCH_FIELD* field, m_fields )
>          {
>              int field_width;
> +            int field_height;
>  
>              if( m_component->GetTransform().y1 )
>              {
>                  field->SetOrientation( TEXT_ORIENT_VERT );
> -                field_width = field->GetBoundingBox().GetHeight();
>              }
>              else
>              {
>                  field->SetOrientation( TEXT_ORIENT_HORIZ );
> -                field_width = field->GetBoundingBox().GetWidth();
>              }
>  
> +            field_width = field->GetBoundingBox().GetWidth();
> +            field_height = field->GetBoundingBox().GetHeight();
> +
>              max_field_width = std::max( max_field_width, field_width );
> +
> +            if( aDynamic )
> +                total_height += field_height + get_field_padding();
> +            else
> +                total_height += WIRE_V_SPACING;
> +
>          }
>  
> -        return wxSize( max_field_width, int( FIELD_V_SPACING * (m_fields.size() - 1) ) );
> +        return wxSize( max_field_width, total_height );
>      }
>  
>  
> @@ -519,16 +531,16 @@ protected:
>      /**
>       * Function fit_fields_between_wires
>       * Shift a field box up or down a bit to make the fields fit between some wires.
> -     * Returns the new position of the field bounding box.
> +     * Returns true if a shift was made.
>       */
> -    wxPoint fit_fields_between_wires( const EDA_RECT& aBox, SIDE aSide )
> +    bool fit_fields_between_wires( EDA_RECT* aBox, SIDE aSide )
>      {
>          if( aSide != SIDE_TOP && aSide != SIDE_BOTTOM )
> -            return aBox.GetPosition();
> +            return false;
>  
> -        std::vector<SCH_ITEM*> colliders = filtered_colliders( aBox );
> +        std::vector<SCH_ITEM*> colliders = filtered_colliders( *aBox );
>          if( colliders.empty() )
> -            return aBox.GetPosition();
> +            return false;
>  
>          // Find the offset of the wires for proper positioning
>          int offset = 0;
> @@ -537,24 +549,28 @@ protected:
>          {
>              SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
>              if( !line )
> -                return aBox.GetPosition();
> +                return false;
>              wxPoint start = line->GetStartPoint(), end = line->GetEndPoint();
>              if( start.y != end.y )
> -                return aBox.GetPosition();
> +                return false;
>  
> -            int this_offset = (3 * FIELD_V_SPACING / 2) - ( start.y % FIELD_V_SPACING );
> +            int this_offset = (3 * WIRE_V_SPACING / 2) - ( start.y % WIRE_V_SPACING );
>              if( offset == 0 )
>                  offset = this_offset;
>              else if( offset != this_offset )
> -                return aBox.GetPosition();
> +                return false;
>          }
>  
>          if( aSide == SIDE_TOP )
>              offset = -offset;
>  
> -        wxPoint pos = aBox.GetPosition();
> -        pos.y = round_n( pos.y - offset, FIELD_V_SPACING, aSide == SIDE_BOTTOM ) + offset;
> -        return pos;
> +        wxPoint pos = aBox->GetPosition();
> +        pos.y = round_n( pos.y - offset, WIRE_V_SPACING, aSide == SIDE_BOTTOM ) + offset;
> +
> +        // Compensate for padding
> +        pos.y += WIRE_V_SPACING / 2;
> +        aBox->SetOrigin( pos );
> +        return true;
>      }
>  
>  
> @@ -597,6 +613,59 @@ protected:
>          return field_xcoord;
>      }
>  
> +    /**
> +     * Function field_vert_placement
> +     * Place a field vertically. Because field vertical placements accumulate,
> +     * this takes a pointer to a vertical position accumulator.
> +     *
> +     * @param aField - the field to place.
> +     * @param aFieldBox - box in which fields will be placed.
> +     * @param aPosAccum - pointer to a position accumulator
> +     * @param aDynamic - use dynamic spacing
> +     *
> +     * @return Correct field vertical position
> +     */
> +    int field_vert_placement( SCH_FIELD *aField, const EDA_RECT &aFieldBox, int *aPosAccum,
> +            bool aDynamic )
> +    {
> +        int field_height;
> +        int padding;
> +
> +        if( aDynamic )
> +        {
> +            if( m_component->GetTransform().y1 )
> +                field_height = aField->GetBoundingBox().GetWidth();
> +            else
> +                field_height = aField->GetBoundingBox().GetHeight();
> +            field_height = aField->GetBoundingBox().GetHeight();
> +
> +            padding = get_field_padding();
> +        }
> +        else
> +        {
> +            field_height = WIRE_V_SPACING / 2;
> +            padding = WIRE_V_SPACING / 2;
> +        }
> +
> +        int placement = *aPosAccum + padding / 2 + field_height / 2;
> +
> +        *aPosAccum += padding + field_height;
> +
> +        return placement;
> +    }
> +
> +    /**
> +     * Function get_field_padding
> +     * Return the desired padding between fields.
> +     */
> +    int get_field_padding()
> +    {
> +        if( m_align_to_grid )
> +            return FIELD_PADDING_ALIGNED;
> +        else
> +            return FIELD_PADDING;
> +    }
> +
>  };
>  
>  const AUTOPLACER::SIDE AUTOPLACER::SIDE_TOP( 0, -1 );

diff --git a/eeschema/autoplace_fields.cpp b/eeschema/autoplace_fields.cpp
index 1efd40e..c8a7aa6 100644
--- a/eeschema/autoplace_fields.cpp
+++ b/eeschema/autoplace_fields.cpp
@@ -63,9 +63,11 @@
 #include <vector>
 #include <algorithm>
 
-#define FIELD_V_SPACING 100
+#define FIELD_PADDING 10            // arbitrarily chosen for aesthetics
+#define FIELD_PADDING_ALIGNED 18    // aligns 50 mil text to a 100 mil grid
+#define WIRE_V_SPACING 100
 #define HPADDING 25
-#define VPADDING 50
+#define VPADDING 25
 
 /**
  * Function round_n
@@ -127,7 +129,7 @@ public:
         Kiface().KifaceSettings()->Read( AUTOPLACE_ALIGN_KEY, &m_align_to_grid, false );
 
         m_comp_bbox = m_component->GetBodyBoundingBox();
-        m_fbox_size = ComputeFBoxSize();
+        m_fbox_size = ComputeFBoxSize( /* aDynamic */ true );
 
         m_power_symbol = ! m_component->IsInNetlist();
 
@@ -143,17 +145,16 @@ public:
      */
     void DoAutoplace( bool aManual )
     {
+        bool force_wire_spacing = false;
         SIDE field_side = choose_side_for_fields( aManual );
         wxPoint fbox_pos = field_box_placement( field_side );
         EDA_RECT field_box( fbox_pos, m_fbox_size );
 
         if( aManual )
-        {
-            fbox_pos = fit_fields_between_wires( field_box, field_side );
-            field_box.SetOrigin( fbox_pos );
-        }
+            force_wire_spacing = fit_fields_between_wires( &field_box, field_side );
 
         // Move the fields
+        int last_y_coord = field_box.GetTop();
         for( unsigned field_idx = 0; field_idx < m_fields.size(); ++field_idx )
         {
             SCH_FIELD* field = m_fields[field_idx];
@@ -163,7 +164,7 @@ public:
 
             wxPoint pos(
                 field_horiz_placement( field, field_box ),
-                field_box.GetY() + (FIELD_V_SPACING * field_idx) );
+                field_vert_placement( field, field_box, &last_y_coord, !force_wire_spacing ) );
 
             if( m_align_to_grid )
             {
@@ -179,29 +180,40 @@ public:
 protected:
     /**
      * Compute and return the size of the fields' bounding box.
+     * @param aDynamic - if true, use dynamic spacing
      */
-    wxSize ComputeFBoxSize()
+    wxSize ComputeFBoxSize( bool aDynamic )
     {
         int max_field_width = 0;
+        int total_height = 0;
+
         BOOST_FOREACH( SCH_FIELD* field, m_fields )
         {
             int field_width;
+            int field_height;
 
             if( m_component->GetTransform().y1 )
             {
                 field->SetOrientation( TEXT_ORIENT_VERT );
-                field_width = field->GetBoundingBox().GetHeight();
             }
             else
             {
                 field->SetOrientation( TEXT_ORIENT_HORIZ );
-                field_width = field->GetBoundingBox().GetWidth();
             }
 
+            field_width = field->GetBoundingBox().GetWidth();
+            field_height = field->GetBoundingBox().GetHeight();
+
             max_field_width = std::max( max_field_width, field_width );
+
+            if( aDynamic )
+                total_height += field_height + get_field_padding();
+            else
+                total_height += WIRE_V_SPACING;
+
         }
 
-        return wxSize( max_field_width, int( FIELD_V_SPACING * (m_fields.size() - 1) ) );
+        return wxSize( max_field_width, total_height );
     }
 
 
@@ -519,16 +531,16 @@ protected:
     /**
      * Function fit_fields_between_wires
      * Shift a field box up or down a bit to make the fields fit between some wires.
-     * Returns the new position of the field bounding box.
+     * Returns true if a shift was made.
      */
-    wxPoint fit_fields_between_wires( const EDA_RECT& aBox, SIDE aSide )
+    bool fit_fields_between_wires( EDA_RECT* aBox, SIDE aSide )
     {
         if( aSide != SIDE_TOP && aSide != SIDE_BOTTOM )
-            return aBox.GetPosition();
+            return false;
 
-        std::vector<SCH_ITEM*> colliders = filtered_colliders( aBox );
+        std::vector<SCH_ITEM*> colliders = filtered_colliders( *aBox );
         if( colliders.empty() )
-            return aBox.GetPosition();
+            return false;
 
         // Find the offset of the wires for proper positioning
         int offset = 0;
@@ -537,24 +549,28 @@ protected:
         {
             SCH_LINE* line = dynamic_cast<SCH_LINE*>( item );
             if( !line )
-                return aBox.GetPosition();
+                return false;
             wxPoint start = line->GetStartPoint(), end = line->GetEndPoint();
             if( start.y != end.y )
-                return aBox.GetPosition();
+                return false;
 
-            int this_offset = (3 * FIELD_V_SPACING / 2) - ( start.y % FIELD_V_SPACING );
+            int this_offset = (3 * WIRE_V_SPACING / 2) - ( start.y % WIRE_V_SPACING );
             if( offset == 0 )
                 offset = this_offset;
             else if( offset != this_offset )
-                return aBox.GetPosition();
+                return false;
         }
 
         if( aSide == SIDE_TOP )
             offset = -offset;
 
-        wxPoint pos = aBox.GetPosition();
-        pos.y = round_n( pos.y - offset, FIELD_V_SPACING, aSide == SIDE_BOTTOM ) + offset;
-        return pos;
+        wxPoint pos = aBox->GetPosition();
+        pos.y = round_n( pos.y - offset, WIRE_V_SPACING, aSide == SIDE_BOTTOM ) + offset;
+
+        // Compensate for padding
+        pos.y += WIRE_V_SPACING / 2;
+        aBox->SetOrigin( pos );
+        return true;
     }
 
 
@@ -597,6 +613,59 @@ protected:
         return field_xcoord;
     }
 
+    /**
+     * Function field_vert_placement
+     * Place a field vertically. Because field vertical placements accumulate,
+     * this takes a pointer to a vertical position accumulator.
+     *
+     * @param aField - the field to place.
+     * @param aFieldBox - box in which fields will be placed.
+     * @param aPosAccum - pointer to a position accumulator
+     * @param aDynamic - use dynamic spacing
+     *
+     * @return Correct field vertical position
+     */
+    int field_vert_placement( SCH_FIELD *aField, const EDA_RECT &aFieldBox, int *aPosAccum,
+            bool aDynamic )
+    {
+        int field_height;
+        int padding;
+
+        if( aDynamic )
+        {
+            if( m_component->GetTransform().y1 )
+                field_height = aField->GetBoundingBox().GetWidth();
+            else
+                field_height = aField->GetBoundingBox().GetHeight();
+            field_height = aField->GetBoundingBox().GetHeight();
+
+            padding = get_field_padding();
+        }
+        else
+        {
+            field_height = WIRE_V_SPACING / 2;
+            padding = WIRE_V_SPACING / 2;
+        }
+
+        int placement = *aPosAccum + padding / 2 + field_height / 2;
+
+        *aPosAccum += padding + field_height;
+
+        return placement;
+    }
+
+    /**
+     * Function get_field_padding
+     * Return the desired padding between fields.
+     */
+    int get_field_padding()
+    {
+        if( m_align_to_grid )
+            return FIELD_PADDING_ALIGNED;
+        else
+            return FIELD_PADDING;
+    }
+
 };
 
 const AUTOPLACER::SIDE AUTOPLACER::SIDE_TOP( 0, -1 );

Follow ups

References