Fixing the Undo-stack (and others!)
-
Good morning everyone,
I'm currently writing my first Ruby for Sketchup, a fairly simple plugin that goes through a model assigning colours to faces for the purpose of separating the faces in Photoshop. The end goal is to have a companion PS script to do the separating; this isn't dissimilar to the 'Render ID' function in VRay (https://www.google.co.uk/search?q=vray+render+id).
Right now it's working fairly well, recursively colouring faces in nested groups and giving adjacent softened faces the same colour. However, there are a few problems:
- The Undo-stack is picking up every method call, leaving a bit of a mess.. I've read ThomThom's article on it but I still don't entirely understand model.start_operation, I've also tried playing with the arguments but to little effect. Any assistance would be most welcome!
- ComponentDefinition classes aren't getting picked up by the main method (which currently treats them in the same way it treats the Group classes)
I'd also appreciate any tips on style and refactoring, as I'm very much a ruby noobie. Cheers!
-CJ
-
I'd use
{ self.colourfaces() }
and set@model
etc in the called method thus:def self.colourfaces(entities=[]) @colour_selection = Sketchup;;Color.names @colour_number = @colour_selection.length @colour_current = @colour_selection[rand(@colour_number)] @model = Sketchup.active_model ### @model.start_operation("Colour faces", true) ### NOTE ### @entities = Sketchup.active_model.entities entities = @entities unless entities[0] entities.each do |entity| ... ### set ents to the container's entities THEN ### self.colourfaces(ents) ... end ### @model.commit_operation ### end # self.colourfaces
You also need to spot Groups and Instances separately
... elsif entity.is_a?(Sketchup;;Group) ents = entity.entities self.colourfaces(ents) elsif entity.is_a?(Sketchup;;ComponentInstance) ### NOTE ents = entity.definition.entities ### NOTE self.colourfaces(ents) end
-
Fantastic, thanks TIG
-
Hi again,
I've managed to fix the undo-stack, as well as implement a couple of other features (same colour across groups/components, reset all materials to default) but the Component section is still not working as intended on some models.
On simpler models it seems to work fine, however on larger models (like the one attached) it seems to get stuck. Any suggestions would be most appreciated! I've noticed when I take out the is_a?(Sketchup::ComponentInstance) section the rest of the script works fine and as intended.
Also, once this is sorted a better UI is the next on the list
-CJ
-
When you process a
ComponentInstance
itsdefinition.entities
is altered, you only need to do that once...
There might be several instances...
So set up an initial array before the iterated tests:@defns=[]
So when it's an instance the first thing you do it test if it's already been done...
This is cod-code - adjust it to your needs...
` next if @defns.include?(instance.definition)
@defns << instance.definitionprocess definition.entities`
That way each definition is only processed once...
-
Thanks for highlighting this TIG, unfortunately this didn't fix the issue. The script hangs and doesn't update the faces until the next user action, then only updates one component definition. Does this yield any clues?
I'm going to sleep on it
-CJ
-
Any errors in the Ruby Console ?
-
Hi TIG, I realised that I hadn't run it through the console straight after I finished yesterday. I've now run it through the console & found there's a problem in the self.softcheck method that gives a Error: #<NoMethodError: undefined method `material' for nil:NilClass> when run in some models.
I changed the condition from if @face_array[0] && @face_array[0].material == nil and got a Error: #<SystemStackError: stack level too deep>.. Ouch. I believe this is because of excessive recursion, the model comes back with smoothed surfaces half finished so I don't think there's an infinite loop in there. I presume this means I need to rewrite the code algorithmically?
def self.softcheck(face) face.edges.each do |edge| if edge.soft? @face_array = edge.faces @face_array.delete(face) if @face_array[0] && @face_array[0].material == nil @face_array[0].material = @colour_current self.softcheck(@face_array[0]) end else next end end end
I'm guessing I'm not the first person to try and 'select' all faces in a softened surface, so if there's a good existing definition I could have a look at I'd be most obliged. Cheers
-CJ
-
As I recall thomthom wrote an example code method, to select all faces in a smoothed surface...
Here http://sketchucation.com/forums/viewtopic.php?p=365381#p365381 and others like Jim http://sketchucation.com/forums/viewtopic.php?p=453845#p453845 -
It works
I had a good look at ThomThom's code and saw how he did it, very neat - going to have a good think about how to optimise it now as it should only iterate each surface once; if the softcheck returns an array faces in a surface I presume I could subtract this from the array of entities being coloured. Time to experiment!
In the meantime, here are the results:
-CJ
-
And of course thank you hugely for your time and expertise TIG, I've thoroughly enjoyed my first foray into Ruby for Sketchup. Thanks again
-CJ
-
@joclo said:
I changed the condition from if @face_array[0] && @face_array[0].material == nil and got a Error: #<SystemStackError: stack level too deep>.. Ouch. I believe this is because of excessive recursion, the model comes back with smoothed surfaces half finished so I don't think there's an infinite loop in there. I presume this means I need to rewrite the code algorithmically?
Recursive methods to traverse geometry is likely to cause stack overflow as you'll be recursing thousands of times. Geometry walkers need to "unroll" their loop to avoid this.
-
Got it, thanks Thom. I realised that I could have optimised it to recurse fewer times, but at some point with increasingly complex surfaces it will run into a stack overflow again. I had a little read elsewhere and found that there are workarounds (ulimit and altering the interpreter, sounds like a bit of a dodgy hack to me..), but eventually it's always got an upper limit.
I very much like the idea of a Geometry walker traversing geometry, and ended up using a solution very similar to yours. Cheers!
-CJ
Advertisement