Python scripting: reading zone outline

Hello,

I would like to read the outline of a zone using the scripting interface of Kicad. I am proficient with python, but somehow I can’t get around this one. I know how to create a new zone, but I would like to get a list of points in the outline.

I can get a reference to my zone as follows:

>>> import pcbnew
>>> area = pcbnew.GetBoard().GetArea(0) # for area at index 0
>>> area
<pcbnew.ZONE_CONTAINER; proxy of <Swig Object of type 'ZONE_CONTAINER *' at 0x12195d2d0> >

Then I can get the outline as follows

>>> outline = area.Outline()
>>> outline
<pcbnew.SHAPE_POLY_SET; proxy of <Swig Object of type 'SHAPE_POLY_SET *' at 0x1173589f0> >

Then essentially I would like to get the vertices, but everything I try ends up in some C++ iterator or pointer wrapper. I tried to Google for how to use such Swig objects but I could not figure out anything.

Trying to get the first outline:

>>> outline.Outline(0)
<Swig Object of type 'SHAPE_LINE_CHAIN *' at 0x117358ed0>

Trying to get a vertex:

>>> outline.Vertex(0)
<Swig Object of type 'VECTOR2I *' at 0x117358f60>

Trying to iterate:

>>> outline.Iterate()
<Swig Object of type 'SHAPE_POLY_SET::ITERATOR_TEMPLATE< VECTOR2I > *' at 0x117358cc0>

In all these cases the returned object has only four methods (at least according to the auto-completion in Kicad):

  1. append
  2. acquire
  3. disown
  4. own

And none of these seem to have anything to do with actually dereferencing or obtaining access the underlying object of type SHAPE_LINE_CHAIN or VECTOR2I. I tried to call some methods of SHAPE_LINE_CHAIN, such as SegmentCount, or GetPosition, GetX and so on, but no luck. I looked if in pcbnew there is some method for casting or dereferencing but I found nothing, and all the examples (also in this forum) are about creating, not reading an outline.

What version of KiCad are you using?

This works for me on KiCad 4.0.7 stable, Windows 7:

To get the corners of the first (index 0) area on the board:

b = pcbnew.GetBoard()
a = b.GetArea(0)
c = [a.GetCornerPosition(i) for i in range(a.GetNumCorners())]
# c should now be the list of wxPoint objects that are the corners of area 0

To get the corners of all areas (i.e. zones and keepouts):

b=pcbnew.GetBoard()
areas = [b.GetArea(i) for i in range(b.GetAreaCount())]
areacorners = []
for a in areas:
    areacorners.append([a.GetCornerPosition(i) for i in range(a.GetNumCorners())])

or all in nested list comprehensions:

b=pcbnew.GetBoard()
areacorners = [[a.GetCornerPosition(i) for i in range(a.GetNumCorners())] for a in [b.GetArea(i) for i in range(b.GetAreaCount())]]

Edit to add: looks like the nightly also has these functions, so I would expect it to work (according to the pcbnew documentation.

One difference is the Outline() returns SHAPE_POLY_SET for you, and for me, it returns a CPolyLine.

On mine (4.0.7 stable) I can get to corners also through Outline().m_CornersList.GetCorner(index)
But since your Outline() returns a differently named class, it might not have the same properties.

Edit again to add:
I’ve added commands to KiCommand to retrieve areas, zones, and keepouts. And a command to extract areacorners from any of the areas. Let me know if you want me to release the new code on github.

2 Likes

2017-10-17 revision 537804b on OSX (I had issues with the zone fill using the OpenGL canvas with the stable, so I tried the nightly).

I looked in the documentation and in pcbnew.py for something like 4h, I even saw that method but I associated “corner” to GetCornerRadius and GetCornerSmoothingType, some generic “corner property”, never to the actual vertex. :unamused: :sweat_smile:[quote=“HiGreg, post:2, topic:8302”]
Edit to add: looks like the nightly also has these functions, so I would expect it to work (according to the pcbnew documentation.

One difference is the Outline() returns SHAPE_POLY_SET for you, and for me, it returns a CPolyLine.
[/quote]

I tried to cast to some shape poly list and to create a CPolyLine out of it, but no luck. Also the GetCornerPosition method does not return a wxPoint object, instead it returns a pointer to VECTOR2I:

>>> area.GetCornerPosition(0)
<Swig Object of type 'VECTOR2I *' at 0x11403f990>

Which again does not have any of the x and y properties. Many other properties (such as GetPosition in a module) correctly return a wxPoint.[quote=“HiGreg, post:2, topic:8302”]
I’ve added commands to KiCommand to retrieve areas, zones, and keepouts. And a command to extract areacorners from any of the areas. Let me know if you want me to release the new code on github.
[/quote]

I am not using KiCommand in this project, but I think it could be definitely useful to have it there; in my (limited) experience with scripting KiCad I found other pieces of code being the main resource to learn how to use the API.

There does appear to be a difference in the KiCad source code for handling GetCornerPosition(). In the master branch (i.e. nightly), the return type is VECTOR2I&, while in the 4.0 branch the return type is wxPoint&

Here are some excerpts of code from master and 4.0 branch:

include/math/vector2d.h (both 4.0 and master)
typedef VECTOR2<int>          VECTOR2I;


4.0/pcbnew/class_zone.h
const wxPoint& GetCornerPosition( int aCornerIndex ) const
    {
        return m_Poly->GetPos( aCornerIndex );
    }

const VECTOR2I& GetCornerPosition( int aCornerIndex ) const
    {
        SHAPE_POLY_SET::VERTEX_INDEX index;

        // Convert global to relative indices
        if( !m_Poly->GetRelativeIndices( aCornerIndex, &index ) )
            throw( std::out_of_range( "aCornerIndex-th vertex does not exist" ) );

        return m_Poly->CVertex( index );
    }


master/pcbnew/class_zone.h 

typedef union {
        wxPoint wx;
        VECTOR2I vector;
    } WX_VECTOR_CONVERTER;

So in terms of memory structure, VECTOR2I and wxPoint are identical. The master branch has a convenience union of WX_VECTOR_CONVERTOR to help with translating.

One thing to try is to see if you can index the return value of GetCornerPosition (i.e. value[0] and value[1]). Otherwise, I think this is a problem with swig automatically determining the return type.

There is surely a lot more subtlety than I can glean from the source code, but it seems pretty clear this is the source of the problem (hehe, get it? source). I don’t know if the answer is in defining something different in the code or in defining something in the swig files (*.i files). Sorry I can’t help further.

I know this doesn’t necessarily help your issue at the moment, but maybe this will point the way to fixing the nightlies. Have you tried the latest nightly?

If nothing helps, perhaps filing a bug and pointing to (or copying) this explanation may help point a developer to the right location.

Thanks for the reply.

I tried with version a562525 but the problem persists.

Indexing the value did not help. I think you are right, and Swig just can’t figure out the correct type.

I will submit a bug report :wink:


Edit: here goes the bug report link: https://bugs.launchpad.net/kicad/+bug/1728611

1 Like