Generating paint area for non copper regions not doing anything

Issue #135 resolved
Mohd Lee
created an issue

I've been using this feature back in 7.0 in windows and it's working fine. On recent update to 8.3, the status bar shows idle shortly after clicking inside the polygon. When I run from Linux using the latest code, I'm getting the following error in my console

[DEBUG][MainThread] on_paint_button_click--> FlatCAMObj.read_form()
[DEBUG][MainThread] button=1, x=488, y=526, xdata=17.075127, ydata=43.276854
[DEBUG][MainThread] FCVisibleProcessContainer.on_change()
[DEBUG][Dummy-2] Running task: {'params': [<FlatCAMApp.App object at 0x7f250b0a55a8>], 'fcn': <function job_thread at 0x7f24e8eb96e0>}
[DEBUG][Dummy-1] Running task: {'params': [<FlatCAMApp.App object at 0x7f250b0a55a8>], 'fcn': <function job_thread at 0x7f24e8eb96e0>}
[DEBUG][Dummy-2] Task ignored.
[DEBUG][Dummy-1] new_object()
[DEBUG][Dummy-1] Calling object constructor...
[DEBUG][Dummy-1] 0.000329 seconds before initialize().
[DEBUG] camlib.clear_polygon()
[DEBUG][Dummy-1] FCVisibleProcessContainer.on_done()
Traceback (most recent call last):
  File "/home/xxx/build/flatcam/FlatCAMWorker.py", line 32, in do_worker_task
    task['fcn'](*task['params'])
  File "/home/xxx/build/flatcam/FlatCAMObj.py", line 1025, in job_thread
    raise e
AssertionError

I went back to test 7.0, 8.0 and 8.1 in Linux and it's still working and the last working version is 6733ebb, and it broke in c2c7a83 and the error is slightly different

[DEBUG][MainThread] on_paint_button_click--> FlatCAMObj.read_form()
[DEBUG][MainThread] button=1, x=186, y=354, xdata=17.332544, ydata=49.022995
[DEBUG][MainThread] No active tool to respond to click!
[DEBUG][Dummy-1] Running task: {'params': [<FlatCAMApp.App object at 0x7f52b33960e8>], 'fcn': <function job_thread at 0x7f52980d3aa0>}
[DEBUG][Dummy-2] Running task: {'params': [<FlatCAMApp.App object at 0x7f52b33960e8>], 'fcn': <function job_thread at 0x7f52980d3aa0>}
[DEBUG][Dummy-1] new_object()
[DEBUG][Dummy-1] new_object --> OC.get_names()
[DEBUG][Dummy-1] Calling object constructor...
[DEBUG][Dummy-1] 0.003489 seconds before initialize().
Traceback (most recent call last):
  File "/home/faulty/build/flatcam/FlatCAMWorker.py", line 32, in do_worker_task
    task['fcn'](*task['params'])
  File "/home/xxx/build/flatcam/FlatCAMObj.py", line 999, in job_thread
    app_obj.new_object("geometry", name, gen_paintarea)
  File "/home/xxx/build/flatcam/FlatCAMApp.py", line 769, in new_object
    initialize(obj, self)
  File "/home/xxx/build/flatcam/FlatCAMObj.py", line 993, in gen_paintarea
    geo_obj.solid_geometry = list(cp.get_objects())
AttributeError: 'list' object has no attribute 'get_objects'

