git.lucas.co / hou-control
git clone https://git.lucas.co/hou-control.git

commit0a5ce15550264048fb1759943a7bac875aa305e8
parent90fe0e40b2
authorLucas Galante <[email protected]>
date2026-04-17 09:59
hc: implement precise framing logic for Perspective and Ortho projections

 python3.11libs/hc/hccam.py | 44 ++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 40 insertions(+), 4 deletions(-)

diff --git a/python3.11libs/hc/hccam.py b/python3.11libs/hc/hccam.py
index 899778a..1fcc8ef 100644
--- a/python3.11libs/hc/hccam.py
+++ b/python3.11libs/hc/hccam.py
@@ -42,12 +42,48 @@ class HCCam:
         self.p = hou.Vector3(centroid)
 
     def frame(self):
+        import math
         geo = self.geo()
-        centroid = geo.boundingBox().center() if geo else hou.Vector3(0, 0, 0)
-        self.t = hou.Vector3(centroid)
+        if not geo:
+            return
+
+        bbox = geo.boundingBox()
+        centroid = bbox.center()
+        size = bbox.sizevec()
+        
+        # Set pivot and basic target position to centroid
         self.p = hou.Vector3(centroid)
-        # self.ow = 10
-        self.setZoom(10)
+        self.t = hou.Vector3(centroid)
+        
+        margin = 1.2
+        
+        if self.projection == 'perspective':
+            # Calculate FOV from camera parameters
+            # Horizontal FOV: 2 * atan(aperture / (2 * focal))
+            # Vertical FOV: 2 * atan((aperture/aspect) / (2 * focal))
+            aperture = self.cam_node.parm('aperture').eval()
+            focal = self.cam_node.parm('focal').eval()
+            aspect = self.aspect_ratio
+            
+            fov_h = 2.0 * math.atan(aperture / (2.0 * focal))
+            fov_v = 2.0 * math.atan((aperture / aspect) / (2.0 * focal))
+            
+            # Use the bounding sphere radius for a safe framing from any angle
+            radius = size.length() * 0.5
+            
+            # Required distance to fit the sphere in both H and V FOV
+            dist_h = (radius * margin) / math.sin(fov_h * 0.5)
+            dist_v = (radius * margin) / math.sin(fov_v * 0.5)
+            
+            dist = max(dist_h, dist_v)
+            self.setZoom(dist)
+            
+        else:
+            # Orthographic framing
+            # We need to fit the bounding box into the orthowidth
+            # ow = max(width, height * aspect)
+            width = max(size[0], size[1], size[2])
+            self.ow = width * margin * 1.5 # Extra buffer for ortho
 
     def home(self):
         geo = self.geo()