How to finding if a point lies inside a specified layer.

Hello, I am looking for a function that returns true if a given point is inside a function.

The following function returns true if a point lies inside a polygon (specified by layer and a cell).
However I don't want to use this method because I don't want to iterate over all the shapes.
Is there a simpler way of finding if a point is inside or outside (touching or not touching) a specified layer.

iter = pya.RecursiveShapeIterator.new(layout, cell, layer_index)
specified_region = iter.shape().polygon
is_point_inside = specified_region.inside(pya.Point(x, y))

Thanks for the help!

Comments

  • edited October 2020

    Perhaps you could use Layout.begin_shapes_touching. You would need to make a Region out of your point.

  • edited October 2020

    @jheinsoo You're right. Confining the search does the trick.

    But you do not necessarily need a Region. Iterating probably won't hurt if you confine the search to a small area around the test point. This will return only those polygons whose bounding boxes already contain the point:

    test_box = pya.Box(x - 1, y - 1, x + 1 y + 1)
    iter = pya.RecursiveShapeIterator.new(layout, cell, layer_index, test_box, True)
    ...
    

    I think this is the most efficient way to do the test.

    Matthias

  • edited February 5

    I want to read out the layers, which are present for a given layout coordinate, given by a selected ruler. So I tried the approach above:

    import pya
    
    mw = pya.Application.instance().main_window()
    lv = mw.current_view()
    cv = lv.active_cellview()
    ly = cv.layout()
    
    for a in lv.each_annotation_selected():
      x1 = round(a.p1.x,1)
      y1 = round(a.p1.y,1)
      x2 = round(a.p2.x,1)
      y2 = round(a.p2.y,1)
      test_box = pya.Box(x1 - 1, y1 - 1, x1 + 1, y1 + 1)
    
    for li in ly.layer_indexes():
      iter = pya.RecursiveShapeIterator.new(lv, cv, li, test_box, True)
    

    But I get the following error message:

    TypeError: No overload with matching arguments. Variants are:
    static new RecursiveShapeIterator ptr new(const Layout layout, const Cell cell, unsigned int layer) [3 argument(s) expected, but 5 given]
    static new RecursiveShapeIterator ptr new(const Layout layout, const Cell cell, unsigned int[] layers) [3 argument(s) expected, but 5 given]
    static new RecursiveShapeIterator ptr new(const Layout layout, const Cell cell, unsigned int layer, const Box box, bool overlapping = false) [match candidate]
    static new RecursiveShapeIterator ptr new(const Layout layout, const Cell cell, unsigned int layer, const Region region, bool overlapping = false) [match candidate]
    static new RecursiveShapeIterator ptr new(const Layout layout, const Cell cell, unsigned int[] layers, const Region region, bool overlapping = false) [match candidate]
    static new RecursiveShapeIterator ptr new(const Layout layout, const Cell cell, unsigned int[] layers, const Box box, bool overlapping = false) [match candidate]
    in RecursiveShapeIterator.new

  • The first parameter should be a layout, not a layoutview; and the second should be a cell, not a cellview. Try using pya.RecursiveShapeIterator(ly, cv.cell, li, test_box, True) instead

  • edited February 5

    Wow, thank you, taylorn, for your fast response, which was completey correct! It works now without the error message, but the output is empty. How should the report look like, if an overlap of test_box and shape is detected by RecursiveShapeIterator?

  • No problem!

    Nothing is inherently displayed; all you've done is construct the iterator. Try calling iter.at_end(). If it's true, there is no shape; if it's false, there is a shape. You can use that boolean to continue as you wish.

  • Ok, but how can I get the layer indexes, where a shape exists, which overlaps with the test_box? For me, it is unclear, what the iterator contains exactly.

  • edited February 5

    The iterator is a RecursiveShapeIterator that contains, iterates over, all shapes in the cell on the layer index you provided. iter.at_end() detects whether there are no more shapes. If it's false, there is a shape that overlaps the box. If it's true, there isn't a shape.

    So to get the layer indices that have a shape, when you're iterating li over all layer indices, just keep the ones where iter.at_end() returns false.

    Hope this helps!

  • edited February 6

    Yes, thanks. Now I understand the structure. But when I read out iter.at_end(), I always get back False, even if I use RecursiveShapeIterator in a confined box, where no shapes are present on any layer. It seems, that the box confinement does not work for me, as I can read out shapes outside the box on the same layer with iter.shape(). I would expect, that RecursiveShapeIterator looks for shapes in the region only, which is specified by test_box.

  • One trouble might be that a.p1 is a DPoint, not a Point. The difference is that the DPoint's coordinates are multiplied by the layout's database unit. Essentially there's a mismatch between the DPoint coordinates and the coordinates the Box wants to use, which ARE the integer coordinates.

    Try using a.p1.to_itype(ly.dbu) in place of a.p1, and similarly for a.p2.

  • Thanks, that worked. Now I still have the problem, that RecursiveShapeIterator seems to check on bounding boxes rather than shape geometry, so I get a False also, when I put my test_box inside a shape with a hole. Is there a method to check on shape area rather than shape bounding box?

  • edited February 6

    That's a trouble that RecursiveShapeIterator has, that Matthias has addressed here. You can either check against the specific polygon the iterator delivers, or you can convert the whole layer into a region and call .interacting().

  • @taylorn, thanks for chiming in :)

    You're right, the RecursiveShapeIterator first of all finds shapes whose bounding box is touching the given search box. That is a first level of identification.

    Once you have a shape and you know it is a polygon (or you convert it to one), you can test if a given point is inside the polygon using Polygon#inside (https://www.klayout.de/doc-qt5/code/class_Polygon.html#method50).

    Note that the polygon may come from a subhierarchy, so you have to transform it into the top cell first, or you do the opposite with the point (disclaimer: not tested):

      while not iter.at_end():
         polygon = iter.shape.polygon
         if polygon is not None:
           # transform the polygon into the top level
           polygon = iter.trans() * polygon
           if polygon.inside(point):
             ... point is inside polygon
         iter.next()
    

    Matthias

Sign In or Register to comment.