Pausing for Sketchup to update
-
I am trying to use Ruby to automate a tour through my model but I have hit a wall trying to get my script to wait for the view to update. I want the script to step through the model and find each entity that matches a particular criteria, perform some action and only then go to the next matching entity and repeat. Every structure I try, like 'array.each do|x|' or 'while' or 'if n<number_of_entities', steps through the whole model but perform the desired action only on the last matching entity.
Searching this forum, I have found several posts on similar issues but none of the given solutions work for me and I just cannot figure out why. One solution is 'view.invalidate'. As far as I can see, this has absolutely no visible effect on the model or view or script execution. The other suggestion is 'UI.start_timer'. Yes, execution pauses for the set interval but then acts on the last match as above. The only thing that works - sort of - is 'UI.messagebox' This allows the action to be performed on each chosen entity in turn but of course the box pops up with that irritating noise and requires user intervention to cancel so the script can continue. I have searched in vain for a work around, like repositioning the box off screen and setting a timeout to automatically close the box (plus silencing the beep). Sadly, no such options appear to exist. (It seems Visual Basic and the Windows API can do it but that's way beyond me.)
Can anyone explain the correct use of view.invalidate and/or start_timer? Or direct me to some existing tutorial on these? The usual newbie disclaimer and apologies apply... Thank you.
Mike
-
Hi Mike, to undesratnd better, you say you are trying to make tour through your model. Do you want ruby to move the camera through the model from object to object, and so it will be a regular looking animation? Is that the goal?
If so, you will need to make a camera and animate it, using the object locations to guide the location of the camera. I'm not sure exactly what you do to do that, but I think it is the animation class, and you just animate the camera.
Is that sort of the right idea?
Chris
-
Hi Chris, thanks for the reply and yes, I have got the camera to move without using the animation class or pages. So the step I can't solve is how to avoid the need for user intervention.
I began with some code I found on the Google blog here: http://sketchupapi.blogspot.com/2008/11/storing-and-retrieving-with.html. This creates a simple group with attributes attached. The group is placed at the current view and takes the current camera parameters as its attributes. A context menu item allows you to go to that view with a few clicks.
I use the reveal_view method to first set up the views I want but the go_to_view method is not convenient for viewing in a particular sequence - you may not be able to see the next location to click on it. An advantage of my idea is you can even hide the cameras or turn off the layer they are on so the model is not cluttered with camera groups.
I also made a modified version of reveal_view so that I don't have to go to each location to create the camera for that view. With this version, a context menu becomes available when an edge is selected and its start and end point become the eye and target of the camera.
Since these 'cameras' are all placed on their own layer, I can use
layer.name=="cameras"
to store them in an array. It should then be a simple matter of extracting the attributes from each in turn (withinfo=cam.attribute_dictionary("is_a_camera")
) and sending the view to that location (withview.camera=Sketchup::Camera.new(info["eye"]
...etc) but Ruby doesn't have the good grace to wait unless I use messageboxes, which is a bit self defeating.Anyway, here is my code:
def do_tour ents=Sketchup.active_model.entities cams=[] e=0 num=ents.length while e < num if ents[e].layer.name=="cameras" cams.push(ents[e]) end e+=1 end cam_no=0 cam_indx=cams.length # while cam_no<cam_indx cams.each do |cam| info=cam.attribute_dictionary("is_a_camera") m=Sketchup.active_model view=m.active_view UI.start_timer(3, false) {puts cam_no, cam} ### For some reason this timer now does not pause, but it did previously. The view moves ### immediately to the last camera position and halts. It does verify in the console that ### the script is stepping through the camera group list. Only the last camera number is ### displayed (cam_indx times). If the flag is set to true instead of false, you can see ### in the console it is looping endlessly but the view remains the same. view.camera=Sketchup;;Camera.new(info["eye"],info["target"],info["up"],info["persp"],info["fov"]), 3 # UI.messagebox("camera no.; " + cam_no.to_s) ### uncomment this line to see how the script ### (almost) should work. view.invalidate ### I read somewhere that if this is placed after the messagebox call, the ### box won't appear. But it does. cam_no += 1 ### I also tried the following snippet but it merely behaves like start_timer # time_mark = ( Time.now + 3 ) # while time_mark > Time.now # end end end
This would be activated by a Plugin menu item.
I've attached a Sketchup file with some cameras already created. It is not pretty but should save time for you.
test_anim.skp
If the problem is a simple bug or wrong syntax, it eludes me. My conceptual understanding of Ruby is weak (hence my wholesale copying of Google's code). But if I can get past this, I can think about different transition times, changing attributes (to pan maybe), and maybe allowing a user to pause whenever they want.Mike
-
Mike,
Check the SU2KT exporter. There is a class called 'AnimCamera' (now I realise I should have given it more unique name).
class AnimCamera def initialize @frame=0 @slide_time=0.0 end def nextFrame(view) @slide_time=@frame/@frame_per_sec model=Sketchup.active_model model.pages.show_frame_at @slide_time @frame+=1 end def stop Sketchup;;set_status_text("", 1) Sketchup;;set_status_text("", 2) end end # class AnimCamera
This way you can start an animation:
Sketchup.active_model.active_view.animation = AnimCamera.new
Check Ruby API for more info.Tomasz
-
I have used a shuttle method in javascript that might be useful for you. I will try and explain it in javascript but I am sure it is easy to transpose into ruby. The idea is to shuttle from one function to another, but here I use a controller
function
.There is a global variable -
pass
.The controller
function
has this statementeval("fun"+pass)()
in a timer likesetInterval
.Each
fun+pass function
has two parts - conditional stop and go (or wait in your case I guess).If the condition is satisfied, stop timer,
pass++
and call controller .... and on and on.I hope this is helpful.
Chris
-
...so the answer is that I am barking up the wrong program tree? Damn! So near, yet so far.
-
@designforlife said:
...so the answer is that I am barking up the wrong program tree? Damn! So near, yet so far.
Not necessarily, but web dialogs are very good if you want flexibility and interoperability including human interaction. Why not give them a try?
Chris
-
@chrisglasier said:
@designforlife said:
...so the answer is that I am barking up the wrong program tree? Damn! So near, yet so far.
Not necessarily, but web dialogs are very good if you want flexibility and interoperability including human interaction. Why not give them a try?
Chris
Could you explain a bit about web dialogs? How do they relate? I tried a few of the methods from the API page to display a web page in Sketchup. Beyond that, I am trying to imagine the potential.
Actually, the name initially suggested to me they might work the other way round - to display sketchup in a web page but I believe that requires the web user to download and install a browser add-on.
Mike
-
Hi Mike, sorry if I'm being dense, but to make sure I'm understanding....
The only thing you want is to make it so the user can pause/cancel the animation while the animation is in process? Is that the only thing that you are asking about?
Chris
-
@designforlife said:
Beyond that, I am trying to imagine the potential.
Well you could have a look at this plugin and, as you have a PC, download it. The ruby file shows you how to open a web dialog window and sit an html file in it. You can also find how to pass strings back and forth between JavaScript and Ruby.
Let me know if you find this helpful but need more info.
Chris
@chris fullmer said:
The only thing you want is to make it so the user can pause/cancel the animation while the animation is in process? Is that the only thing that you are asking about?
Just saw your note Chris ... what I understood was that Mike wants to stop the script outrunning the animation, so I suggested using two functions, one substituting his messagebox suggestion.
cg
-
@designforlife said:
Can anyone explain the correct use of view.invalidate and/or start_timer?
You may try to substitute "while...end" or "for...end" etc control structures by timer. The funny thing is that maybe it is not necessary to put 'Sketchup.active_model.active_view.invalidate' line inside the timer.
` wait_time=(<suspend period>)
times=(how many times you want to repeat <some code>)
timer_id=UI.start_timer(wait_time,true) {
<some code> # put some geometry to active_model
times -=1 # decrement times to be able to stop timer
Sketchup.active_model.active_view.invalidatedo not forget to stop timer
timer_id=UI.stop_timer timer_id if times==0
}`I noticed, that visual results of <some code> processing appears even without view.invalidate. Note, that <some code> appears to work a kind of 'transparent' way (you may zoom, pan, rotate view and even launch other commands during the timer processing).Such behaviour indicates, that during <suspend period> all possible events (mouse events, key stroke events and so on) rise and you can handle them.
Hope that helps.
Advertisement