Comments (28)

  1. jpcgt repo owner

    I understood that. I'm not understanding when (in which commit) it used to work, when it failed, and when it presents the first traceback and then the other.

  2. jpcgt repo owner

    Okay. I cannot reproduce the problem, so I will need you to run some tests. Let's apply "divide-and-conquer": Generating the non-copper regions and painting are two separate things, so lets figure out which one if causing the problem.

    Please create a simple rectangle using the geometry editor:

    1. Start FlatCAM.
    2. Do Edit→New Geometry
    3. Select the new geometry from the Project list
    4. Do Edit→Edit Geometry
    5. Create a rectangle
    6. Do Edit→Update Geometry
    7. Double click on the item in Project to go to the Geometry object's properties/options.
    8. Under Paint Area, set the desired values and click generate.
    9. Click inside the rectangle.

    This works for me on 8.3.

  3. Mohd Lee reporter

    It failed on me on Arch Linux, with the latest code.

    [INFO][MainThread] FlatCAM Starting...
    [DEBUG][MainThread] Application path is /home/xxx/build/flatcam
    [DEBUG][MainThread] Started in /home/xxx/build/flatcam
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] propagate_defaults()
    [DEBUG][MainThread]   cncjob_coordinate_format OK!
    [DEBUG][MainThread]   zdownrate OK
    [DEBUG][MainThread]   excellon_zeros OK!
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] options_write_form_field(): No field for: def_win_h
    [DEBUG][MainThread] options_write_form_field(): No field for: def_win_w
    [DEBUG][MainThread] options_write_form_field(): No field for: cncjob_coordinate_format
    [DEBUG][MainThread] options_write_form_field(): No field for: zoom_ratio
    [DEBUG][MainThread] options_write_form_field(): No field for: shell_at_startup
    [DEBUG][MainThread] options_write_form_field(): No field for: serial
    [DEBUG][MainThread] options_write_form_field(): No field for: shell_shape
    [DEBUG][MainThread] options_write_form_field(): No field for: zoom_in_key
    [DEBUG][MainThread] options_write_form_field(): No field for: zoom_out_key
    [DEBUG][MainThread] options_write_form_field(): No field for: def_win_y
    [DEBUG][MainThread] options_write_form_field(): No field for: stats
    [DEBUG][MainThread] options_write_form_field(): No field for: recent_limit
    [DEBUG][MainThread] options_write_form_field(): No field for: defaults_save_period_ms
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] options_write_form_field(): No field for: fit_key
    [DEBUG][MainThread] options_write_form_field(): No field for: excellon_zeros
    [DEBUG][MainThread] options_write_form_field(): No field for: zdownrate
    [DEBUG][MainThread] options_write_form_field(): No field for: def_win_x
    [DEBUG][MainThread] options_write_form_field(): No field for: last_folder
    [DEBUG][MainThread] options_write_form_field(): No field for: point_clipboard_format
    [DEBUG][MainThread] Options --> 0
    [INFO][MainThread] Starting Worker...
    [INFO][MainThread] Checking for updates in backgroud (this is version 8.3).
    [DEBUG][Dummy-1] Worker Started!
    [DEBUG][Dummy-2] Worker Started!
    [DEBUG][MainThread] setup_recent_items()
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] setup_shell()
    [DEBUG][MainThread] END of constructor. Releasing control.
    [DEBUG][Dummy-1] Running task: {'worker_name': 'worker2', 'params': [], 'fcn': <bound method App.version_check of <FlatCAMApp.App object at 0x7f2f812006d8>>}
    [DEBUG][Dummy-1] Task ignored.
    [DEBUG][Dummy-2] Running task: {'worker_name': 'worker2', 'params': [], 'fcn': <bound method App.version_check of <FlatCAMApp.App object at 0x7f2f812006d8>>}
    [DEBUG][Dummy-2] version_check()
    [DEBUG][Dummy-2] Checking for updates @ http://flatcam.org/version?s=3vvle8uanqozw6k0so6t&v=8.3&os=unix&cncjob_on_exportgcode_button=1&on_options_app2project=7&save_defaults=128&on_delete=1&on_file_openproject=4&geometry_on_paint_button=7&on_fileopengerber=10&gerber_on_iso_button=5&geometry_on_generatecnc_button=2&on_file_new=7&gerber_on_generatenoncopper_button=12
    [DEBUG][Dummy-2] FlatCAM is up to date!
    [DEBUG][MainThread] new_object()
    [DEBUG][MainThread] Calling object constructor...
    [DEBUG][MainThread] 0.000163 seconds before initialize().
    [DEBUG][MainThread] 0.000047 seconds executing initialize().
    [DEBUG][MainThread] Moving new object back to main thread.
    [DEBUG][MainThread] on_object_created()
    [DEBUG][MainThread] on_object_created --> OC.append()
    [DEBUG][MainThread] append --> OC.get_names()
    [DEBUG][MainThread] FlatCAMGeometry.set_ui()
    [DEBUG][MainThread] plot --> FlatCAMObj.plot()
    [WARNING][MainThread] Did not plot:<type 'NoneType'>
    [DEBUG][MainThread] on_zoom_fit--> OC.get_bounds()
    [DEBUG] Geometry->bounds()
    [DEBUG] solid_geometry is None
    [ERROR][MainThread] Height is 0.000000
    [DEBUG][MainThread] 0.086212 seconds adding object and plotting.
    [DEBUG][MainThread] on_list_selection_change()
    [DEBUG][MainThread] Current: <PyQt4.QtGui.QItemSelection object at 0x7f2f805a8b90>, Previous <PyQt4.QtGui.QItemSelection object at 0x7f2f6d2a0758>
    [DEBUG][MainThread] build_ui--> FlatCAMObj.build_ui()
    [DEBUG][MainThread] Radio toggled
    [DEBUG][MainThread] Mouse button pressed on list
    [DEBUG][MainThread] plot_all()
    [DEBUG][MainThread] on_tool_select('select')
    [DEBUG][MainThread] select is checked.
    [DEBUG][MainThread] plot_all()
    [DEBUG][MainThread] on_tool_select('rectangle')
    [DEBUG][MainThread] rectangle is checked.
    [DEBUG][MainThread] button=1, x=327, y=600, xdata=-0.058398, ydata=0.639600
    [DEBUG][MainThread] button=1, x=715, y=312, xdata=0.373192, ydata=0.319244
    [DEBUG][MainThread] on_shape_complete()
    [DEBUG][MainThread] plot_all()
    [DEBUG][MainThread] plot_all()
    [DEBUG][MainThread] plot --> FlatCAMObj.plot()
    [DEBUG][MainThread] Mouse button pressed on list
    [DEBUG][MainThread] build_ui--> FlatCAMObj.build_ui()
    [DEBUG][MainThread] Mouse button pressed on list
    [DEBUG][MainThread] on_paint_button_click--> FlatCAMObj.read_form()
    [DEBUG][MainThread] button=1, x=400, y=429, xdata=0.037775, ydata=0.447748
    [DEBUG][MainThread] FCVisibleProcessContainer.on_change()
    [DEBUG][Dummy-2] Running task: {'params': [<FlatCAMApp.App object at 0x7f2f812006d8>], 'fcn': <function job_thread at 0x7f2f6db192a8>}
    [DEBUG][Dummy-2] Task ignored.
    [DEBUG][Dummy-1] Running task: {'params': [<FlatCAMApp.App object at 0x7f2f812006d8>], 'fcn': <function job_thread at 0x7f2f6db192a8>}
    [DEBUG][Dummy-1] new_object()
    [DEBUG][Dummy-1] Calling object constructor...
    [DEBUG][Dummy-1] 0.000159 seconds before initialize().
    [DEBUG] camlib.clear_polygon()
    [DEBUG][Dummy-1] FCVisibleProcessContainer.on_done()
    Traceback (most recent call last):
      File "/home/xxx/build/flatcam/FlatCAMWorker.py", line 32, in do_worker_task
        task['fcn'](*task['params'])
      File "/home/xxx/build/flatcam/FlatCAMObj.py", line 1025, in job_thread
        raise e
    AttributeError: 'NoneType' object has no attribute 'coords'
    

    When I ran in Windows with version 8.3 (installed from setup), your test step works, but not from my imported gerble file.

  4. aineko

    I am experiencing the same issue. What I noticed is that it works fine with small (or zero) paint geometry margin, but when I set the margin above certain threshold it fails consistently.

  5. jpcgt repo owner

    Please attach the Gerber and list the exact sequence of actions with full detail that lead to the problem, including the exact coordinates where you click to do the painting.

  6. aineko

    @jpcgt, I can reproduce the issue without importing any gerbers.

    • Create 1x1mm rectangle (in the shell):
    > new_geometry test
    > add_rect test 0.0 0.0 1.0 1.0
    
    • Select the geometry and set Paint Area parameters:
    Tool dia 2.0
    Overlap 0.15
    Margin 0.2
    
    • Click on Generate and then click anywhere inside the rectangle, this results in:
    Traceback (most recent call last):
      File "/home/ai/opt/FlatCAM-8.3/FlatCAMWorker.py", line 32, in do_worker_task
        task['fcn'](*task['params'])
      File "/home/ai/opt/FlatCAM-8.3/FlatCAMObj.py", line 1025, in job_thread
        raise e
    AttributeError: 'NoneType' object has no attribute 'coords'
    

    If the Paint Area parameters are reduced, it works fine. For example try Tool dia 0.1

  7. Thomas Duffin

    I have encountered, replicated and fixed the original bug described (I will submit my code as soon as I get time).

    The issue occurs in clear_polygon() in camlib.py.

    When using the standard paint area method (seed-based has no issue as it uses a different function) if the margin is 0, clear_polygon() is passed a polygon of type 'Polygon' but when the margin is >0 (or possibly a small enough value, I haven't experimented to find a threshold) it is passed a polygon of type 'MultiPolygon' which causes an assertion error on line camlib.py:382.

    My gut tells me that this is probably due to a recent change in exactly how shapely works? I am however still new to, and in the process of familiarising myself with the FlatCam source code, so if anyone else can shed more light onto exactly why this is happening now, be my guest.

  8. jpcgt repo owner

    I'm not sure how exactly we are getting a MultiPolygon, but if we are, it means there can be more than one Polygon in it. The whole point of the "click on the plot" feature is for the user to select a single polygon. The problem is not here but somewhere up the call chain.

    @Thomas Duffin do you want to look further into this? If you are going to, I will explore other bugs at this time.

  9. Thomas Duffin

    Edit: My defaults file and example gerber (RGBLEDMount - Top Copper.gtl) have been attached as requested.

    Procedure

    • Open gerber.
    • Generate non-copper regions for the gerber.
    • Paint-Area inside the traces. The non-copper areas in this gerber happen to form a single polygon, so it should be obvious where to paint. I get the same area on other gerbers with multiple polygons though.

    Paint-area options:

    • Tool Dia: 0.3
    • Overlap: 0.1
    • Margin: 0.2
    • Method: Standard

    (I use mm as my units)

    • With a margin of 0 the operation completes successfully.
    • With a margin of 0.2 the operation fails as described above. IE AssertionError is raised

    Basic debugging

    print type(polygon) on camlib.py: 380 gives:

    "Polygon" when margin is 0 "MultiPolygon" when margin is 0.2

    Changing camlib.py:382 to "assert type(polygon) == MultiPolygon or type(polygon) == Polygon" allows the operation to complete successfully with either margin size.

    Possibly useful information

    • OS: Windows 7 64bit
    • Python: 2.7.9
    • Shapely: 1. 5.7

    If there's anything else you can think of that may be of use, I'll be happy to provide.

  10. Thomas Duffin

    Great timing!

    I will have a poke around.

    The next thing I want to try is attempting to replicate on linux and with other versions of shapely, and I'll get some debugging info on why it thinks there are multiple polygons to process.

    Are there any areas of the code you would recommend looking in to? I'm still figuring out the full chain of functions the polygon is passed through.

  11. jpcgt repo owner

    In FlatCAMObj.FlatCAMGeometry.paint_poly(): poly = self.find_polygon(inside_pt).

    find_polygon in camlib.py line 145. It recursively looks into geometry objects until it cannot iterate over them any further. For some reason this is not working with MultiPolygon.

  12. Thomas Duffin

    I have tracked down the actual cause of the problem. My gut was correct, it is just one of shapely's eccentricities.

    When you call Polygon.buffer() (in this case on FlatCAMObj.py:1095) if the buffer distance is great enough that the polygon breaks up, it returns the points as a MultiPolygon.

    No multi-polygon detection is happening or anything untoward like that.

    This is already handled adequately in clear_polygon():

    • .buffer() on line camlib.py:392 can happily handle both Polygon and Multipolygon objects.
    • The try/except starting at line camlib.py:395 already handles the Polygon/Multipolygon possibility created by the camlib.py:392 .buffer().

    Therefore my original fix should be satisfactory as it is simply the assert on camlib.py:382 un-necessarily blocking Multipolygons being passed to clear_polygon().

    Can you un-decline my pull request or do I need to re-post it?

  13. jpcgt repo owner

    Okay, this is starting to make sense. So the user chooses a single polygon by clicking on the screen, but the buffer operation can break the polygon into multiple polygons, therefore multiple polygons end up in the clear_polygon() method. This sounds like a totally acceptable situation.

  14. jpcgt repo owner

    Cannot un-decline. Please submit a new one, and please add one big comment explaining how it happens that a MultiPolygon reaches that method when trying to paint/clear a poly. Thanks for figuring this one out!

  15. jpcgt repo owner

    Fixes #135

    The polygon passed to clear_polygon() is generated using shapely’s buffer() function on line FlatCAMObj.py:1095.

    When the margin given to the buffer() function is small, a single Polygon object is returned. If the margin is large enough it causes the polygon to be broken into pieces and a Multipolygon is returned instead. A visualisation of this can be seen in the shapely manual in the object.buffer() section.

    The first thing clear_polygon() does is buffer the polygon again to take the tool diameter into account and the Polygon/Multipolygon generated by this is handled further down the function. The buffer() function used to take the tool diameter into account can be called happily on both Polygon and Multipolygon objects so there is no reason to block Multipolygons being passed to clear_polygon().

    Therefore simply adding Multipolygon to the acceptable types in the assert statement on line camlib.py:382 fixes this bug and causes no further issues.

    → <<cset 2c9a30748300>>

  16. Log in to comment