Why this error and how to correct it?

Ok. Never mind.

Just a quick note.
When you make an iteration on edges ( “collected” by grep) in array, the order of the collection will be random. So depending on how this random order appearing you maybe get different results. Sometimes looks good sometimes not…
It is also depends if you first erase the edge faces.size > 2 then faces.size == 1 or opposite order.
Even if the geometry looks same the order of the edges in the array can be different in a second “trial” than you will get different result…

1 Like

The reason this happens is because you are breaking a fundamental rule of collections.
The rule is: Do not modify the collection while you are iterating it.

Instead, save up an array of items (faces) to be deleted, and then use the bulk entities delete method:

            edges_to_erase = []
            grp.entities.grep(Sketchup::Edge) do |e|
              edges_to_erase << e if e.faces.size != 2
            end
            grp.entities.erase_entities(edges_to_erase)
4 Likes

Thank you so much. Everything looks logical now. I will follow your advice.

Dan, Most probably (surely) you are right, I just would like to understand too…

Don’t we make a “shadow copy” of collection with grep and delete the edges form entities collection on the basis of the reference?

Let me tell you my own experience.


This is object before face erasing. As you can see in photo 4 edges have more than 2 faces. In grep imagine we find edge 1 first and delete it. In this way just red face deleted but if grep find edge 4 first and delete it 3 connected faces will be deleted. It will be random and not good for our codes.
Please forgive me if I waste your time.

Generally yes.

However, in this case it is more complex because it is one of the Entities collections and the calls to edge.erase! cause the SketchUp engine to also erase faces if the deleted edge was on the outer loop of a face.

Look back and you’ll see the error was that calls to edge.faces returned empty arrays that return nil for .first and .last. So also a conditional like next if edge.faces.empty? can be used to avoid this error.

But is is still safer to collect the edges to be erased and use the bulk eraser when dealing with an API Entities collection.

  ents = grp.entities
  ents.erase_entities(
    ents.grep(Sketchup::Edge).select { |e| e.faces.size != 2 }
  ) 

In the above example, e.faces.size will return 0 for an empty array, so there will be no error and any edge without a face will be chosen.

1 Like

Thanks Dan,
I hope I’m starting to understand now.
I did a small experiment too: I drew a simple edge and launched a code snippet.

model = Sketchup.active_model
greped = model.entities.grep(Sketchup::Edge)
p "edges: #{greped}"
greped_e = model.entities.grep(Sketchup::Edge) do |e|
  e.erase! 
end
p "greped_e : #{greped_e} edges: #{greped}"

The result:

"edges: [#<Sketchup::Edge:0x000001a7d6735558>]"
"greped_e : [nil] edges: [#<Deleted Entity:0xd6735558>]"

So after erasing the edge, the greped_e is an empty array and the original greped variable has been changed.
The return values show that the entities object itself has been changed and greped variable pointing to the deleted edge (deleted Entity) …

(It’s hard to comprehend this for a mechanical engineer whose last official computer education were on this such stuff… )

NO! You are confusing the issue with a non-understanding of how the #grep block form method works. Read the doc …

If the optional block is supplied, each matching element is passed to it, and the block’s result is stored in the output array.

So the greped_e array will contain the result(s) of calling #erase! on however many edges that result from the filtering. The doc for #erase! is incorrect with regard to the return value. It actually has no meaningful return value and returns nil.

So the greped_e array will have a nil member for each one of the edges that were grepped.

Yes and no. Firstly, you never used the original greped array reference the second time except for in the puts statement. Secondly (as explained above) the members of the greped_e array are purposefully nil references because that it was #erase! returns.

Of course if you have a valid Drawingelement reference in an array, and thereafter erase it, then it’s reference becomes invalid. There is no surprise here.

2 Likes

Okay. The explanation seems pretty straightforward…but I’ll take a :beers: now and will read again an other day… Thanks a lot! :+1:t4:

1 Like