Why this error and how to correct it?

You are great. Thank you so much.

1 Like

I write following codes but problem happen again.

            grp.entities.grep(Sketchup::Edge) do |e|
              e.erase! if e.faces.size  == 1
            end
            grp.entities.grep(Sketchup::Edge) do |e|
              e.erase! if e.faces.size > 2
            end
            grp.entities.grep(Sketchup::Edge) do |e|
              e.erase! if e.faces.size == 0
            end
            grp.entities.grep(Sketchup::Edge) do |e|
              if e.faces.first != nil && e.faces.last != nil
#              if e.faces.first.valid? && e.faces.last.valid?
                e.erase! if e.faces.first.normal == e.faces.last.normal
              else
                t2 = 1
              end
            end

t2 = 1 at the end.

This means at least once there were no faces in the iteration.

Did you get the same error message? Or what do you mean by " problem happen again."?

My problem solved by using your last code. I just confuse why this problem happen. I deleted edges that have one face, then edges that have more than 2 faces then edges that have no faces so in remain edges both faces should be valid but it is not!!! again after delete edges with more than 2 edges I deleted faces with one edges but problem is same as before.

Are you sure if t2 were set to “1” by your last “trial” not the previous one?
What do you see in console if you change:
t2 = 1
to
puts " no faces"

Yes I am sure. Shape is complex I can send you shape if you are interest. My Code…

            grp.entities.grep(Sketchup::Edge) do |e|
              e.erase! if e.faces.size > 2
            end
            grp.entities.grep(Sketchup::Edge) do |e|
              e.erase! if e.faces.size == 1
            end
            grp.entities.grep(Sketchup::Edge) do |e|
              e.erase! if e.faces.size == 0
            end
            grp.entities.grep(Sketchup::Edge) do |e|
              if e.faces.first && e.faces.last
                e.erase! if e.faces.first.normal == e.faces.last.normal
              else
                t2 = 1
              end
            end
            grp.entities.grep(Sketchup::Edge) do |e|
              if e.faces.first && e.faces.last
                e.erase! if e.faces.first.normal == e.faces.last.normal.reverse
              else
                t2 = 1  
              end  
            end         

I use t2 to terminate extension forgive me not to change it.

Yes, I may be interested … but I don’t know exactly if I will have time for it today or tomorrow …to examine.
Just drop the group in question saved as separated .skp here in a forum (if not super secret :wink: )

Nothing secret for you. Let me send you PM and short video.


if I don’t delete faces shape will be like following picture.

Also SKP file.Test Villa2.skp (10.7 MB)

You told me above that “problem is same as before.” Which is an error message.

But I see now, you have an other problem. You do not have error message, but deleted geometry. It is not the same. Absolutely different one.

1 Like

Sorry for misunderstanding. As I told you by using your last code my problem solved (Error message) but I just confuse why this problem happen (deleted geometry). It is not my problem now and I really don’t want waste your time. Thanks again Dezmo.

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