Untagged deleted element after using outer_shell on group
-
The example code shows that I am checking to see if any of the elements are deleted. The problem is that the valid? and deleted? methods are both telling me that the entity is OK, but then when I try to actually use the entity, it suddenly says that it is deleted.
-
Sorry. I didn't show line 280, which generates the error:
e1 = entity.all_connected.find {|e| e.valid? && !e.deleted? && e.get_attribute("<xxx>", "Type", false) =~ /yyy/}
This line assumes that Ruby, like C, will stop evaluating && expressions as soon as one is false. But in this case, the first two are not false, which I verified with the printf statements I showed above.
The printf's are just for debugging purposes. This is code I have been using for a long time. But it suddenly doesn't work when I use it on a group created with outer_shell. In only that case do I get the situation where valid? and deleted? tell me the entity is OK. But when I check for an attribute on the entity, it suddenly says the entity is deleted. And it is only when I use outer_shell from a Ruby app. If I use the Solid Tool Outer Shell interactively, I do not get the problem.
(I replaced some application specific info with xxx and yyy. Actual values were different, but not relavent.)
-
Speculation..
a) there may still be a bug inmodel.raytest()
(or this is a new one, in addition to the others.)b) somehow the model or group entities collection has not finished purging itself, by the time you call
ent.all_connected()
(which by the way returns an Array copy of a subset of the context'sentities
C-side collection. If the actual connected entities changes on the C-side, in the duration, then the Array objects, and the actual objects in the C-side collection will be "out-of-sync.")Suggest that after doing the
group.outer_shell()
, that you callGC.start
so that (hopefully,) any Ruby-side references are cleaned up, before you begin usingall_connected()
.Also be sure you use a
begin
..rescue
..end
block for your operation:begin model.start_operation("#{group.name} outer shell") # # your code goes here # # you can also use nested rescue blocks in here begin # do something touchy here rescue model.abort_operation() end # model.commit_operation() rescue model.abort_operation() end
-
@spring.freediver said:
e1 = entity.all_connected.find {|e| e.valid? && !e.deleted? && e.get_attribute("<xxx>", "Type", false) =~ /yyy/}
This line assumes that Ruby, like C, will stop evaluating && expressions as soon as one is false. But in this case, the first two are not false, which I verified with the printf statements I showed above.
Don't assume:
e1 = entity.all_connected.find {|e| next unless e.valid? next if e.deleted? e.get_attribute("<xxx>", "Type", false) =~ /yyy/ }
-
The method
e.valid?
is virtually equivalent to!e.deleted?
so there is no need to test for both.The use of multiple
&&
tests can be flaky - try substituting the more forgivingand
tests instead - although by choosing only one '?' method you only need the one && anyway!'Raytesting' is not 100% foolproof. The original groups' have 'vanished' during the 'outer_shell' processing IF the two groups are manifold. Why not use the newly created group's entities - these should of course all be 'valid'
I still don't quite understand what you are trying to achieve by doing these tests after you've run the 'outer_shell'... What objects are you testing to see if they are valid, and why?Also I note your 'attributes' are somewhat unconventional.
The library name should start with a letter, contain no spaces or punctuation, except '' - so ^[A-Za-z][A-Za-z0-9]...
The key name should be similar, and by convention it is in lowercase ^[a-z][a-z0-9_]... - It can start with a '_' but these are usually reserved for DC standard attributes.
I don't suspect them in this issue but it's perhaps useful for the future... -
@tig said:
The use of multiple
&&
tests can be flaky - try substituting the more forgivingand
tests instead - although by choosing only one '?' method you only need the one && anyway!I prefer && over
and
because it has higher precedence. http://www.themomorohoax.com/2008/12/08/and-vs-amperand-in-ruby
Regardless of what one prefer - it isn't "flaky" - as that indicate it doesn't always work. It does what is is supposed to do.http://avdi.org/devblog/2010/08/02/using-and-and-or-in-ruby/
http://blog.jayfields.com/2007/08/ruby-operator-precedence-of-and-which.html -
Thanks for all the feedback guys.
TIG: the xxx and yyy are not the actual values. Since they aren't relevent, I replaced the longer names used in the actual code. As for what the application is doing, it is simply allowing the user to intersect a ray with one of the faces of a newly created outer_shell group, and then finding all the connected elements of that face or edge. I used raytest so I can get all the transformations for a nested entity from the item[1] array. As I mentioned, this is old code I have been using for over a year. It just started displaying this problem when I used it on the result of outer_shell. The code that I showed was some hastily added debuging code. I suspected that valid? and deleted? were redundant, but was just trying to find out what was happening.
Dan: Based on your suggestion, I read the docs on GC.start. When should I use it? Immediately after doing the group.outer_shell? I was not aware that entity lists could be changing in the background while a Ruby script was running. I assumed that when I called outer_shell, Sketchup would not return control to my script until it had done all of it's cleanup of entity lists. GC.start would not affect what they have going on the C side, would it?
-
@tig said:
Why are you doing this?
If a deleted edge is causing an issue use
cnn = entity.all_connected cnn.each_index{|i|cnn.delete(cnn[i])if not cnn[i].valid?}
???also:
cnn.delete_if {|e| e.deleted? }
...which is an inline method even though it does not use
!
in the name (ie, it really should be nameddelete_if!
) -
@spring.freediver said:
Dan: Based on your suggestion, I read the docs on
GC.start
. When should I use it? Immediately after doing thegroup.outer_shell
?YES ... and BEFORE getting a new Ruby-side reference to any C-side collection object.
@spring.freediver said:
I was not aware that entity lists could be changing in the background while a Ruby script was running.
Oh yea.. this forum is peppered with posts where coders get strange results when iterating Sketchup API collections... followed by the normal advice from the experienced, that they should always make an
Array
copy of the collection, and iterate thatArray
(.. this is a must if you will be changing the collection during the loop.)@spring.freediver said:
I assumed that when I called
outer_shell
, Sketchup would not return control to my script until it had done all of it's cleanup of entity lists.Yea.. well you can't assume that. Ruby garbage collection in the 1.8 trunk.. sucks. It's also a periodic task... and it may wish to wait till some other time to "sweep" up the Ruby garbage, but you need to tell
GC
to sweep things up now.@spring.freediver said:
GC.start
would not affect what they have going on the C side, would it?GC
is a Ruby class, and it cleans up Ruby.. which actually IS compiled C. The Sketchup engine itself is C++, which has a better garbage collector than Ruby.P.S.: Whenever I say "C-side", I am really referring generically to the Sketchup C++ engine, it's C-wrappers for the Ruby API, and Ruby's C core engine.
-
BTW.. there might be some API call that also has the effect of triggering garbage collection.
Try
view.refresh()
Hmm.. it also occurs to me, that Sketchup may be wanting to keep references to deleted entities around, just in case the user decides to do an undo.
But a call toall_connected()
should not return these entities in the resultArray
. If it does, then it's theall_connected()
method that is bugged.You said that you were wrapping your code in your own operation... if you didn't (ie you just let Sketchup handle the undo,) I wonder if the error would also occur? Meaning it could be an operation bug.
Advertisement