As I showed in the above example, it does not. Ie, it is not that easy. Whether SketchUp changes it’s view or redraws the UI or GUI elements, it does this asynchronously. I showed this in the loop 1 of the ViewObserver
callback where we had to react the view.zoom
changing the view. The same thing will happen with camera.set
.
Actually, it has variable behavior that you might not be aware of. Re, switching to an ISO viewpoint, it depends upon which of the 4 corners you are closer to. This call (to "viewIso:"
) moves to the nearest corner and then changes to a ISo viewpoint. So your image could be viewing the model from a direction you do not expect, all depending upon what the user was doing and how they were viewing the model just before the call.
So Julia’s comment …
… is mostly valid. Also the "send_action"s are basically firing user GUI actions where waiting does not matter. (menu commands, etc.) They were never really expected to be used in synchronous linear code. They were just meant as a quick way to have toolbar buttons do what the user wanted when clicked. (Also, the team members have talked about replacing the entire set of “send actions” with proper method calls upon the correct object, ie, app, view, etc.)
But …
… as I noted above, I don’t think you can get entirely away from using an observer. You’ll never know how long it will take to rerender the view as the model could be small and simple, or large and complex. So it will not always work to just use the global sleep
method.
Firstly, I showed working with the camera in the above example.
In method remember_view()
we use Ruby’s implicit array assignment operation to create an array (referenced as @camera
) holding the 3 objects you’d need to later restore the camera to a known position (whatever it is, and in this case we do not care exactly where it is, just that later we can restore it to whereever it was.)
@camera = cam.eye, cam.target, cam.up
Then in method restore_view()
, I showed using this array (which by the way contains a point3d, point3d, and a vector3d,) and Ruby’s “splat” (*
) operator, to serve up the 3 parameters that the camera.set()
method expects.
(Again see the implicit array assignment section, of the Ruby doc Assignment article. Also, the splat operator works both ways. It can gather up multiple parameters into an array in a method parameter list, or explode an array into a parameter list.)
view.camera.set( *@camera )
This above is the shorthand for doing this to remember:
@eye = cam.eye
@target = cam.target
@up = cam.up
… and this to restore …
view.camera.set( @eye, @target, @up )
Now, for an example. Let us start with the TOP view because it’s easier to visualize.
(1) You want to target the center of what is not hidden.
For the entire model this is easy.
target = model.bounds.center
But, you will be hiding objects, so you want to deal with a bounding box that contains only what you’ve left visible:
bb = Geom::BoundingBox::new
ents = model.entities.each {|e|
next unless e.is_a?(Sketchup::Drawingelement)
# if so will respond to :visible? and :bounds
next if e.is_a?(Sketchup::ConstructionLine)
# avoid infinite clines !
bb.add(e.bounds) if e.visible?
}
target = bb.center
(2) You want to position the camera’s eye directly above the target, so then it’s x and y coords will be identical to the target.
You will want the z coord to be above and likely outside the bounds, so use the entire height of the bounding box.
eye = Geom::Point3d::new( target.x, target.y, bb.height )
Note that in SketchUp Ruby arrays are compatible with both points and vectors, so you could also just transform the target point, by an array representing a transitional vector whose z component is bb.height
…
eye = target.transform([ 0, 0, bb.height ])
The result is the same, … a new point directly above the target, by a distance of bb.height
.
EDIT: Actually the result is not the same. I just noticed an error. The first example puts the point on the top surface of the bounding box. The 2nd example is what I wanted which is half the bb.height
above the bounding box. (We could always just multiply the bb.height
by some factor within the parameter list to be sure the point is high enough.)
(3) The up vector argument is the easiest here, you always want the x axis at the bottom incrementing to the right, and the y axis on the left incrementing towards the top of the screen.
You can use a vector3d …
up = Geom::Vector3d::new( 0, 1, 0 )
… or a simple array (which camera.set
will also accept) …
up = [ 0, 1, 0 ]
(4) You then use the 3 computed arguments …
view.camera.set( eye, target, up )