Hello!
I’m currently trying to set up a camera so that it chooses a random position in spherical coordinates around the origin (within specified ranges) and then zooms the camera into the extents of the object. The models should be loaded in with the object centered on the origin, but I correct for this anyways in the script. Later in the script, I need to use the FOV of the camera, but I’ve been having difficulty consistently getting it. Getting Field of View of a ViewPort inside of RhinoCommon recommended I use the function GetCameraAngle(), which is my current way of doing it, but for some reason the function occasionally fails, and even when it succeeds, the angle returned is inconsistent.
Here’s a snippet of the Python code I’m using (with extra comments and slightly modified indentation due to the removal of irrelevant code):
current_view = sc.doc.Views.ActiveView.ActiveViewport
... # Loading Model Code ...
object = Rhino.RhinoDoc.ActiveDoc.Objects.FindByLayer("CustomLayer")[0]
geom = object.BrepGeometry
bb_object = object.BrepGeometry.GetBoundingBox(True)
rs.MoveObject(object, -bb_object.Center)
# Number of times the call to GetCameraAngle() returned False
fail_iters = 0
# To avoid infinite looping, I have a maximum number of attempts, currently set to 10
while fail_iters < 10:
# Choose random spherical coordinates within range
phi = math.radians(random.randint(30,150))
theta = math.radians(random.randint(0,359))
radius = 40
# Set camera location based on coordinates
camLoc = Rhino.Geometry.Point3d(radius*math.sin(phi)*math.cos(theta), radius*math.sin(phi)*math.sin(theta), radius*math.cos(phi))
# Zoom into object
rs.ViewCameraTarget(camera=camLoc, target=rg.Point3d(0,0,0))
bb_object = object.BrepGeometry.GetBoundingBox(True)
rs.ZoomBoundingBox(bb_object)
# Re-aquire new camera position post-zoom
camLoc, _ = rs.ViewCameraTarget(target=rg.Point3d(0,0,0))
# Half-angle arguments for the GetCameraAngle() method
d_hangle = clr.StrongBox[float]()
v_hangle = clr.StrongBox[float]()
h_hangle = clr.StrongBox[float]()
# If GetCameraAngle() succeeds, then break out of the loop, otherwise continue
if current_view.GetCameraAngle(d_hangle, v_hangle, h_hangle):
break
fail_iters += 1
print("Number of failures: " + str(fail_iters))
print("Horizontal Half-Angle: " + str(h_hangle.Value))
... # Additional working code for hopefully cool stuff ...
And here’s screencaps of the printouts I got for 4 consecutive runs of the code:
I had initially suspected that the random camera position was what lead to the success/failure of the function call, but this clearly isn’t the case. As we can see from the printouts, it either succeeds on the first try or fails the maximum number of times before leaving the loop. I also confirmed that the exact same position leads to different results by plugging in constants instead of random values for the coordinates (I also load up the same model every time, for the record).
Furthermore, even when the call is successful, the horizontal angle returned is often different, and this is true even when I set the spherical coordinates to constant values. For testing, I set the coordinates to phi=math.radians(60)
and theta=math.radians(135)
, and ran it quite a few times (50+). I got horizontal half angles of: 0.2056, 0.2355, 0.2364, 0.4466, 0.7854, 1.050 and 1.523 on successful calls and always 0 on unsuccessful calls. This is also on the same viewport for the record, it is not the result of me switching between different viewports by accident; I checked to make sure. I find it quite interesting that it only seems to switch between these specific values, as I ran it enough times to confirm that these weren’t randomly selected.
If anyone has any idea what’s going on hear, I’d appreciate the insight, as the documentation for this method is missing a description for the method and when/how it returns a failure. Thanks a bundle!
EDIT: Another quick, interesting observation: while most of those angles are largely devoid of semantic meaning as far as I can tell, the angle 0.7854 is 45 degrees.