1#!/pxrpythonsubst 2# 3# Copyright 2016 Pixar 4# 5# Licensed under the Apache License, Version 2.0 (the "Apache License") 6# with the following modification; you may not use this file except in 7# compliance with the Apache License and the following modification to it: 8# Section 6. Trademarks. is deleted and replaced with: 9# 10# 6. Trademarks. This License does not grant permission to use the trade 11# names, trademarks, service marks, or product names of the Licensor 12# and its affiliates, except as required to comply with Section 4(c) of 13# the License and to reproduce the content of the NOTICE file. 14# 15# You may obtain a copy of the Apache License at 16# 17# http://www.apache.org/licenses/LICENSE-2.0 18# 19# Unless required by applicable law or agreed to in writing, software 20# distributed under the Apache License with the above modification is 21# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 22# KIND, either express or implied. See the Apache License for the specific 23# language governing permissions and limitations under the Apache License. 24# 25from __future__ import division 26 27import sys 28import unittest 29import math 30from pxr import Gf 31 32class TestGfFrustum(unittest.TestCase): 33 34 def test_Constructors(self): 35 self.assertIsInstance(Gf.Frustum(), Gf.Frustum) 36 self.assertIsInstance(Gf.Frustum(Gf.Frustum()), Gf.Frustum) 37 38 # code coverage wonderfulness. 39 f = Gf.Frustum() 40 # force instantiation of the frustum planes 41 f.Intersects(Gf.Vec3d()) 42 f2 = Gf.Frustum(f) 43 44 def test_Operators(self): 45 f1 = Gf.Frustum() 46 f2 = Gf.Frustum(f1) 47 self.assertEqual(f1, f2) 48 49 def test_PlaneIntersection(self): 50 f1 = Gf.Frustum() 51 f2 = Gf.Frustum() 52 # force plane instantiation. 53 f1.Intersects(Gf.Vec3d()) 54 f2.Intersects(Gf.Vec3d()) 55 self.assertEqual(f1, f2) 56 57 def test_Position(self): 58 f1 = Gf.Frustum() 59 f2 = Gf.Frustum() 60 f1.position = Gf.Vec3d(1, 0, 0) 61 f2.position = Gf.Vec3d(0, 1, 0) 62 self.assertNotEqual(f1, f2) 63 64 def test_Properties(self): 65 f = Gf.Frustum() 66 f.position = Gf.Vec3d(1, 2, 3) 67 self.assertEqual(f.position, Gf.Vec3d(1, 2, 3)) 68 69 f.rotation = Gf.Rotation(Gf.Vec3d(1, 1, 1), 30) 70 self.assertEqual(f.rotation, Gf.Rotation(Gf.Vec3d(1, 1, 1), 30)) 71 72 f.window = Gf.Range2d( Gf.Vec2d(0, 1), Gf.Vec2d(2, 3)) 73 self.assertEqual(f.window, Gf.Range2d( Gf.Vec2d(0, 1), Gf.Vec2d(2, 3))) 74 75 f.nearFar = Gf.Range1d(1, 2) 76 self.assertEqual(f.nearFar, Gf.Range1d(1, 2)) 77 78 f.viewDistance = 10 79 self.assertEqual(f.viewDistance, 10) 80 81 f.projectionType = Gf.Frustum.Orthographic 82 self.assertEqual(f.projectionType, Gf.Frustum.Orthographic) 83 f.projectionType = Gf.Frustum.Perspective 84 self.assertEqual(f.projectionType, Gf.Frustum.Perspective) 85 86 def test_Projection(self): 87 f = Gf.Frustum() 88 f.SetPerspective( 10, True, 20, 30, 40 ) 89 self.assertEqual(f.GetPerspective(True), (10, 20, 30, 40)) 90 self.assertEqual(f.GetFOV(True), 10) 91 f.SetPerspective( 10, False, 20, 30, 40 ) 92 self.assertEqual(f.GetPerspective(False), (10, 20, 30, 40)) 93 self.assertEqual(f.GetFOV(False), 10) 94 self.assertEqual(f.GetFOV(), 10) 95 f = Gf.Frustum() 96 f.projectionType = f.Orthographic 97 self.assertIsNone(f.GetPerspective(True)) 98 self.assertEqual(f.GetFOV(True), 0.0) 99 self.assertEqual(f.GetFOV(False), 0.0) 100 101 self.assertEqual(Gf.Frustum.GetReferencePlaneDepth(), 1.0) 102 103 f.SetOrthographic( 10, 20, 30, 40, 50, 60 ) 104 self.assertEqual(f.GetOrthographic(), (10, 20, 30, 40, 50, 60)) 105 self.assertEqual(f.GetFOV(), 0.0) 106 f = Gf.Frustum() 107 f.projectionType = f.Perspective 108 self.assertEqual(len(f.GetOrthographic()), 0) 109 110 def test_Intersection(self): 111 f = Gf.Frustum() 112 f.projectionType = f.Orthographic 113 f.FitToSphere( Gf.Vec3d(0, 0, 0), 1 ) 114 self.assertTrue(f.Intersects( Gf.Vec3d(0, 0, 0) )) 115 self.assertTrue(f.Intersects( Gf.Vec3d(0.9, 0, 0) )) 116 self.assertTrue(f.Intersects( Gf.Vec3d(0, 0.9, 0) )) 117 self.assertTrue(f.Intersects( Gf.Vec3d(0, 0, 0.9) )) 118 self.assertTrue(f.Intersects( Gf.Vec3d(-0.9, 0, 0) )) 119 self.assertTrue(f.Intersects( Gf.Vec3d(0, -0.9, 0) )) 120 self.assertTrue(f.Intersects( Gf.Vec3d(0, 0, -0.9) )) 121 f = Gf.Frustum() 122 f.projectionType = f.Perspective 123 f.FitToSphere( Gf.Vec3d(1, 1, 1), 1, 0.2 ) 124 self.assertTrue(f.Intersects( Gf.Vec3d(1, 1, 1) )) 125 self.assertTrue(f.Intersects( Gf.Vec3d(2.1, 1, 1) )) 126 self.assertTrue(f.Intersects( Gf.Vec3d(1, 2.1, 1) )) 127 self.assertTrue(f.Intersects( Gf.Vec3d(1, 1, 2.1) )) 128 self.assertTrue(f.Intersects( Gf.Vec3d(-0.1, 1, 1) )) 129 self.assertTrue(f.Intersects( Gf.Vec3d(1, -0.1, 1) )) 130 self.assertTrue(f.Intersects( Gf.Vec3d(1, 1, -0.1) )) 131 132 def test_CodeCoverage(self): 133 f = Gf.Frustum() 134 f.projectionType = f.Perspective 135 f.window = Gf.Range2d(Gf.Vec2d(10,20), Gf.Vec2d(30,40)) 136 f.FitToSphere(Gf.Vec3d(), 1) 137 f.window = Gf.Range2d(Gf.Vec2d(30,40), Gf.Vec2d(10,20)) 138 f.FitToSphere(Gf.Vec3d(), 1) 139 f.window = Gf.Range2d(Gf.Vec2d(), Gf.Vec2d()) 140 f.FitToSphere(Gf.Vec3d(), 1) 141 f = Gf.Frustum() 142 f.projectionType = f.Perspective 143 f.window = Gf.Range2d(Gf.Vec2d(-30,-40), Gf.Vec2d(10,20)) 144 f.FitToSphere(Gf.Vec3d(), 1) 145 146 def test_Transform(self): 147 f = Gf.Frustum() 148 f.Transform( Gf.Matrix4d(2) ) 149 self.assertEqual(f.nearFar, 2 * Gf.Frustum().nearFar) 150 f.Transform( Gf.Matrix4d(-2) ) 151 f.window = Gf.Range2d(Gf.Vec2d(1,1), Gf.Vec2d(-1,-1)) 152 f.Transform(Gf.Matrix4d(1)) 153 154 def test_SetRotate(self): 155 f = Gf.Frustum() 156 self.assertEqual(f.ComputeViewDirection(), Gf.Vec3d(0,0,-1)) 157 m = Gf.Matrix4d().SetRotate(Gf.Rotation(Gf.Vec3d(1, 1, 1), 30)) 158 self.assertTrue(Gf.IsClose(Gf.Frustum().Transform(m).ComputeViewDirection(), \ 159 Gf.Vec3d(-0.333333, 0.244017, -0.910684), 0.0001)) 160 161 f = Gf.Frustum() 162 self.assertEqual(f.ComputeUpVector(), Gf.Vec3d(0,1,0)) 163 m = Gf.Matrix4d().SetRotate(Gf.Rotation(Gf.Vec3d(1, 1, 1), 30)) 164 self.assertTrue(Gf.IsClose(Gf.Frustum().Transform(m).ComputeUpVector(), \ 165 Gf.Vec3d(-0.244017, 0.910684, 0.333333), 0.0001)) 166 167 def test_ComputeViewFrame(self): 168 f = Gf.Frustum() 169 (side, up, view) = f.ComputeViewFrame() 170 self.assertEqual(side, Gf.Vec3d(1, 0, 0)) 171 self.assertEqual(up, Gf.Vec3d(0, 1, 0)) 172 self.assertEqual(view, Gf.Vec3d(0, 0, -1)) 173 174 def test_ComputeLookAtPoint(self): 175 f = Gf.Frustum() 176 self.assertEqual(f.ComputeLookAtPoint(), Gf.Vec3d(0, 0, -5)) 177 f.projectionType = f.Orthographic 178 self.assertEqual(f.ComputeLookAtPoint(), Gf.Vec3d(0, 0, -5)) 179 180 def test_ComputeViewInverse(self): 181 f = Gf.Frustum() 182 f.Transform(Gf.Matrix4d().SetRotate(Gf.Rotation(Gf.Vec3d(1,1,1),60))) 183 r1 = f.ComputeViewMatrix() * Gf.Vec4d(1, 2, 3, 4) 184 r2 = Gf.Matrix4d().SetRotate(Gf.Rotation(Gf.Vec3d(1,1,1),60)).GetInverse() * Gf.Vec4d(1, 2, 3, 4) 185 self.assertTrue(Gf.IsClose(r1, r2, 0.0001)) 186 r1 = f.ComputeViewInverse() * Gf.Vec4d(1, 2, 3, 4) 187 r2 = Gf.Matrix4d().SetRotate(Gf.Rotation(Gf.Vec3d(1,1,1),60)) * Gf.Vec4d(1, 2, 3, 4) 188 self.assertTrue(Gf.IsClose(r1, r2, 0.0001)) 189 190 def test_ComputeProjectionMatrix(self): 191 # FIXME how to test ComputeProjectionMatrix? 192 f = Gf.Frustum() 193 f.projectionType = f.Orthographic 194 f.ComputeProjectionMatrix() 195 f.projectionType = f.Perspective 196 f.ComputeProjectionMatrix() 197 198 def test_ComputeAspectRatio(self): 199 f = Gf.Frustum().Transform(Gf.Matrix4d(Gf.Vec4d(3,2,1,1))) 200 self.assertEqual(f.ComputeAspectRatio(), 1.5) 201 corners = f.ComputeCorners() 202 self.assertEqual(corners[0], Gf.Vec3d(-3, -2, -1)) 203 self.assertEqual(corners[1], Gf.Vec3d(3, -2, -1)) 204 self.assertEqual(corners[2], Gf.Vec3d(-3, 2, -1)) 205 self.assertEqual(corners[3], Gf.Vec3d(3, 2, -1)) 206 self.assertEqual(corners[4], Gf.Vec3d(-30, -20, -10)) 207 self.assertEqual(corners[5], Gf.Vec3d(30, -20, -10)) 208 self.assertEqual(corners[6], Gf.Vec3d(-30, 20, -10)) 209 self.assertEqual(corners[7], Gf.Vec3d(30, 20, -10)) 210 211 def test_ComputeCorners(self): 212 f = Gf.Frustum() 213 f.projectionType = f.Orthographic 214 f.Transform(Gf.Matrix4d(Gf.Vec4d(3,2,1,1))) 215 corners = f.ComputeCorners() 216 self.assertEqual(corners[0], Gf.Vec3d(-3, -2, -1)) 217 self.assertEqual(corners[1], Gf.Vec3d(3, -2, -1)) 218 self.assertEqual(corners[2], Gf.Vec3d(-3, 2, -1)) 219 self.assertEqual(corners[3], Gf.Vec3d(3, 2, -1)) 220 self.assertEqual(corners[4], Gf.Vec3d(-3, -2, -10)) 221 self.assertEqual(corners[5], Gf.Vec3d(3, -2, -10)) 222 self.assertEqual(corners[6], Gf.Vec3d(-3, 2, -10)) 223 self.assertEqual(corners[7], Gf.Vec3d(3, 2, -10)) 224 225 def test_ComputeNarrowedFrustum(self): 226 f = Gf.Frustum() 227 f.projectionType = f.Orthographic 228 f.Transform(Gf.Matrix4d(Gf.Vec4d(3,2,1,1))) 229 narrowF = f.ComputeNarrowedFrustum(Gf.Vec2d(0, 0), Gf.Vec2d(0.1, 0.1)) 230 self.assertTrue(Gf.IsClose(narrowF.window.min, Gf.Vec2d(-0.3, -0.2), 0.0001)) 231 self.assertTrue(Gf.IsClose(narrowF.window.max, Gf.Vec2d(0.3, 0.2), 0.0001)) 232 233 narrowF = f.ComputeNarrowedFrustum(Gf.Vec3d(0, 0, -1), Gf.Vec2d(0.1, 0.1)) 234 self.assertTrue(Gf.IsClose(narrowF.window.min, Gf.Vec2d(-0.3, -0.2), 0.0001)) 235 self.assertTrue(Gf.IsClose(narrowF.window.max, Gf.Vec2d(0.3, 0.2), 0.0001)) 236 237 # Given a point behind the eye should get the same frustum back 238 narrowF = f.ComputeNarrowedFrustum(Gf.Vec3d(0, 0, 1), Gf.Vec2d(0.1, 0.1)) 239 self.assertTrue(Gf.IsClose(narrowF.window.min, Gf.Vec2d(-3.0,-2.0), 0.0001)) 240 self.assertTrue(Gf.IsClose(narrowF.window.max, Gf.Vec2d(3.0,2.0), 0.0001)) 241 242 def test_ComputePickRay(self): 243 f = Gf.Frustum() 244 f.window = Gf.Range2d(Gf.Vec2d(3,3),Gf.Vec2d(4,4)) 245 f.ComputeNarrowedFrustum(Gf.Vec2d(3,3), Gf.Vec2d(100,100)) 246 247 r = Gf.Frustum().ComputePickRay(Gf.Vec2d(2, 2)) 248 self.assertTrue(Gf.IsClose( r.startPoint, Gf.Vec3d(2./3, 2./3, -1./3), 0.00001 )) 249 self.assertTrue(Gf.IsClose( r.direction, Gf.Vec3d(2./3, 2./3, -1./3), 0.00001 )) 250 251 f = Gf.Frustum() 252 f.projectionType = f.Orthographic 253 r = f.ComputePickRay(Gf.Vec2d(2, 2)) 254 self.assertTrue(Gf.IsClose( r.startPoint, Gf.Vec3d(2, 2, -1), 0.00001 )) 255 self.assertTrue(Gf.IsClose( r.direction, Gf.Vec3d(0, 0, -1), 0.00001 )) 256 257 r = Gf.Frustum().ComputePickRay(Gf.Vec3d(0, 0, -2)) 258 self.assertTrue(Gf.IsClose( r.startPoint, Gf.Vec3d(0, 0, -1), 0.00001 )) 259 self.assertTrue(Gf.IsClose( r.direction, Gf.Vec3d(0, 0, -1), 0.00001 )) 260 261 r = Gf.Frustum().ComputePickRay(Gf.Vec3d(2, 2, -1)) 262 self.assertTrue(Gf.IsClose( r.startPoint, Gf.Vec3d(2./3, 2./3, -1./3), 0.00001 )) 263 self.assertTrue(Gf.IsClose( r.direction, Gf.Vec3d(2./3, 2./3, -1./3), 0.00001 )) 264 265 f = Gf.Frustum() 266 f.projectionType = f.Orthographic 267 r = f.ComputePickRay(Gf.Vec3d(2, 2, -2)) 268 self.assertTrue(Gf.IsClose( r.startPoint, Gf.Vec3d(2, 2, -1), 0.00001 )) 269 self.assertTrue(Gf.IsClose( r.direction, Gf.Vec3d(0, 0, -1), 0.00001 )) 270 271 def test_EmptyFrustumIntersection(self): 272 self.assertFalse(Gf.Frustum().Intersects(Gf.BBox3d())) 273 self.assertTrue(Gf.Frustum().Intersects(Gf.BBox3d(Gf.Range3d(Gf.Vec3d(-1,-1,-1), 274 Gf.Vec3d(1,1,1))))) 275 self.assertTrue(Gf.Frustum().Intersects(Gf.Vec3d(0,0,-1))) 276 self.assertFalse(Gf.Frustum().Intersects(Gf.Vec3d(0,0,0))) 277 278 self.assertFalse(Gf.Frustum().Intersects(Gf.Vec3d(), Gf.Vec3d(1,1,1), Gf.Vec3d(0,0,1))) 279 self.assertTrue(Gf.Frustum().Intersects(Gf.Vec3d(), Gf.Vec3d(-1,-1,-1), Gf.Vec3d(0,0,1))) 280 281 self.assertFalse(Gf.Frustum().Intersects(Gf.Vec3d(0, 100, -100), \ 282 Gf.Vec3d(-100,-100,100), \ 283 Gf.Vec3d(100,-100,100))) 284 self.assertTrue(Gf.Frustum().Intersects(Gf.Vec3d(0, 10, 100), \ 285 Gf.Vec3d(-100,-10,-10), \ 286 Gf.Vec3d(100,-10,-10))) 287 self.assertTrue(Gf.Frustum().Intersects(Gf.Vec3d(0, 1, 1), \ 288 Gf.Vec3d(50,0,-50), \ 289 Gf.Vec3d(-50,0,-50))) 290 291 self.assertTrue(Gf.Frustum().Intersects(Gf.Vec3d(), Gf.Vec3d(-1,-1,-1), Gf.Vec3d(0,0,1))) 292 293 self.assertTrue(Gf.Frustum().Intersects(Gf.Vec3d(0,0,0), Gf.Vec3d(1,1,-1))) 294 self.assertTrue(Gf.Frustum().Intersects(Gf.Vec3d(-100,0,-1), Gf.Vec3d(100,0,-1))) 295 self.assertTrue(Gf.Frustum().Intersects(Gf.Vec3d(0,100,-1), Gf.Vec3d(0,-100,-1))) 296 self.assertFalse(Gf.Frustum().Intersects(Gf.Vec3d(-100,0,1), Gf.Vec3d(100,0,-1))) 297 self.assertFalse(Gf.Frustum().Intersects(Gf.Vec3d(0,0,0), Gf.Vec3d(1,1,1))) 298 299 def test_Str(self): 300 f = Gf.Frustum() 301 f.projectionType = f.Perspective 302 self.assertTrue(len(str(f))) 303 f.projectionType = f.Orthographic 304 self.assertTrue(len(str(f))) 305 306 def test_IntersectionViewVolume(self): 307 308 # a viewProjMat corresponding to a persp cam looking down the Y axis, 309 # aimed at the origin. 310 viewMat = Gf.Matrix4d(1.0, 0.0, 0.0, 0.0, 311 0.0, 0.0, -1.0, 0.0, 312 0.0, 1.0, 0.0, 0.0, 313 0.0, 0.0, -20, 1.0) 314 projMat = Gf.Matrix4d(4.241894005673533, 0.0, 0.0, 0.0, 315 0.0, 4.2418940586972074, 0.0, 0.0, 316 0.0, 0.0, -1, -1.0, 317 0.0, 0.0, -20, 0.0) 318 viewProjMat = viewMat * projMat 319 320 # a typical box entirely in the view 321 b = Gf.BBox3d( Gf.Range3d( Gf.Vec3d( 0, 0, 0 ), Gf.Vec3d( 1, 1, 1 ) ) ) 322 self.assertTrue(Gf.Frustum.IntersectsViewVolume(b,viewProjMat)) 323 324 # a typical box entirely out of the view 325 b = Gf.BBox3d( Gf.Range3d( Gf.Vec3d( 100, 0, 0 ), Gf.Vec3d( 101, 1, 1 ) ) ) 326 self.assertFalse(Gf.Frustum.IntersectsViewVolume(b,viewProjMat)) 327 328 # a large box entirely enclosing the view 329 b = Gf.BBox3d( Gf.Range3d( Gf.Vec3d( -1e9, -1e9, -1e9 ), 330 Gf.Vec3d( 1e9, 1e9, 1e9 ) ) ) 331 self.assertTrue(Gf.Frustum.IntersectsViewVolume(b,viewProjMat)) 332 333 def test_Serialization(self): 334 f = Gf.Frustum(Gf.Vec3d(3,4,5), Gf.Rotation((1,2,3),40), 335 Gf.Range2d(Gf.Vec2d(-0.2,-0.3), Gf.Vec2d(0.2, 0.33)), 336 Gf.Range1d(10, 100), 337 Gf.Frustum.Perspective, 338 viewDistance = 20) 339 340 f2 = eval(repr(f)) 341 342 f3 = Gf.Frustum(rotation = Gf.Rotation((1,2,3),40), 343 nearFar = Gf.Range1d(10, 100), 344 projectionType = Gf.Frustum.Perspective, 345 position = Gf.Vec3d(3,4,5), 346 viewDistance = 20, 347 window = Gf.Range2d(Gf.Vec2d(-0.2,-0.3), Gf.Vec2d(0.2, 0.33))) 348 349 self.assertAlmostEqual(f, f2) 350 self.assertAlmostEqual(f, f3) 351 self.assertAlmostEqual(f.viewDistance, 20.0) 352 self.assertAlmostEqual(f2.viewDistance, 20.0) 353 354 def test_ConstructFromMatrix(self): 355 m = Gf.Matrix4d(0.9987016645043332, -0.035803686178599, -0.036236464677155, 0.0, 356 0.0362364646771555, 0.999278702502407, 0.011357524061459, 0.0, 357 0.0358036861785999, -0.012655859557126, 0.999278702502407, 0.0, 358 3.0 , -6.0 , 5.0 , 1.0) 359 360 f = Gf.Frustum(m, 361 Gf.Range2d(Gf.Vec2d(-0.22,-0.2), Gf.Vec2d(0.2, 0.33)), 362 Gf.Range1d(20, 90), 363 Gf.Frustum.Perspective) 364 365 f = Gf.Frustum(m, 366 Gf.Range2d(Gf.Vec2d(-0.22,-0.2), Gf.Vec2d(0.2, 0.33)), 367 Gf.Range1d(20, 90), 368 Gf.Frustum.Perspective) 369 370 corners = f.ComputeCorners() 371 results = (Gf.Vec3d( -2.255306906099, -9.58646139968125, -14.8715637017144), 372 Gf.Vec3d( 6.133787075736, -9.88721236358150, -15.1759500050026), 373 Gf.Vec3d( -1.871200380521, 1.00589284684426, -14.7511739466630), 374 Gf.Vec3d( 6.517893601314, 0.70514188294401, -15.0555602499511), 375 Gf.Vec3d(-20.648881077448, -22.13907629856565, -84.4220366577152), 376 Gf.Vec3d( 17.102041840815, -23.49245563611677, -85.7917750225117), 377 Gf.Vec3d(-18.920401712348, 25.52651781079917, -83.8802827599836), 378 Gf.Vec3d( 18.830521205915, 24.17313847324806, -85.2500211247801)) 379 380 self.assertEqual(len(corners), len(results)) 381 for i in range(len(results)): 382 self.assertTrue(Gf.IsClose(corners[i], results[i], 0.0001)) 383 384 corners = f.ComputeCornersAtDistance(20) 385 for i in range(len(corners)): 386 self.assertTrue(Gf.IsClose(corners[i], results[i], 0.0001)) 387 388 corners = f.ComputeCornersAtDistance(90) 389 for i in range(len(corners)): 390 self.assertTrue(Gf.IsClose(corners[i], results[i+4], 0.0001)) 391 392 corners = f.ComputeCornersAtDistance((20 + 90) / 2.0) 393 for i in range(len(corners)): 394 self.assertTrue( 395 Gf.IsClose(corners[i], (results[i] + results[i+4]) / 2.0, 396 0.0001)) 397 398 399if __name__ == '__main__': 400 unittest.main() 401