1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Associates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16
17 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
18
19 ON_OBJECT_IMPLEMENT( ON_Viewport, ON_Geometry, "D66E5CCF-EA39-11d3-BFE5-0010830122F0" );
20
len2d(double x,double y)21 static double len2d( double x, double y )
22 {
23 double d= 0.0;
24 double fx = fabs(x);
25 double fy = fabs(y);
26 if ( fx > fy ) {
27 d = fy/fx;
28 d = fx*sqrt(1.0+d*d);
29 }
30 else if ( fy > fx ){
31 d = fx/fy;
32 d = fy*sqrt(1.0+d*d);
33 }
34 return d;
35 }
36
unitize2d(double x,double y,double * ux,double * uy)37 static void unitize2d( double x, double y, double* ux, double* uy )
38 {
39 const double eps = 2.0*ON_SQRT_EPSILON;
40 // carefully turn two numbers into a 2d unit vector
41 double s, c, d;
42 c = x;
43 s = y;
44 if ( s == 0.0 ) {
45 c = (c < 0.0) ? -1.0 : 1.0;
46 }
47 else {
48 if ( fabs(s) > fabs(c) ) {
49 d = c/s;
50 d = fabs(s)*sqrt(1.0+d*d);
51 }
52 else {
53 d = s/c;
54 d = fabs(c)*sqrt(1.0+d*d);
55 }
56 d = 1.0/d;
57 if ( fabs(d-1.0) > eps ) {
58 s *= d;
59 c *= d;
60 }
61 if ( fabs(s) <= eps || fabs(c) >= 1.0-eps ) {
62 s = 0.0;
63 c = (c < 0.0) ? -1.0 : 1.0;
64 }
65 else if ( fabs(c) < eps || fabs(s) >= 1.0-eps) {
66 c = 0.0;
67 s = (s < 0.0) ? -1.0 : 1.0;
68 }
69 }
70 if ( ux )
71 *ux = c;
72 if ( uy )
73 *uy = s;
74 }
75
76
77 static
ON__IsCameraFrameUnitVectorHelper(const ON_3dVector & v)78 bool ON__IsCameraFrameUnitVectorHelper( const ON_3dVector& v )
79 {
80 // looser standard than ON_3dVector::IsUnitVector() so
81 // going to/from floats in OpenGL and Direct3d doesn't
82 // create "invalid" views.
83 return (v.x != ON_UNSET_VALUE && v.y != ON_UNSET_VALUE && v.z != ON_UNSET_VALUE && fabs(v.Length() - 1.0) <= 1.0e-6);
84 }
85
86 static
ON__IsCameraFramePerpindicular(const ON_3dVector & unit_vector0,const ON_3dVector & unit_vector1)87 bool ON__IsCameraFramePerpindicular( const ON_3dVector& unit_vector0,const ON_3dVector& unit_vector1 )
88 {
89 return ( fabs(unit_vector0.x*unit_vector1.x + unit_vector0.y*unit_vector1.y + unit_vector0.z*unit_vector1.z) <= 1.0e-6 );
90 }
91
92 bool
ON_GetViewportRotationAngles(const ON_3dVector & X,const ON_3dVector & Y,const ON_3dVector & Z,double * angle1,double * angle2,double * angle3)93 ON_GetViewportRotationAngles(
94 const ON_3dVector& X, // X,Y,Z must be a right handed orthonormal basis
95 const ON_3dVector& Y,
96 const ON_3dVector& Z,
97 double* angle1, // returns rotation about world Z
98 double* angle2, // returns rotation about world X ( 0 <= a2 <= pi )
99 double* angle3 // returns rotation about world Z
100 )
101 {
102 // double a1 = 0.0; // rotation about world Z
103 // double a2 = 0.0; // rotation about world X ( 0 <= a2 <= pi )
104 // double a3 = 0.0; // rotation about world Z
105 bool bValidFrame = false;
106 double sin_a1 = 0.0;
107 double cos_a1 = 1.0;
108 double sin_a2 = 0.0;
109 double cos_a2 = 1.0;
110 double sin_a3 = 0.0;
111 double cos_a3 = 1.0;
112
113 // If si = sin(ai) and ci = cos(ai), then the relationship between the camera
114 // frame and the angles is defined by the matrix equation C = R3*R2*R1, where:
115 //
116 // c1 -s1 0 1 0 0 c3 -s3 0
117 // R1 = s1 c1 0 R2 = 0 c2 -s2 R3 = s3 c3 0
118 // 0 0 1 0 s2 c2 0 0 1
119 //
120 // CamX[0] CamY[0] CamZ[0]
121 // C = CamX[1] CamY[1] CamZ[1]
122 // CamX[2] CamY[2] CamZ[2]
123 //
124 // . . s2*s3
125 //
126 // R3*R2*R1 = . . -s2*c3
127 //
128 // s1*s2 c1*s2 c2
129 //
130
131 {
132 // don't attempt to work with slop
133 const double eps = 8.0*ON_SQRT_EPSILON;
134 double dx,dy,dz,d;
135 dx = X*X;
136 dy = Y*Y;
137 dz = Z*Z;
138 if ( fabs(dx-1.0) <= eps && fabs(dy-1.0) <= eps && fabs(dz-1.0) <= eps ) {
139 dx = X*Y;
140 dy = Y*Z;
141 dz = Z*X;
142 if ( fabs(dx) <= eps && fabs(dy) <= eps && fabs(dz) <= eps ) {
143 d = ON_TripleProduct( X, Y, Z );
144 bValidFrame = (d > 0.0);
145 }
146 }
147 }
148
149 if ( bValidFrame )
150 {
151 // Usually "Z" = opposite of unitized camera direction.
152 // "Y" = camera up made ortho to "Z" and unitized.
153 // "X" = YxZ.
154 // So, when possible, I solve for angles in terms
155 // of "Z" and "Y" since "X" will generally have the most noise.
156 //
157 // Use C = R3*R2*R1 to get sin(a2), cos(a2).
158 cos_a2 = Z.z;
159 sin_a2 = len2d(Z.x,Z.y);
160 unitize2d(cos_a2,sin_a2,&cos_a2,&sin_a2); // kill noise
161
162 if ( sin_a2 > 0.0 ) {
163 // use bottom row to get angle1.
164 sin_a1 = X.z;
165 cos_a1 = Y.z;
166 unitize2d(cos_a1,sin_a1,&cos_a1,&sin_a1); // kill noise
167
168 // use right column to get angle3
169 cos_a3 = -Z.y;
170 sin_a3 = Z.x;
171 unitize2d(cos_a3,sin_a3,&cos_a3,&sin_a3); // kill noise
172 }
173 else if ( cos_a2 == 1.0 ) {
174 // R2 = identity and C determines (angle1+angle3)
175 // arbitrarily set angle1 = 0.
176 cos_a3 = Y.y; // = cos(angle3+angle1)
177 sin_a3 = -Y.x; // = sin(angle3+angle1)
178 }
179 else if ( cos_a2 == -1.0 ) {
180 // R2 = [1 0 0 / 0 -1 0/ 0 0 -1] and C determines (angle3-angle1)
181 // arbitrarily set angle1 = 0
182 cos_a3 = -Y.y; // = cos(angle3-angle1)
183 sin_a3 = Y.x; // = sin(angle3-angle1)
184 }
185 }
186
187
188 if ( cos_a1 == -1.0 && sin_a1 == 0.0 ) {
189 // when a1 = pi, juggle angles to get a1 = 0 with
190 // same effective rotation to keep legacy 3d apps
191 // happy.
192 // a1: pi -> 0
193 // a2: a2 -> 2pi - a2
194 // a1: a3 -> pi + a3
195 sin_a1 = 0.0;
196 cos_a1 = 0.0;
197 sin_a2 = -sin_a2;
198 sin_a3 = -sin_a3;
199 cos_a3 = -cos_a3;
200 }
201
202 if ( angle1 )
203 *angle1 = atan2( sin_a1, cos_a1 );
204 if ( angle2 )
205 *angle2 = atan2( sin_a2, cos_a2 );
206 if ( angle3 )
207 *angle3 = atan2( sin_a3, cos_a3 );
208
209 return bValidFrame;
210 }
211
SetPerspectiveClippingPlaneConstraints(unsigned int depth_buffer_bit_depth)212 void ON_Viewport::SetPerspectiveClippingPlaneConstraints(
213 unsigned int depth_buffer_bit_depth
214 )
215 {
216 double min_near_dist = 0.0;
217 double min_near_over_far = 0.0;
218 ON_Viewport::GetPerspectiveClippingPlaneConstraints(m_CamLoc,depth_buffer_bit_depth,&min_near_dist,&min_near_over_far);
219 SetPerspectiveMinNearDist(min_near_dist);
220 SetPerspectiveMinNearOverFar(min_near_over_far);
221 }
222
223
SetPerspectiveMinNearOverFar(double min_near_over_far)224 void ON_Viewport::SetPerspectiveMinNearOverFar(double min_near_over_far)
225 {
226 if ( ON_IsValid(min_near_over_far)
227 && min_near_over_far > ON_ZERO_TOLERANCE
228 && min_near_over_far < 1.0-ON_ZERO_TOLERANCE
229 )
230 {
231 m__MIN_NEAR_OVER_FAR = min_near_over_far;
232 }
233 }
234
PerspectiveMinNearOverFar() const235 double ON_Viewport::PerspectiveMinNearOverFar() const
236 {
237 return m__MIN_NEAR_OVER_FAR;
238 }
239
SetPerspectiveMinNearDist(double min_near_dist)240 void ON_Viewport::SetPerspectiveMinNearDist(double min_near_dist)
241 {
242 if ( ON_IsValid(min_near_dist) && min_near_dist > ON_ZERO_TOLERANCE )
243 {
244 m__MIN_NEAR_DIST = min_near_dist;
245 }
246 }
247
PerspectiveMinNearDist() const248 double ON_Viewport::PerspectiveMinNearDist() const
249 {
250 return m__MIN_NEAR_DIST;
251 }
252
253
254
255 // Discuss any changes of these values with Dale Lear
256 const double ON_Viewport::DefaultNearDist = 0.005;
257 const double ON_Viewport::DefaultFarDist = 1000.0;
258 const double ON_Viewport::DefaultMinNearDist = 0.0001;
259 const double ON_Viewport::DefaultMinNearOverFar = 0.0001;
260
261 // For 32 bit float based OpenGL drivers, the value of
262 // the ON_Viewport::DefaultMinNearOverFar constant must
263 // be <0.01 and >= 0.0001.
264 // If you change this value, you need to retest RR 8902 on OpenGL
265 // drivers that (internally) use float precision transformations.
266 // Some OpenGL drivers, like the Microsoft software emulation
267 // driver for XP crash in some cases when near/far > 1e8.
268 //
269 // ON_Viewport::DefaultMinNearOverFar = 0.001 // used in Rhino 3.0 beta testing until 11 Sep 2002
270 // ON_Viewport::DefaultMinNearOverFar = 0.01 // used for Rhino 3.0 CD1 and CD2
271 // ON_Viewport::DefaultMinNearOverFar = 0.000001 // used for Rhino 3.0 CD3
272 // ON_Viewport::DefaultMinNearOverFar = 0.0001 // used for Rhino 4.0 Fixes RR 8902
273
274 void
Initialize()275 ON_Viewport::Initialize()
276 {
277 m__MIN_NEAR_DIST = ON_Viewport::DefaultMinNearDist;
278 m__MIN_NEAR_OVER_FAR = ON_Viewport::DefaultMinNearOverFar;
279
280 m_bValidCamera = true;
281 m_bValidFrustum = true;
282 m_bValidPort = false;
283 m_reserved1 = 0;
284 m_projection = ON::parallel_view;
285
286 m_bLockCamUp = false;
287 m_bLockCamDir = false;
288 m_bLockCamLoc = false;
289 m_frustum_symmetry_flags = 0;
290
291 m_CamLoc.x = 0.0;
292 m_CamLoc.y = 0.0;
293 m_CamLoc.z = 100.0;
294 m_CamDir = -ON_zaxis;
295 m_CamUp = ON_yaxis;
296 m_CamX = ON_xaxis;
297 m_CamY = ON_yaxis;
298 m_CamZ = ON_zaxis;
299 m_frus_left = -20.0;
300 m_frus_right = 20.0;
301 m_frus_bottom = -20.0;
302 m_frus_top = 20.0;
303 m_frus_near = m__MIN_NEAR_DIST;
304 m_frus_far = ON_Viewport::DefaultFarDist;
305 m_port_left = 0;
306 m_port_right = 1000;
307 m_port_bottom = 0;
308 m_port_top = 1000;
309 m_port_near = 0;//0xffff;
310 m_port_far = 1;//0;
311 m_clip_mods.Identity();
312 m_clip_mods_inverse.Identity();
313 m_target_point = ON_UNSET_POINT;
314 m_viewport_id = ON_nil_uuid;
315 }
316
317 const ON_3dVector ON_Viewport::Default3dCameraDirection(-0.43301270189221932338186158537647,0.75,-0.5);
318
ON_Viewport()319 ON_Viewport::ON_Viewport()
320 {
321 Initialize();
322 }
323
~ON_Viewport()324 ON_Viewport::~ON_Viewport()
325 {}
326
operator =(const ON_Viewport & src)327 ON_Viewport& ON_Viewport::operator=( const ON_Viewport& src )
328 {
329 if ( this != &src )
330 {
331 ON_Object::operator=(src);
332
333 m_bValidCamera = src.m_bValidCamera;
334 m_bValidFrustum = src.m_bValidFrustum;
335 m_bValidPort = src.m_bValidPort;
336
337 m_projection = src.m_projection;
338
339 m_bLockCamUp = src.m_bLockCamUp;
340 m_bLockCamDir = src.m_bLockCamDir;
341 m_bLockCamLoc = src.m_bLockCamLoc;
342 m_frustum_symmetry_flags = src.m_frustum_symmetry_flags;
343
344 m_CamLoc = src.m_CamLoc;
345 m_CamDir = src.m_CamDir;
346 m_CamUp = src.m_CamUp;
347 m_CamX = src.m_CamX;
348 m_CamY = src.m_CamY;
349 m_CamZ = src.m_CamZ;
350
351 m_frus_left = src.m_frus_left;
352 m_frus_right = src.m_frus_right;
353 m_frus_bottom = src.m_frus_bottom;
354 m_frus_top = src.m_frus_top;
355 m_frus_near = src.m_frus_near;
356 m_frus_far = src.m_frus_far;
357
358 m_port_left = src.m_port_left;
359 m_port_right = src.m_port_right;
360 m_port_bottom = src.m_port_bottom;
361 m_port_top = src.m_port_top;
362 m_port_near = src.m_port_near;
363 m_port_far = src.m_port_far;
364
365 m_target_point = src.m_target_point;
366
367 m_clip_mods = src.m_clip_mods;
368 m_clip_mods_inverse = src.m_clip_mods_inverse;
369
370 m__MIN_NEAR_OVER_FAR = src.m__MIN_NEAR_OVER_FAR;
371 m__MIN_NEAR_DIST = src.m__MIN_NEAR_DIST;
372
373 m_viewport_id = src.m_viewport_id;
374 }
375 return *this;
376 }
377
378
Read(ON_BinaryArchive & file)379 ON_BOOL32 ON_Viewport::Read( ON_BinaryArchive& file )
380 {
381 Initialize();
382 int major_version = 0;
383 int minor_version = 1;
384 bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
385 if (rc && major_version==1)
386 {
387 // common to all 1.x versions
388 int i=0;
389 if (rc) rc = file.ReadInt( &i );
390 if (rc) m_bValidCamera = (i?true:false);
391 if (rc) rc = file.ReadInt( &i );
392 if (rc) m_bValidFrustum = (i?true:false);
393 if (rc) rc = file.ReadInt( &i );
394 if (rc) m_bValidPort = (i?true:false);
395 if (rc) rc = file.ReadInt( &i );
396 if (rc) m_projection = ON::ViewProjection(i);
397 if (rc) rc = file.ReadPoint( m_CamLoc );
398 if (rc) rc = file.ReadVector( m_CamDir );
399 if (rc) rc = file.ReadVector( m_CamUp );
400 if (rc) rc = file.ReadVector( m_CamX );
401 if (rc) rc = file.ReadVector( m_CamY );
402 if (rc) rc = file.ReadVector( m_CamZ );
403 if (rc) rc = file.ReadDouble( &m_frus_left );
404 if (rc) rc = file.ReadDouble( &m_frus_right );
405 if (rc) rc = file.ReadDouble( &m_frus_bottom );
406 if (rc) rc = file.ReadDouble( &m_frus_top );
407 if (rc) rc = file.ReadDouble( &m_frus_near );
408 if (rc) rc = file.ReadDouble( &m_frus_far );
409 if (rc) rc = file.ReadInt( &m_port_left );
410 if (rc) rc = file.ReadInt( &m_port_right );
411 if (rc) rc = file.ReadInt( &m_port_bottom );
412 if (rc) rc = file.ReadInt( &m_port_top );
413 if (rc) rc = file.ReadInt( &m_port_near );
414 if (rc) rc = file.ReadInt( &m_port_far );
415
416 if (rc && minor_version >= 1 )
417 {
418 // 1.1 fields
419 if (rc) rc = file.ReadUuid(m_viewport_id);
420
421 if (rc && minor_version >= 2 )
422 {
423 // 1.2 fields
424 bool b;
425 b = false;
426 if (rc) rc = file.ReadBool(&b);
427 if (rc) SetCameraUpLock(b);
428
429 b = false;
430 if (rc) rc = file.ReadBool(&b);
431 if (rc) SetCameraDirectionLock(b);
432
433 b = false;
434 if (rc) rc = file.ReadBool(&b);
435 if (rc) SetCameraLocationLock(b);
436
437 b = false;
438 if (rc) rc = file.ReadBool(&b);
439 if (rc) SetFrustumLeftRightSymmetry(b);
440
441 b = false;
442 if (rc) rc = file.ReadBool(&b);
443 if (rc) SetFrustumTopBottomSymmetry(b);
444 }
445 }
446
447 if ( m_bValidCamera )
448 {
449 if ( !m_CamLoc.IsValid() || !m_CamUp.IsValid() || !m_CamDir.IsValid() )
450 {
451 ON_ERROR("ON_Viewport.m_bValidCamera in file was true and it should be false.");
452 m_bValidCamera = false;
453 }
454 }
455
456 if( m_bValidFrustum )
457 {
458 if ( !ON_IsValid(m_frus_left) || !ON_IsValid(m_frus_right)
459 || !ON_IsValid(m_frus_top) || !ON_IsValid(m_frus_bottom)
460 || !ON_IsValid(m_frus_near) || !ON_IsValid(m_frus_far)
461 || m_frus_right <= m_frus_left
462 || m_frus_top <= m_frus_bottom
463 || m_frus_near <= 0.0
464 || m_frus_far <= m_frus_near
465 )
466 {
467 ON_ERROR("ON_Viewport.m_bValidFrustum in file was true and it should be false.");
468 m_bValidFrustum = false;
469 }
470 }
471 }
472 return rc;
473 }
474
Write(ON_BinaryArchive & file) const475 ON_BOOL32 ON_Viewport::Write( ON_BinaryArchive& file ) const
476 {
477 int i;
478 bool rc = file.Write3dmChunkVersion(1,2);
479 if (rc)
480 {
481 i = m_bValidCamera?1:0;
482 if (rc) rc = file.WriteInt( i );
483 i = m_bValidFrustum?1:0;
484 if (rc) rc = file.WriteInt( i );
485 i = m_bValidPort?1:0;
486 if (rc) rc = file.WriteInt( i );
487 i = m_projection;
488 if ( file.Archive3dmVersion() <= 4 && IsPerspectiveProjection() )
489 {
490 // V4 files do not support 2 point perspective projection
491 i = ON::perspective_view;
492 }
493 if (rc) rc = file.WriteInt( i );
494 if (rc) rc = file.WritePoint( m_CamLoc );
495 if (rc) rc = file.WriteVector( m_CamDir );
496 if (rc) rc = file.WriteVector( m_CamUp );
497 if (rc) rc = file.WriteVector( m_CamX );
498 if (rc) rc = file.WriteVector( m_CamY );
499 if (rc) rc = file.WriteVector( m_CamZ );
500 if (rc) rc = file.WriteDouble( m_frus_left );
501 if (rc) rc = file.WriteDouble( m_frus_right );
502 if (rc) rc = file.WriteDouble( m_frus_bottom );
503 if (rc) rc = file.WriteDouble( m_frus_top );
504 if (rc) rc = file.WriteDouble( m_frus_near );
505 if (rc) rc = file.WriteDouble( m_frus_far );
506 if (rc) rc = file.WriteInt( m_port_left );
507 if (rc) rc = file.WriteInt( m_port_right );
508 if (rc) rc = file.WriteInt( m_port_bottom );
509 if (rc) rc = file.WriteInt( m_port_top );
510 if (rc) rc = file.WriteInt( m_port_near );
511 if (rc) rc = file.WriteInt( m_port_far );
512
513 // 1.1 fields
514 if (rc) rc = file.WriteUuid(m_viewport_id);
515
516 // 1.2 fields
517 bool b;
518
519 b = CameraUpIsLocked();
520 if (rc) rc = file.WriteBool(b);
521
522 b = CameraDirectionIsLocked();
523 if (rc) rc = file.WriteBool(b);
524
525 b = CameraLocationIsLocked();
526 if (rc) rc = file.WriteBool(b);
527
528 b = FrustumIsLeftRightSymmetric();
529 if (rc) rc = file.WriteBool(b);
530
531 b = FrustumIsTopBottomSymmetric();
532 if (rc) rc = file.WriteBool(b);
533 }
534 return rc;
535 }
536
IsValidCamera() const537 bool ON_Viewport::IsValidCamera() const
538 {
539 return ( m_bValidCamera );
540 }
541
IsValidFrustum() const542 bool ON_Viewport::IsValidFrustum() const
543 {
544 return ( m_bValidFrustum );
545 }
546
IsValid(ON_TextLog * text_log) const547 ON_BOOL32 ON_Viewport::IsValid( ON_TextLog* text_log ) const
548 {
549 if ( !IsValidCamera() )
550 {
551 if ( 0 != text_log )
552 {
553 text_log->Print("invalid viewport camera settings.\n");
554 }
555 return false;
556 }
557 if ( !IsValidFrustum() )
558 {
559 if ( 0 != text_log )
560 {
561 text_log->Print("invalid viewport frustum settings.\n");
562 }
563 return false;
564 }
565 if ( !m_bValidPort )
566 {
567 if ( 0 != text_log )
568 {
569 text_log->Print("invalid viewport port extents settings.\n");
570 }
571 return false;
572 }
573 return true;
574 }
575
576
Dimension() const577 int ON_Viewport::Dimension() const
578 {
579 return 3;
580 }
581
GetNearPlane(ON_Plane & near_plane) const582 bool ON_Viewport::GetNearPlane( ON_Plane& near_plane ) const
583 {
584 bool rc = IsValidFrustum() && IsValidCamera();
585 if ( rc )
586 {
587 near_plane.origin = m_CamLoc - m_frus_near*m_CamZ;
588 near_plane.xaxis = m_CamX;
589 near_plane.yaxis = m_CamY;
590 near_plane.zaxis = m_CamZ;
591 near_plane.UpdateEquation();
592 }
593 return rc;
594 }
595
GetNearPlaneEquation(ON_PlaneEquation & near_plane_equation) const596 bool ON_Viewport::GetNearPlaneEquation(
597 ON_PlaneEquation& near_plane_equation
598 ) const
599 {
600 bool rc = m_bValidCamera && m_bValidFrustum;
601 if (rc)
602 {
603 near_plane_equation.ON_3dVector::operator=(m_CamZ);
604 near_plane_equation.d = -near_plane_equation.ON_3dVector::operator*(m_CamLoc - m_frus_near*m_CamZ);
605 }
606 return rc;
607 }
608
609
GetFarPlane(ON_Plane & far_plane) const610 bool ON_Viewport::GetFarPlane( ON_Plane& far_plane ) const
611 {
612 bool rc = IsValidFrustum() && IsValidCamera();
613 if ( rc )
614 {
615 far_plane.origin = m_CamLoc - m_frus_far*m_CamZ;
616 far_plane.xaxis = m_CamX;
617 far_plane.yaxis = m_CamY;
618 far_plane.zaxis = m_CamZ;
619 far_plane.UpdateEquation();
620 }
621 return rc;
622 }
623
GetFarPlaneEquation(ON_PlaneEquation & far_plane_equation) const624 bool ON_Viewport::GetFarPlaneEquation(
625 ON_PlaneEquation& far_plane_equation
626 ) const
627 {
628 bool rc = m_bValidCamera && m_bValidFrustum;
629 if (rc)
630 {
631 far_plane_equation.ON_3dVector::operator=(m_CamZ);
632 far_plane_equation.d = -far_plane_equation.ON_3dVector::operator*(m_CamLoc - m_frus_far*m_CamZ);
633 }
634 return rc;
635 }
636
637
GetViewPlane(double view_plane_depth,ON_Plane & view_plane) const638 bool ON_Viewport::GetViewPlane(
639 double view_plane_depth,
640 ON_Plane& view_plane
641 ) const
642 {
643 bool rc = IsValidFrustum() && IsValidCamera();
644 if ( rc )
645 {
646 view_plane.origin = m_CamLoc - view_plane_depth*m_CamZ;
647 view_plane.xaxis = m_CamX;
648 view_plane.yaxis = m_CamY;
649 view_plane.zaxis = m_CamZ;
650 view_plane.UpdateEquation();
651 }
652 return rc;
653 }
654
GetViewPlaneEquation(double view_plane_depth,ON_PlaneEquation & view_plane_equation) const655 bool ON_Viewport::GetViewPlaneEquation(
656 double view_plane_depth,
657 ON_PlaneEquation& view_plane_equation
658 ) const
659 {
660 bool rc = m_bValidCamera && m_bValidFrustum;
661 if (rc)
662 {
663 view_plane_equation.ON_3dVector::operator=(m_CamZ);
664 view_plane_equation.d = -view_plane_equation.ON_3dVector::operator*(m_CamLoc - view_plane_depth*m_CamZ);
665 }
666 return rc;
667 }
668
669
GetNearRect(ON_3dPoint & left_bottom,ON_3dPoint & right_bottom,ON_3dPoint & left_top,ON_3dPoint & right_top) const670 bool ON_Viewport::GetNearRect(
671 ON_3dPoint& left_bottom,
672 ON_3dPoint& right_bottom,
673 ON_3dPoint& left_top,
674 ON_3dPoint& right_top
675 ) const
676 {
677 ON_Plane near_plane;
678 bool rc = GetNearPlane( near_plane );
679 if (rc ) {
680 double x = 1.0, y = 1.0;
681 GetViewScale(&x,&y);
682 x = 1.0/x;
683 y = 1.0/y;
684 left_bottom = near_plane.PointAt( x*m_frus_left, y*m_frus_bottom );
685 right_bottom = near_plane.PointAt( x*m_frus_right, y*m_frus_bottom );
686 left_top = near_plane.PointAt( x*m_frus_left, y*m_frus_top );
687 right_top = near_plane.PointAt( x*m_frus_right, y*m_frus_top );
688 }
689 return rc;
690 }
691
GetFarRect(ON_3dPoint & left_bottom,ON_3dPoint & right_bottom,ON_3dPoint & left_top,ON_3dPoint & right_top) const692 bool ON_Viewport::GetFarRect(
693 ON_3dPoint& left_bottom,
694 ON_3dPoint& right_bottom,
695 ON_3dPoint& left_top,
696 ON_3dPoint& right_top
697 ) const
698 {
699 ON_Plane far_plane;
700 bool rc = GetFarPlane( far_plane );
701 if (rc )
702 {
703 double s = IsPerspectiveProjection()
704 ? m_frus_far/m_frus_near
705 : 1.0;
706 double x = 1.0, y = 1.0;
707 GetViewScale(&x,&y);
708 x = 1.0/x;
709 y = 1.0/y;
710 left_bottom = far_plane.PointAt( s*x*m_frus_left, s*y*m_frus_bottom );
711 right_bottom = far_plane.PointAt( s*x*m_frus_right, s*y*m_frus_bottom );
712 left_top = far_plane.PointAt( s*x*m_frus_left, s*y*m_frus_top );
713 right_top = far_plane.PointAt( s*x*m_frus_right, s*y*m_frus_top );
714 }
715 return rc;
716 }
717
GetViewPlaneRect(double view_plane_depth,ON_3dPoint & left_bottom,ON_3dPoint & right_bottom,ON_3dPoint & left_top,ON_3dPoint & right_top) const718 bool ON_Viewport::GetViewPlaneRect(
719 double view_plane_depth,
720 ON_3dPoint& left_bottom,
721 ON_3dPoint& right_bottom,
722 ON_3dPoint& left_top,
723 ON_3dPoint& right_top
724 ) const
725 {
726 ON_Plane view_plane;
727 bool rc = GetViewPlane( view_plane_depth, view_plane );
728 if (rc )
729 {
730 double s = IsPerspectiveProjection()
731 ? view_plane_depth/m_frus_near
732 : 1.0;
733 double x = 1.0, y = 1.0;
734 GetViewScale(&x,&y);
735 x = 1.0/x;
736 y = 1.0/y;
737 left_bottom = view_plane.PointAt( s*x*m_frus_left, s*y*m_frus_bottom );
738 right_bottom = view_plane.PointAt( s*x*m_frus_right, s*y*m_frus_bottom );
739 left_top = view_plane.PointAt( s*x*m_frus_left, s*y*m_frus_top );
740 right_top = view_plane.PointAt( s*x*m_frus_right, s*y*m_frus_top );
741 }
742 return rc;
743 }
744
745
GetBBox(double * boxmin,double * boxmax,ON_BOOL32 bGrowBox) const746 ON_BOOL32 ON_Viewport::GetBBox(
747 double* boxmin,
748 double* boxmax,
749 ON_BOOL32 bGrowBox
750 ) const
751 {
752 ON_3dPoint corners[9];
753 bool rc = GetNearRect(corners[0],corners[1],corners[2],corners[3]);
754 if (rc)
755 rc = GetFarRect(corners[4],corners[5],corners[6],corners[7]);
756 corners[8] = m_CamLoc;
757 if (rc)
758 {
759 rc = ON_GetPointListBoundingBox(
760 3, 0, 9,
761 3, &corners[0].x,
762 boxmin, boxmax, bGrowBox?true:false
763 );
764 }
765 return rc;
766 }
767
Transform(const ON_Xform & xform)768 ON_BOOL32 ON_Viewport::Transform( const ON_Xform& xform )
769 {
770 ON_BOOL32 rc = IsValidCamera();
771 if (rc) {
772 // save input settings
773 const ON_3dPoint c0 = m_CamLoc;
774 const ON_3dPoint u0 = m_CamUp;
775 const ON_3dPoint d0 = m_CamDir;
776 const ON_3dPoint x0 = m_CamX;
777 const ON_3dPoint y0 = m_CamY;
778 const ON_3dPoint z0 = m_CamZ;
779
780 // compute transformed settings
781 ON_3dPoint c = xform*c0;
782 ON_3dVector u = (xform*(c0 + u0)) - c;
783 ON_3dVector d = (xform*(c0 + d0)) - c;
784
785 if ( m_bLockCamLoc )
786 c = m_CamLoc;
787 if ( m_bLockCamUp )
788 u = m_CamY;
789 if ( m_bLockCamDir )
790 d = -m_CamZ;
791
792 if ( !u.IsValid() || !d.IsValid()
793 || u.IsTiny() || d.IsTiny()
794 || ON_CrossProduct(u,d).IsTiny() )
795 {
796 rc = false;
797 }
798 else
799 {
800 if ( m_bLockCamUp && !m_bLockCamDir )
801 {
802 d.Unitize();
803 if ( fabs(d*u) <= ON_ZERO_TOLERANCE )
804 d = -m_CamZ;
805 }
806 else if ( m_bLockCamDir && !m_bLockCamUp )
807 {
808 u.Unitize();
809 if ( fabs(d*u) <= ON_ZERO_TOLERANCE )
810 u = m_CamY;
811 }
812
813 // set new camera position
814 if ( !m_bLockCamLoc )
815 SetCameraLocation(c);
816 if ( !m_bLockCamDir )
817 SetCameraDirection(d);
818 if ( !m_bLockCamUp)
819 SetCameraUp(u);
820 rc = SetCameraFrame();
821 if ( !rc )
822 {
823 // restore input settings
824 m_CamLoc = c0;
825 m_CamUp = u0;
826 m_CamDir = d0;
827 m_CamX = x0;
828 m_CamY = y0;
829 m_CamZ = z0;
830 }
831 }
832 }
833 return rc;
834 }
835
SetCameraLocation(const ON_3dPoint & p)836 bool ON_Viewport::SetCameraLocation( const ON_3dPoint& p )
837 {
838 if ( m_bLockCamLoc )
839 {
840 if ( m_CamLoc.IsValid() )
841 {
842 return (p == m_CamLoc);
843 }
844 }
845 if ( p != ON_3dPoint::UnsetPoint && !p.IsValid() )
846 return false;
847 m_CamLoc = p;
848 if ( !m_CamLoc.IsValid() )
849 m_bValidCamera = false;
850 return m_bValidCamera;
851 }
852
SetCameraDirection(const ON_3dVector & v)853 bool ON_Viewport::SetCameraDirection( const ON_3dVector& v )
854 {
855 if ( m_bLockCamDir )
856 {
857 if ( m_CamDir.IsValid() && !m_CamDir.IsTiny() )
858 {
859 return (v == m_CamDir);
860 }
861 }
862 if ( !v.IsValid() || v.IsTiny() )
863 return false;
864 m_CamDir = v;
865 return SetCameraFrame();
866 }
867
SetCameraUp(const ON_3dVector & v)868 bool ON_Viewport::SetCameraUp( const ON_3dVector& v )
869 {
870 if ( m_bLockCamUp )
871 {
872 if ( m_CamUp.IsValid() && !m_CamUp.IsTiny() )
873 {
874 return (v == m_CamUp);
875 }
876 }
877 if ( !v.IsValid() || v.IsTiny() )
878 return false;
879 m_CamUp = v;
880 return SetCameraFrame();
881 }
882
883 //ON_BOOL32 ON_Viewport::SetTargetDistance( double d )
884 //{
885 // m_target_distance = (d>0.0) ? d : 0.0;
886 // return (d>=0.0) ? true : false;
887 //}
888
SetCameraFrame()889 bool ON_Viewport::SetCameraFrame()
890 {
891 m_bValidCamera = false;
892
893 if ( !m_CamDir.IsValid() || !m_CamUp.IsValid() )
894 return false;
895
896 double d;
897 ON_3dVector CamX, CamY, CamZ;
898
899 if ( m_bLockCamUp && !m_bLockCamDir )
900 {
901 // up takes precedence over direction
902 CamY = m_CamUp;
903 if ( !CamY.IsValid() )
904 return false;
905 if ( !CamY.Unitize() )
906 return false;
907
908 d = m_CamDir*CamY;
909 CamZ = -m_CamDir + d*CamY;
910 if ( !CamZ.IsValid() )
911 return false;
912 if ( !CamZ.Unitize() )
913 return false;
914 }
915 else
916 {
917 // direction takes precedence over up
918 CamZ = -m_CamDir;
919 if ( !CamZ.IsValid() )
920 return false;
921 if ( !CamZ.Unitize() )
922 return false;
923
924 d = m_CamUp*CamZ;
925 CamY = m_CamUp - d*CamZ;
926 if ( !CamY.IsValid() )
927 return false;
928 if ( !CamY.Unitize() )
929 return false;
930 }
931
932 CamX = ON_CrossProduct( CamY, CamZ );
933 if ( !CamX.IsValid() )
934 return false;
935 if ( !CamX.Unitize() )
936 return false;
937
938 // Gaurd against garbage resulting from nearly parallel
939 // and/or ultra short short dir and up.
940 if ( !ON__IsCameraFrameUnitVectorHelper(CamX) )
941 return false;
942 if ( !ON__IsCameraFrameUnitVectorHelper(CamY) )
943 return false;
944 if ( !ON__IsCameraFrameUnitVectorHelper(CamZ) )
945 return false;
946 if ( !ON__IsCameraFramePerpindicular(CamX,CamY) )
947 return false;
948 if ( !ON__IsCameraFramePerpindicular(CamY,CamZ) )
949 return false;
950 if ( !ON__IsCameraFramePerpindicular(CamZ,CamX) )
951 return false;
952
953 m_CamX = CamX;
954 m_CamY = CamY;
955 m_CamZ = CamZ;
956
957 m_bValidCamera = m_CamLoc.IsValid();
958 return m_bValidCamera;
959 }
960
CameraLocation() const961 ON_3dPoint ON_Viewport::CameraLocation() const
962 {
963 return m_CamLoc;
964 }
965
CameraDirection() const966 ON_3dVector ON_Viewport::CameraDirection() const
967 {
968 return m_CamDir;
969 }
970
CameraUp() const971 ON_3dVector ON_Viewport::CameraUp() const
972 {
973 return m_CamUp;
974 }
975
CameraLocationIsLocked() const976 bool ON_Viewport::CameraLocationIsLocked() const
977 {
978 return m_bLockCamLoc;
979 }
980
CameraDirectionIsLocked() const981 bool ON_Viewport::CameraDirectionIsLocked() const
982 {
983 return m_bLockCamDir;
984 }
985
CameraUpIsLocked() const986 bool ON_Viewport::CameraUpIsLocked() const
987 {
988 return m_bLockCamUp;
989 }
990
FrustumIsLeftRightSymmetric() const991 bool ON_Viewport::FrustumIsLeftRightSymmetric() const
992 {
993 return (0 != (0x02 & m_frustum_symmetry_flags));
994 }
995
FrustumIsTopBottomSymmetric() const996 bool ON_Viewport::FrustumIsTopBottomSymmetric() const
997 {
998 return (0 != (0x01 & m_frustum_symmetry_flags));
999 }
1000
UnlockCamera()1001 void ON_Viewport::UnlockCamera()
1002 {
1003 SetCameraLocationLock(false);
1004 SetCameraDirectionLock(false);
1005 SetCameraUpLock(false);
1006 }
1007
UnlockFrustumSymmetry()1008 void ON_Viewport::UnlockFrustumSymmetry()
1009 {
1010 SetFrustumLeftRightSymmetry(false);
1011 SetFrustumTopBottomSymmetry(false);
1012 }
1013
SetCameraLocationLock(bool bLockCameraLocation)1014 void ON_Viewport::SetCameraLocationLock( bool bLockCameraLocation )
1015 {
1016 m_bLockCamLoc = bLockCameraLocation ? true : false;
1017 }
1018
SetCameraDirectionLock(bool bLockCameraDirection)1019 void ON_Viewport::SetCameraDirectionLock( bool bLockCameraDirection )
1020 {
1021 m_bLockCamDir = bLockCameraDirection ? true : false;
1022 }
1023
SetCameraUpLock(bool bLockCameraUp)1024 void ON_Viewport::SetCameraUpLock( bool bLockCameraUp )
1025 {
1026 m_bLockCamUp = bLockCameraUp ? true : false;
1027 }
1028
SetFrustumLeftRightSymmetry(bool bForceLeftRightSymmetry)1029 void ON_Viewport::SetFrustumLeftRightSymmetry( bool bForceLeftRightSymmetry )
1030 {
1031 if ( bForceLeftRightSymmetry )
1032 m_frustum_symmetry_flags |= 0x02; // set bit 2
1033 else
1034 m_frustum_symmetry_flags &= 0xFD; // clear bit 2
1035 }
1036
SetFrustumTopBottomSymmetry(bool bForceTopBottomSymmetry)1037 void ON_Viewport::SetFrustumTopBottomSymmetry( bool bForceTopBottomSymmetry )
1038 {
1039 if ( bForceTopBottomSymmetry )
1040 m_frustum_symmetry_flags |= 0x01; // set bit 1
1041 else
1042 m_frustum_symmetry_flags &= 0xFE; // clear bit 1
1043 }
1044
1045
1046
GetDollyCameraVector(int x0,int y0,int x1,int y1,double distance_to_camera,ON_3dVector & dolly_vector) const1047 bool ON_Viewport::GetDollyCameraVector(
1048 int x0, int y0, // (x,y) screen coords of start point
1049 int x1, int y1, // (x,y) screen coords of end point
1050 double distance_to_camera, // distance from camera
1051 ON_3dVector& dolly_vector// dolly vector returned here
1052 ) const
1053 {
1054 int port_left, port_right, port_bottom, port_top;
1055 ON_Xform c2w;
1056 dolly_vector.Zero();
1057 bool rc = GetScreenPort( &port_left, &port_right, &port_bottom, &port_top );
1058 if ( rc )
1059 rc = GetXform( ON::clip_cs, ON::world_cs, c2w );
1060 if ( rc ) {
1061 double dx = 0.5*(port_right - port_left);
1062 double dy = 0.5*(port_top - port_bottom);
1063 double dz = 0.5*(FrustumFar() - FrustumNear());
1064 if ( dx == 0.0 || dy == 0.0 || dz == 0.0 )
1065 rc = false;
1066 else {
1067 double z = (distance_to_camera - FrustumNear())/dz - 1.0;
1068 ON_3dPoint c0( (x0-port_left)/dx-1.0, (y0-port_bottom)/dy-1.0, z );
1069 ON_3dPoint c1( (x1-port_left)/dx-1.0, (y1-port_bottom)/dy-1.0, z );
1070 ON_3dPoint w0 = c2w*c0;
1071 ON_3dPoint w1 = c2w*c1;
1072 dolly_vector = w0 - w1;
1073 }
1074 }
1075 return rc;
1076 }
1077
DollyCamera(const ON_3dVector & dolly)1078 bool ON_Viewport::DollyCamera( const ON_3dVector& dolly )
1079 {
1080 bool rc = false;
1081 if ( m_CamLoc.IsValid() && dolly.IsValid() )
1082 {
1083 m_CamLoc += dolly;
1084 rc = m_bValidCamera;
1085 }
1086 return rc;
1087 }
1088
DollyFrustum(double dollyDistance)1089 bool ON_Viewport::DollyFrustum( double dollyDistance )
1090 {
1091 bool rc = false;
1092 double new_near, new_far, scale;
1093 if ( m_bValidFrustum )
1094 {
1095 new_near = m_frus_near + dollyDistance;
1096 new_far = m_frus_far + dollyDistance;
1097 if ( IsPerspectiveProjection() && new_near < m__MIN_NEAR_DIST )
1098 {
1099 new_near = m__MIN_NEAR_DIST;
1100 }
1101 scale = ( IsPerspectiveProjection() )
1102 ? new_near/m_frus_near
1103 : 1.0;
1104 if ( new_near > 0.0 && new_far > new_near && scale > 0.0 )
1105 {
1106 m_frus_near = new_near;
1107 m_frus_far = new_far;
1108 m_frus_left *= scale;
1109 m_frus_right *= scale;
1110 m_frus_top *= scale;
1111 m_frus_bottom *= scale;
1112 rc = true;
1113 }
1114 }
1115 return rc;
1116 }
1117
GetCameraFrame(double * CameraLocation,double * CameraX,double * CameraY,double * CameraZ) const1118 bool ON_Viewport::GetCameraFrame(
1119 double* CameraLocation,
1120 double* CameraX,
1121 double* CameraY,
1122 double* CameraZ
1123 ) const
1124 {
1125 if ( CameraLocation ) {
1126 CameraLocation[0] = m_CamLoc.x;
1127 CameraLocation[1] = m_CamLoc.y;
1128 CameraLocation[2] = m_CamLoc.z;
1129 }
1130 if ( CameraX ) {
1131 CameraX[0] = m_CamX.x;
1132 CameraX[1] = m_CamX.y;
1133 CameraX[2] = m_CamX.z;
1134 }
1135 if ( CameraY ) {
1136 CameraY[0] = m_CamY.x;
1137 CameraY[1] = m_CamY.y;
1138 CameraY[2] = m_CamY.z;
1139 }
1140 if ( CameraZ ) {
1141 CameraZ[0] = m_CamZ.x;
1142 CameraZ[1] = m_CamZ.y;
1143 CameraZ[2] = m_CamZ.z;
1144 }
1145 return m_bValidCamera;
1146 }
1147
CameraX() const1148 ON_3dVector ON_Viewport::CameraX() const
1149 {
1150 return m_CamX;
1151 }
1152
CameraY() const1153 ON_3dVector ON_Viewport::CameraY() const
1154 {
1155 return m_CamY;
1156 }
1157
CameraZ() const1158 ON_3dVector ON_Viewport::CameraZ() const
1159 {
1160 return m_CamZ;
1161 }
1162
IsCameraFrameWorldPlan(int * xindex,int * yindex,int * zindex)1163 bool ON_Viewport::IsCameraFrameWorldPlan(
1164 int* xindex,
1165 int* yindex,
1166 int* zindex
1167 )
1168 {
1169 int i;
1170 int ix = 0;
1171 int iy = 0;
1172 int iz = 0;
1173 double X[3], Y[3], Z[3];
1174 bool rc = GetCameraFrame( NULL, X, Y, Z );
1175 if ( rc ) {
1176 for ( i = 0; i < 3; i++ ) {
1177 if ( X[i] == 1.0 ) {
1178 ix = i+1;
1179 break;
1180 }
1181 if ( X[i] == -1.0 ) {
1182 ix = -(i+1);
1183 break;
1184 }
1185 }
1186 for ( i = 0; i < 3; i++ ) {
1187 if ( Y[i] == 1.0 ) {
1188 iy = i+1;
1189 break;
1190 }
1191 if ( Y[i] == -1.0 ) {
1192 iy = -(i+1);
1193 break;
1194 }
1195 }
1196 for ( i = 0; i < 3; i++ ) {
1197 if ( Z[i] == 1.0 ) {
1198 iz = i+1;
1199 break;
1200 }
1201 if ( Z[i] == -1.0 ) {
1202 iz = -(i+1);
1203 break;
1204 }
1205 }
1206 rc = ( iz != 0 ) ? 1 : 0;
1207 }
1208 if ( xindex ) *xindex = ix;
1209 if ( yindex ) *yindex = iy;
1210 if ( zindex ) *zindex = iz;
1211 return rc;
1212 }
1213
1214
GetCameraExtents(int count,int stride,const double * points,ON_BoundingBox & cbox,int bGrowBox) const1215 bool ON_Viewport::GetCameraExtents(
1216 // returns bounding box in camera coordinates - this is useful information
1217 // for setting view frustrums to include the point list
1218 int count, // count = number of 3d points
1219 int stride, // stride = number of doubles to skip between points (>=3)
1220 const double* points, // 3d points in world coordinates
1221 ON_BoundingBox& cbox, // bounding box in camera coordinates
1222 int bGrowBox // set to true to grow existing box
1223 ) const
1224 {
1225 ON_Xform w2c;
1226 bool rc = bGrowBox?true:false;
1227 int i;
1228 if ( count > 0 && stride >= 3 && points != NULL ) {
1229 rc = false;
1230 if ( GetXform( ON::world_cs, ON::camera_cs, w2c ) ) {
1231 rc = true;
1232 for ( i = 0; i < count && rc; i++, points += stride ) {
1233 rc = cbox.Set( w2c*ON_3dPoint(points), bGrowBox );
1234 bGrowBox = true;
1235 }
1236 }
1237 }
1238 return rc;
1239 }
1240
GetCameraExtents(const ON_BoundingBox & wbox,ON_BoundingBox & cbox,int bGrowBox) const1241 bool ON_Viewport::GetCameraExtents(
1242 // returns bounding box in camera coordinates - this is useful information
1243 // for setting view frustrums to include the point list
1244 const ON_BoundingBox& wbox, // world coordinate bounding box
1245 ON_BoundingBox& cbox, // bounding box in camera coordinates
1246 int bGrowBox // set to true to grow existing box
1247 ) const
1248 {
1249 bool rc = false;
1250 ON_3dPointArray corners;
1251 if ( wbox.GetCorners( corners ) ) {
1252 rc = GetCameraExtents( corners.Count(), 3, &corners.Array()[0].x, cbox, bGrowBox );
1253 }
1254 return rc;
1255 }
1256
GetCameraExtents(ON_3dPoint & worldSphereCenter,double worldSphereRadius,ON_BoundingBox & cbox,int bGrowBox) const1257 bool ON_Viewport::GetCameraExtents(
1258 // returns bounding box in camera coordinates - this is useful information
1259 // for setting view frustrums to include the point list
1260 ON_3dPoint& worldSphereCenter,
1261 double worldSphereRadius,
1262 ON_BoundingBox& cbox, // bounding box in camera coordinates
1263 int bGrowBox // set to true to grow existing box
1264 ) const
1265 {
1266 bool rc = false;
1267 ON_BoundingBox sbox;
1268 if ( GetCameraExtents( 1, 3, &worldSphereCenter.x, sbox, false ) ) {
1269 const double r = fabs( worldSphereRadius );
1270 sbox[0][0] -= r;
1271 sbox[0][1] -= r;
1272 sbox[0][2] -= r;
1273 sbox[1][0] += r;
1274 sbox[1][1] += r;
1275 sbox[1][2] += r;
1276 if ( bGrowBox )
1277 cbox.Union( sbox );
1278 else
1279 cbox = sbox;
1280 rc = true;
1281 }
1282 return rc;
1283 }
1284
1285
UpdateTargetPointHelper(ON_Viewport & vp,double target_distance)1286 static void UpdateTargetPointHelper( ON_Viewport& vp, double target_distance )
1287 {
1288 if ( !vp.IsValidCamera() || !vp.IsValidFrustum() )
1289 return;
1290 if ( !ON_IsValid(target_distance) || target_distance <= 0.0 )
1291 return;
1292
1293 ON_3dPoint old_tp = vp.TargetPoint();
1294
1295 // Put the target directly in front of the camera.
1296 // The target_tol test is here to avoid making insignificant
1297 // changes that appear in the user interface and upset users
1298 // who find 1.00000000001 to be grossly different from 1.0.
1299 double target_tol = 1.0e-5*(vp.FrustumWidth()+vp.FrustumHeight())
1300 + ON_ZERO_TOLERANCE;
1301 ON_3dPoint new_tp = vp.CameraLocation() - target_distance*vp.CameraZ();
1302 if ( new_tp.IsValid()
1303 && (!old_tp.IsValid() || new_tp.DistanceTo(old_tp) > target_tol)
1304 )
1305 {
1306 vp.SetTargetPoint(new_tp);
1307 }
1308 }
1309
ChangeToParallelProjection(bool bSymmetricFrustum)1310 bool ON_Viewport::ChangeToParallelProjection( bool bSymmetricFrustum )
1311 {
1312 bool rc = (m_bValidCamera && m_bValidFrustum);
1313
1314 SetCameraUpLock(false);
1315 SetCameraDirectionLock(false);
1316
1317 if ( ON::parallel_view == m_projection
1318 && (bSymmetricFrustum?true:false) == FrustumIsLeftRightSymmetric()
1319 && (bSymmetricFrustum?true:false) == FrustumIsTopBottomSymmetric()
1320 )
1321 {
1322 // no changes are required
1323 return rc;
1324 }
1325
1326 // if needed, make frustum symmetric
1327 // If bSymmetricFrustum is true and the input frustum is not symmetric,
1328 // then this will dolly the camera location.
1329 ChangeToSymmetricFrustum(bSymmetricFrustum,bSymmetricFrustum,ON_UNSET_VALUE);
1330 SetFrustumTopBottomSymmetry(bSymmetricFrustum);
1331 SetFrustumLeftRightSymmetry(bSymmetricFrustum);
1332
1333 const ON::view_projection old_projection = m_projection;
1334 double target_distance = TargetDistance(true);
1335 if ( !ON_IsValid(target_distance)
1336 || !m_bValidFrustum
1337 || !ON_IsValid(m_frus_near)
1338 || m_frus_near <= 0.0
1339 || target_distance <= m_frus_near
1340 )
1341 {
1342 target_distance = 0.0; // makes it easier to check for valid target distance
1343 }
1344
1345 // if needed change projection
1346 if ( ON::parallel_view != old_projection )
1347 {
1348 if ( !SetProjection(ON::parallel_view) )
1349 rc = false;
1350 }
1351
1352 if ( rc )
1353 {
1354 if ( ON::perspective_view == old_projection )
1355 {
1356 // change from a perspective to a parallel projection
1357 if ( target_distance > 0.0 && 0.0 < m_frus_near && m_frus_near < m_frus_far )
1358 {
1359 // Update the frustum so that the plane through the target point
1360 // is the one that is parallel projected. This is generally
1361 // the best choice when switching from perspective to
1362 // parallel projection. If needed, SetFrustum() will make the
1363 // frustum symmetric
1364 double s = target_distance/m_frus_near;
1365 double l = m_frus_left*s;
1366 double r = m_frus_right*s;
1367 double t = m_frus_top*s;
1368 double b = m_frus_bottom*s;
1369 if ( !SetFrustum( l, r, b, t, m_frus_near, m_frus_far ))
1370 rc = false;
1371 }
1372 }
1373 if ( m_target_point.IsValid() )
1374 UpdateTargetPointHelper(*this,target_distance);
1375 }
1376
1377 return rc;
1378 }
1379
ChangeFromParallelToPerspectiveHelper(ON_Viewport & vp,double target_distance,double lens_length)1380 static bool ChangeFromParallelToPerspectiveHelper( ON_Viewport& vp, double target_distance, double lens_length )
1381 {
1382 // helper use by ChangeToPerspectiveProjection() and ChangeToTwoPointPerspectiveProjection()
1383 if ( ON::perspective_view == vp.Projection() )
1384 return true;
1385
1386 if ( !vp.SetProjection(ON::perspective_view) )
1387 return false;
1388
1389 // change from a parallel to a perspective projection
1390 double frus_left,frus_right,frus_bottom,frus_top,frus_near,frus_far;
1391 if ( !vp.GetFrustum(&frus_left,&frus_right,&frus_bottom,&frus_top,&frus_near,&frus_far) )
1392 return false;
1393
1394 // Using width because it works for both two point and ordinary perspective
1395 const double width = fabs(frus_right - frus_left);
1396 const ON_3dPoint width_point = ( ON_IsValid(target_distance) && target_distance > 0.0)
1397 ? vp.CameraLocation() - target_distance*vp.CameraZ()
1398 : ON_3dPoint::UnsetPoint;
1399
1400 if ( frus_near < 1.0e-8 && frus_far >= 1.0e-7)
1401 {
1402 frus_near = 1.0e-8;
1403 vp.SetFrustum(frus_left,frus_right,frus_bottom,frus_top,frus_near,frus_far);
1404 vp.GetFrustum(&frus_left,&frus_right,&frus_bottom,&frus_top,&frus_near,&frus_far);
1405 }
1406
1407 bool rc = false;
1408
1409 if ( ON_IsValid(lens_length) && lens_length > 0.0 )
1410 {
1411 rc = vp.SetCamera35mmLensLength(lens_length);
1412 if ( rc
1413 && width_point.IsValid()
1414 && !vp.CameraLocationIsLocked()
1415 && vp.GetFrustum(&frus_left,&frus_right,&frus_bottom,&frus_top,&frus_near,&frus_far)
1416 && frus_near > 0.0
1417 )
1418 {
1419 double d = (vp.CameraLocation() - width_point)*vp.CameraZ();
1420 if ( d > frus_near )
1421 {
1422 // make sure target plane is visible
1423 double w = fabs(frus_right - frus_left)*d/frus_near;
1424 if ( width > w && w > 0.0 )
1425 {
1426 // move camera back to increase "w" back up to "width"
1427 ON_3dPoint cam_loc0 = vp.CameraLocation();
1428 double dz = d*(width/w - 1.0);
1429 ON_3dPoint cam_loc1 = cam_loc0 + dz*vp.CameraZ();
1430 vp.SetCameraLocation(cam_loc1);
1431 }
1432 }
1433 }
1434 }
1435 return rc;
1436 }
1437
ChangeToPerspectiveProjection(double target_distance,bool bSymmetricFrustum,double lens_length)1438 bool ON_Viewport::ChangeToPerspectiveProjection(
1439 double target_distance,
1440 bool bSymmetricFrustum,
1441 double lens_length
1442 )
1443 {
1444 bool rc = (m_bValidCamera && m_bValidFrustum);
1445
1446 SetCameraUpLock(false);
1447 SetCameraDirectionLock(false);
1448
1449 if ( ON::perspective_view == m_projection
1450 && (bSymmetricFrustum?true:false) == FrustumIsLeftRightSymmetric()
1451 && (bSymmetricFrustum?true:false) == FrustumIsTopBottomSymmetric()
1452 )
1453 {
1454 double current_lens_length = lens_length;
1455 if ( ON_IsValid(lens_length)
1456 && lens_length > 0.0
1457 && GetCamera35mmLensLength(¤t_lens_length)
1458 && fabs(current_lens_length - lens_length) > 0.125
1459 )
1460 {
1461 SetCamera35mmLensLength(lens_length);
1462 }
1463 // no other changes are required
1464 return rc;
1465 }
1466
1467 if ( !ON_IsValid(target_distance) || target_distance <= 0.0 )
1468 target_distance = TargetDistance(true);
1469
1470 // If needed, make frustum symmetric. This may move the
1471 // camera location in a direction perpendicular to m_CamZ.
1472 ChangeToSymmetricFrustum(bSymmetricFrustum,bSymmetricFrustum,target_distance);
1473 SetFrustumTopBottomSymmetry(bSymmetricFrustum);
1474 SetFrustumLeftRightSymmetry(bSymmetricFrustum);
1475
1476 // If needed change projection to perspective. If
1477 // the input projection is parallel, this may move
1478 // the camera in the m_CamZ direction to preserve
1479 // viewing the target plane.
1480 if (!ChangeFromParallelToPerspectiveHelper(*this,target_distance,lens_length))
1481 rc = false;
1482
1483 if ( rc && m_target_point.IsValid() )
1484 UpdateTargetPointHelper(*this,target_distance);
1485
1486 return rc;
1487 }
1488
1489 static
GetTwoPointPerspectiveUpAndDirHelper(const ON_3dVector & up,const ON_3dVector & CamDir,const ON_3dVector & CamY,const ON_3dVector & CamZ,ON_3dVector & new_up,ON_3dVector & new_dir)1490 bool GetTwoPointPerspectiveUpAndDirHelper( const ON_3dVector& up,
1491 const ON_3dVector& CamDir,
1492 const ON_3dVector& CamY,
1493 const ON_3dVector& CamZ,
1494 ON_3dVector& new_up,
1495 ON_3dVector& new_dir
1496 )
1497 {
1498 // get up direction
1499 ON_3dVector unit_up;
1500 ON_3dVector unit_dir;
1501 if ( up.IsZero() && CamY.IsValid() && CamY.IsUnitVector() )
1502 {
1503 new_up = CamY;
1504 if ( fabs(new_up.z) >= fabs(new_up.y) && fabs(new_up.z) >= fabs(new_up.x) )
1505 new_up.Set(0.0,0.0,new_up.z<0.0?-1.0:1.0);
1506 else if ( fabs(new_up.y) >= fabs(new_up.z) && fabs(new_up.y) >= fabs(new_up.x) )
1507 new_up.Set(0.0,new_up.y<0.0?-1.0:1.0,0.0);
1508 else
1509 new_up.Set(new_up.x<0.0?-1.0:1.0,0.0,0.0);
1510 unit_up = new_up;
1511 }
1512 else if ( up.IsValid() && !up.IsTiny() )
1513 {
1514 unit_up = up;
1515 if ( !unit_up.IsUnitVector() && !unit_up.Unitize() )
1516 return false;
1517 new_up = up;
1518 }
1519 else
1520 {
1521 return false;
1522 }
1523
1524 // get camera dir
1525 if ( CamDir.IsValid() && !CamDir.IsTiny() )
1526 {
1527 new_dir = CamDir;
1528 unit_dir = new_dir;
1529 if ( unit_dir.Unitize() && ON__IsCameraFramePerpindicular(unit_up,unit_dir) )
1530 return true;
1531 unit_dir = unit_dir - (unit_dir*unit_up)*unit_up;
1532 if ( unit_dir.IsValid() && !unit_dir.IsTiny() && unit_dir.Unitize() )
1533 {
1534 new_dir = unit_dir;
1535 return true;
1536 }
1537 }
1538
1539 if ( CamZ.IsValid() && CamZ.IsUnitVector() )
1540 {
1541 new_dir = -CamZ;
1542 unit_dir = new_dir;
1543 if ( unit_dir.Unitize() && ON__IsCameraFramePerpindicular(unit_up,unit_dir) )
1544 return true;
1545 unit_dir = unit_dir - (new_dir*unit_up)*unit_up;
1546 if ( unit_dir.IsValid() && !unit_dir.IsTiny() && unit_dir.Unitize() )
1547 {
1548 new_dir = unit_dir;
1549 return true;
1550 }
1551 }
1552
1553 return false;
1554 }
1555
ChangeToTwoPointPerspectiveProjection(double target_distance,ON_3dVector up,double lens_length)1556 bool ON_Viewport::ChangeToTwoPointPerspectiveProjection(
1557 double target_distance,
1558 ON_3dVector up,
1559 double lens_length
1560 )
1561 {
1562 bool rc = (m_bValidCamera && m_bValidFrustum);
1563
1564 SetCameraDirectionLock(false);
1565
1566 if ( IsTwoPointPerspectiveProjection() )
1567 {
1568 double current_lens_length = lens_length;
1569 if ( ON_IsValid(lens_length)
1570 && lens_length > 0.0
1571 && GetCamera35mmLensLength(¤t_lens_length)
1572 && fabs(current_lens_length - lens_length) > 0.125
1573 )
1574 {
1575 SetCamera35mmLensLength(lens_length);
1576 }
1577 // no other changes are required
1578 return rc;
1579 }
1580
1581 if ( !ON_IsValid(target_distance) || target_distance <= 0.0 )
1582 target_distance = TargetDistance(true);
1583
1584 // if needed, make frustum left/right symmetric. This may move the
1585 // camera location in a direction perpendicular to m_CamZ.
1586 ChangeToSymmetricFrustum(true,false,target_distance);
1587 SetFrustumLeftRightSymmetry(true);
1588 SetFrustumTopBottomSymmetry(false);
1589
1590 // If needed change projection to perspective. If
1591 // the input projection is parallel, this may move
1592 // the camera in the m_CamZ direction to preserve
1593 // viewing the target plane.
1594 if (!ChangeFromParallelToPerspectiveHelper(*this,target_distance,lens_length))
1595 rc = false;
1596
1597 if ( rc )
1598 {
1599 ON_3dVector new_up = m_CamY;
1600 ON_3dVector new_dir = -m_CamZ;
1601 ON_3dPoint new_loc = m_CamLoc;
1602 if ( !GetTwoPointPerspectiveUpAndDirHelper(up,m_CamDir,m_CamY,m_CamZ,new_up,new_dir) )
1603 {
1604 rc = false;
1605 }
1606 else
1607 {
1608 // move location so the stuff that is currently visible
1609 // tends to end up in someplace in the new frustum.
1610 ON_3dPoint center_point = FrustumCenterPoint(target_distance);
1611 if ( center_point.IsValid() && (new_loc-center_point)*m_CamZ > 0.0 )
1612 {
1613 ON_Xform rot;
1614 rot.Rotation(m_CamY,new_up,center_point);
1615 new_loc = rot*m_CamLoc;
1616 if ( !new_loc.IsValid() )
1617 new_loc = m_CamLoc;
1618 }
1619
1620 ON_3dVector saved_up = m_CamUp;
1621 ON_3dVector saved_dir = m_CamDir;
1622 bool bSavedLockCamUp = m_bLockCamUp;
1623 m_CamUp = new_up; // intentionally ignoring m_bLockCamUp
1624 m_CamDir = new_dir; // intentionally ignoring m_bLockDirUp
1625 SetCameraUpLock(true);
1626 if ( !SetCameraFrame() )
1627 {
1628 rc = false;
1629 m_CamUp = saved_up;
1630 m_CamDir = saved_dir;
1631 m_bLockCamUp = bSavedLockCamUp;
1632 }
1633 SetCameraLocation(new_loc);
1634
1635 UpdateTargetPointHelper(*this,target_distance);
1636 }
1637
1638 }
1639
1640 return rc;
1641 }
1642
SetProjection(ON::view_projection projection)1643 bool ON_Viewport::SetProjection( ON::view_projection projection )
1644 {
1645 // Debugging projection changes is easier if we
1646 // do this initial check.
1647 if ( projection == m_projection )
1648 return true;
1649
1650 bool rc = false;
1651 if ( projection == ON::perspective_view )
1652 {
1653 rc = true;
1654 m_projection = ON::perspective_view;
1655 }
1656 else
1657 {
1658 rc = (projection == ON::parallel_view);
1659 m_projection = ON::parallel_view;
1660 }
1661
1662 return rc;
1663 }
1664
Projection() const1665 ON::view_projection ON_Viewport::Projection() const
1666 {
1667 return m_projection;
1668 }
1669
IsParallelProjection() const1670 bool ON_Viewport::IsParallelProjection() const
1671 {
1672 return ( ON::parallel_view == m_projection );
1673 }
1674
IsPerspectiveProjection() const1675 bool ON_Viewport::IsPerspectiveProjection() const
1676 {
1677 return ( ON::perspective_view == m_projection );
1678 }
1679
IsTwoPointPerspectiveProjection() const1680 bool ON_Viewport::IsTwoPointPerspectiveProjection() const
1681 {
1682 bool rc = IsPerspectiveProjection()
1683 && CameraUpIsLocked()
1684 && FrustumIsLeftRightSymmetric()
1685 && !FrustumIsTopBottomSymmetric();
1686 return rc;
1687 }
1688
SetFrustum(double frus_left,double frus_right,double frus_bottom,double frus_top,double frus_near,double frus_far)1689 bool ON_Viewport::SetFrustum(
1690 double frus_left,
1691 double frus_right,
1692 double frus_bottom,
1693 double frus_top,
1694 double frus_near,
1695 double frus_far
1696 )
1697 {
1698 bool rc = false;
1699 if (
1700 ON_IsValid(frus_left)
1701 && ON_IsValid(frus_right)
1702 && ON_IsValid(frus_top)
1703 && ON_IsValid(frus_bottom)
1704 && ON_IsValid(frus_near)
1705 && ON_IsValid(frus_far)
1706 && frus_left < frus_right
1707 && frus_bottom < frus_top
1708 && 0.0 < frus_near
1709 && frus_near < frus_far
1710 )
1711 {
1712 if ( IsPerspectiveProjection()
1713 && (frus_near <= 1.0e-8 || frus_far > 1.0001e8*frus_near)
1714 )
1715 {
1716 ON_ERROR("ON_Viewport::SetFrustum - Beyond float precision perspective frus_near/frus_far values - will crash MS OpenGL");
1717 }
1718
1719 if ( FrustumIsLeftRightSymmetric() && frus_left != -frus_right )
1720 {
1721 double d = 0.5*(frus_right-frus_left);
1722 frus_right = d;
1723 frus_left = -d;
1724 }
1725
1726 if ( FrustumIsTopBottomSymmetric() && frus_bottom != -frus_top )
1727 {
1728 double d = 0.5*(frus_top-frus_bottom);
1729 frus_top = d;
1730 frus_bottom = -d;
1731 }
1732
1733 m_frus_left = frus_left;
1734 m_frus_right = frus_right;
1735 m_frus_bottom = frus_bottom;
1736 m_frus_top = frus_top;
1737 m_frus_near = frus_near;
1738 m_frus_far = frus_far;
1739 m_bValidFrustum = true;
1740 rc = true;
1741 }
1742 else
1743 {
1744 // 17 March 2008 Dale Lear
1745 // I added this to trap the bug that is creating
1746 // invalid viewports. Developers: If you ever
1747 // get this error, immediately investigate it.
1748 ON_ERROR("ON_Viewport::SetFrustum - invalid input");
1749 }
1750 return rc;
1751 }
1752
1753
GetFrustum(double * frus_left,double * frus_right,double * frus_bottom,double * frus_top,double * frus_near,double * frus_far) const1754 bool ON_Viewport::GetFrustum(
1755 double* frus_left,
1756 double* frus_right,
1757 double* frus_bottom,
1758 double* frus_top,
1759 double* frus_near, // = NULL
1760 double* frus_far // = NULL
1761 ) const
1762 {
1763 if ( frus_left )
1764 *frus_left = m_frus_left;
1765 if ( frus_right )
1766 *frus_right = m_frus_right;
1767 if ( frus_bottom )
1768 *frus_bottom = m_frus_bottom;
1769 if ( frus_top )
1770 *frus_top = m_frus_top;
1771 if ( frus_near )
1772 *frus_near = m_frus_near;
1773 if ( frus_far )
1774 *frus_far = m_frus_far;
1775 return m_bValidFrustum;
1776 }
1777
FrustumLeft() const1778 double ON_Viewport::FrustumLeft() const { return m_frus_left; }
FrustumRight() const1779 double ON_Viewport::FrustumRight() const { return m_frus_right; }
FrustumBottom() const1780 double ON_Viewport::FrustumBottom() const { return m_frus_bottom; }
FrustumTop() const1781 double ON_Viewport::FrustumTop() const { return m_frus_top; }
FrustumNear() const1782 double ON_Viewport::FrustumNear() const { return m_frus_near; }
FrustumFar() const1783 double ON_Viewport::FrustumFar() const { return m_frus_far; }
FrustumWidth() const1784 double ON_Viewport::FrustumWidth() const { return m_frus_right-m_frus_left; }
FrustumHeight() const1785 double ON_Viewport::FrustumHeight() const { return m_frus_top-m_frus_bottom; }
1786
FrustumMinimumDiameter() const1787 double ON_Viewport::FrustumMinimumDiameter() const
1788 {
1789 double w = fabs(m_frus_right-m_frus_left);
1790 double h = fabs(m_frus_top-m_frus_bottom);
1791 return (w<=h)?w:h;
1792 }
1793
FrustumMaximumDiameter() const1794 double ON_Viewport::FrustumMaximumDiameter() const
1795 {
1796 double w = fabs(m_frus_right-m_frus_left);
1797 double h = fabs(m_frus_top-m_frus_bottom);
1798 return (w<=h)?w:h;
1799 }
1800
1801
SetFrustumAspect(double frustum_aspect)1802 bool ON_Viewport::SetFrustumAspect( double frustum_aspect )
1803 {
1804 // maintains camera angle
1805 bool rc = false;
1806 double w, h, d, left, right, bot, top, near_dist, far_dist;
1807 if ( frustum_aspect > 0.0 && GetFrustum( &left, &right, &bot, &top, &near_dist, &far_dist ) ) {
1808 w = right - left;
1809 h = top - bot;
1810 if ( fabs(h) > fabs(w) ) {
1811 d = (h>=0.0) ? fabs(w) : -fabs(w);
1812 d *= 0.5;
1813 h = 0.5*(top+bot);
1814 bot = h-d;
1815 top = h+d;
1816 h = top - bot;
1817 }
1818 else {
1819 d = (w>=0.0) ? fabs(h) : -fabs(h);
1820 d *= 0.5;
1821 w = 0.5*(left+right);
1822 left = w-d;
1823 right = w+d;
1824 w = right - left;
1825 }
1826 if ( frustum_aspect > 1.0 ) {
1827 // increase width
1828 d = 0.5*w*frustum_aspect;
1829 w = 0.5*(left+right);
1830 left = w-d;
1831 right = w+d;
1832 w = right - left;
1833 }
1834 else if ( frustum_aspect < 1.0 ) {
1835 // increase height
1836 d = 0.5*h/frustum_aspect;
1837 h = 0.5*(bot+top);
1838 bot = h-d;
1839 top = h+d;
1840 h = top - bot;
1841 }
1842 rc = SetFrustum( left, right, bot, top, near_dist, far_dist );
1843 }
1844 return rc;
1845 }
1846
1847
1848
GetFrustumAspect(double & frustum_aspect) const1849 bool ON_Viewport::GetFrustumAspect( double& frustum_aspect ) const
1850 {
1851 // frustum_aspect = frustum width / frustum height
1852 double w, h, left, right, bot, top;
1853 bool rc = m_bValidFrustum;
1854 frustum_aspect = 0.0;
1855
1856 if ( GetFrustum( &left, &right, &bot, &top ) ) {
1857 w = right - left;
1858 h = top - bot;
1859 if ( h == 0.0 )
1860 rc = false;
1861 else
1862 frustum_aspect = w/h;
1863 }
1864 return rc;
1865 }
1866
GetFrustumCenter(double * frus_center) const1867 bool ON_Viewport::GetFrustumCenter( double* frus_center ) const
1868 {
1869 double camZ[3], frus_near, frus_far, d;
1870 if ( !frus_center )
1871 return false;
1872 if ( !GetCameraFrame( frus_center, NULL, NULL, camZ ) )
1873 return false;
1874 if ( !GetFrustum( NULL, NULL, NULL, NULL, &frus_near, &frus_far ) )
1875 return false;
1876 d = -0.5*(frus_near+frus_far);
1877 frus_center[0] += d*camZ[0];
1878 frus_center[1] += d*camZ[1];
1879 frus_center[2] += d*camZ[2];
1880 return true;
1881 }
1882
SetScreenPort(int port_left,int port_right,int port_bottom,int port_top,int port_near,int port_far)1883 bool ON_Viewport::SetScreenPort(
1884 int port_left,
1885 int port_right,
1886 int port_bottom,
1887 int port_top,
1888 int port_near, // = 0
1889 int port_far // = 0
1890 )
1891 {
1892 if ( port_left == port_right )
1893 return false;
1894 if ( port_bottom == port_top )
1895 return false;
1896 m_port_left = port_left;
1897 m_port_right = port_right;
1898 m_port_bottom = port_bottom;
1899 m_port_top = port_top;
1900 if ( port_near || port_near != port_far )
1901 {
1902 m_port_near = port_near;
1903 m_port_far = port_far;
1904 }
1905 m_bValidPort = true;
1906 return m_bValidPort;
1907 }
1908
GetScreenPort(int * port_left,int * port_right,int * port_bottom,int * port_top,int * port_near,int * port_far) const1909 bool ON_Viewport::GetScreenPort(
1910 int* port_left,
1911 int* port_right,
1912 int* port_bottom,
1913 int* port_top,
1914 int* port_near, // = NULL
1915 int* port_far // = NULL
1916 ) const
1917 {
1918 if ( port_left )
1919 *port_left = m_port_left;
1920 if ( port_right )
1921 *port_right = m_port_right;
1922 if ( port_bottom )
1923 *port_bottom = m_port_bottom;
1924 if ( port_top )
1925 *port_top = m_port_top;
1926 if ( port_near )
1927 *port_near = m_port_near;
1928 if ( port_far )
1929 *port_far = m_port_far;
1930 return m_bValidPort;
1931 }
1932
ScreenPortWidth() const1933 int ON_Viewport::ScreenPortWidth() const
1934 {
1935 int width = m_port_right - m_port_left;
1936 return width >= 0 ? width : -width;
1937 }
1938
ScreenPortHeight() const1939 int ON_Viewport::ScreenPortHeight() const
1940 {
1941 int height = m_port_top - m_port_bottom;
1942 return height >= 0 ? height : -height;
1943 }
1944
GetScreenPortAspect(double & aspect) const1945 bool ON_Viewport::GetScreenPortAspect(double& aspect) const
1946 {
1947 const double width = m_port_right - m_port_left;
1948 const double height = m_port_top - m_port_bottom;
1949 aspect = ( m_bValidPort && ON_IsValid(height) && ON_IsValid(width) && height != 0.0 )
1950 ? fabs(width/height)
1951 : 0.0;
1952 return (m_bValidPort && aspect != 0.0);
1953 }
1954
ON_ViewportFromRhinoView(ON::view_projection projection,const ON_3dPoint & rhvp_target,double rhvp_angle1,double rhvp_angle2,double rhvp_angle3,double rhvp_viewsize,double rhvp_cameradist,int screen_width,int screen_height,ON_Viewport & vp)1955 bool ON_ViewportFromRhinoView(
1956 ON::view_projection projection,
1957 const ON_3dPoint& rhvp_target, // 3d point
1958 double rhvp_angle1, double rhvp_angle2, double rhvp_angle3, // radians
1959 double rhvp_viewsize, // > 0
1960 double rhvp_cameradist, // > 0
1961 int screen_width, int screen_height,
1962 ON_Viewport& vp
1963 )
1964 /*****************************************************************************
1965 Compute canonical view projection information from Rhino viewport settings
1966 INPUT:
1967 projection
1968 rhvp_target
1969 Rhino viewport target point (3d point that is center of view rotations)
1970 rhvp_angle1, rhvp_angle2, rhvp_angle3
1971 Rhino viewport angle settings
1972 rhvp_viewsize
1973 In perspective, rhvp_viewsize = tangent(half lens angle).
1974 In parallel, rhvp_viewsize = 1/2 * minimum(frustum width,frustum height)
1975 rhvp_cameradistance ( > 0 )
1976 Distance from camera location to Rhino's "target" point
1977 screen_width, screen_height (0,0) if not known
1978 *****************************************************************************/
1979 {
1980 vp.SetProjection( projection );
1981 /*
1982 width, height
1983 width and height of viewport
1984 ( = RhinoViewport->width, RhinoViewport->height )
1985 z_buffer_depth
1986 depth for the z buffer. 0xFFFF is currently used for Rhino
1987 quick rendering.
1988 */
1989
1990 // In the situation where there is no physical display device, assume a
1991 // 1000 x 1000 "screen" and set the parameters accordingly. Toolkit users
1992 // that are using this class to actually draw a picture, can make a subsequent
1993 // call to SetScreenPort().
1994
1995 const double height = (screen_width < 1 || screen_height < 1)
1996 ? 1000.0 : (double)screen_height;
1997 const double width = (screen_width < 1 || screen_height < 1)
1998 ? 1000.0 : (double)screen_width;
1999 //const int z_buffer_depth = 0xFFFF; // value Rhino "Shade" command uses
2000
2001 // Use this function to obtain standard view information from a Rhino VIEWPORT
2002 // view. The Rhino viewport has many entries. As of 17 October, 1997 all Rhino
2003 // world to clipping transformation information is derived from the VIEWPORT
2004 // fields:
2005 //
2006 // target, angle1, angle2, angle3, viewsize, and cameradist.
2007 //
2008 // The width, height and zbuffer_depth arguments are used to determing the
2009 // clipping to screen transformation.
2010
2011 ON_Xform R1, R2, R3, RhinoRot;
2012 double frustum_left, frustum_right, frustum_bottom, frustum_top;
2013 double near_clipping_distance, far_clipping_distance;
2014
2015 // Initialize default view in case input is garbage.
2016 if (height < 1)
2017 return false;
2018 if ( width < 1 )
2019 return false;
2020 if ( rhvp_viewsize <= 0.0 )
2021 return false;
2022 if ( rhvp_cameradist <= 0.0 )
2023 return false;
2024
2025 // A Rhino 1.0 VIEWPORT structure describes the camera's location, direction,
2026 // and orientation by specifying a rotation transformation that is
2027 // applied to an initial frame. The rotation transformation is defined
2028 // as a sequence of 3 rotations abount fixed axes. The initial frame
2029 // has the camera located at (0,0,cameradist), pointed in the direction
2030 // (0,0,-1), and oriented so that up is (0,1,0).
2031
2032 R1.Rotation( rhvp_angle1, ON_zaxis, ON_origin ); // so called "twist"
2033 R2.Rotation( rhvp_angle2, ON_xaxis, ON_origin ); // so called "elevation"
2034 R3.Rotation( rhvp_angle3, ON_zaxis, ON_origin ); // so called "fudge factor"
2035 RhinoRot = R3 * R2 * R1;
2036
2037 vp.SetCameraUp( RhinoRot*ON_yaxis );
2038 vp.SetCameraDirection( -(RhinoRot*ON_zaxis) );
2039 vp.SetCameraLocation( rhvp_target - rhvp_cameradist*vp.CameraDirection() );
2040 vp.SetTargetPoint( rhvp_target );
2041 //vp.SetTargetDistance( rhvp_cameradist );
2042
2043 // Camera coordinates "X" = CameraRight = CameraDirection x CameraUp
2044 // Camera coordinates "Y" = CameraUp
2045 // Camera coordinates "Z" = -CameraDirection
2046
2047 // Rhino 1.0 did not support skew projections. In other words, the
2048 // view frustum is symmetric and ray that begins at CameraLocation and
2049 // goes along CameraDirection runs along the frustum's central axis.
2050 // The aspect ratio of the view frustum equals
2051 // (screen port width)/(screen port height)
2052 // This means frus_left = -frus_right, frus_bottom = -frus_top, and
2053 // frus_top/frus_right = height/width
2054
2055 // Set near and far clipping planes to some reasonable values. If
2056 // the depth of the pixel is important, then the near and far clipping
2057 // plane will need to be adjusted later.
2058 // Rhino 1.0 didn't have a far clipping plane in wire frame (which explains
2059 // why you can get perspective views reversed through the origin by using
2060 // the SetCameraTarget() command. It's near clipping plane is set to
2061 // a miniscule value. For mesh rendering, it must come up with some
2062 // sort of reasonable near and far clipping planes because the zbuffer
2063 // is used correctly. When time permits, I'll dig through the rendering
2064 // code and determine what values are being used.
2065 //
2066 near_clipping_distance = rhvp_cameradist/64.0;
2067 if ( near_clipping_distance > 1.0 )
2068 near_clipping_distance = 1.0;
2069 far_clipping_distance = 4.0*rhvp_cameradist;
2070
2071
2072 if ( width <= height )
2073 {
2074 frustum_right = rhvp_viewsize;
2075 frustum_top = frustum_right*height/width;
2076 }
2077 else
2078 {
2079 frustum_top = rhvp_viewsize;
2080 frustum_right = frustum_top*width/height;
2081 }
2082 if ( vp.IsPerspectiveProjection() )
2083 {
2084 frustum_right *= near_clipping_distance;
2085 frustum_top *= near_clipping_distance;
2086 }
2087 frustum_left = -frustum_right;
2088 frustum_bottom = -frustum_top;
2089
2090
2091 vp.SetFrustum(
2092 frustum_left, frustum_right,
2093 frustum_bottom, frustum_top,
2094 near_clipping_distance, far_clipping_distance );
2095
2096 // Windows specific stuff that requires knowing size of client area in pixels
2097 vp.SetScreenPort( 0, (int)width, // windows has screen X increasing accross
2098 (int)height, 0, // windows has screen Y increasing downwards
2099 0, 0xFFFF );
2100
2101 return (vp.IsValid()?true:false);
2102 }
2103
GetCameraAngle(double * angle,double * angle_h,double * angle_w) const2104 bool ON_Viewport::GetCameraAngle(
2105 double* angle,
2106 double* angle_h,
2107 double* angle_w
2108 ) const
2109 {
2110 bool rc = false;
2111 if ( angle )
2112 *angle = 0.0;
2113 if ( angle_h )
2114 *angle_h = 0.0;
2115 if ( angle_w )
2116 *angle_w = 0.0;
2117 double half_w, half_h, left, right, bot, top, near_dist;
2118 if ( GetFrustum( &left, &right, &bot, &top, &near_dist, NULL ) )
2119 {
2120 half_w = ( right > -left ) ? right : -left;
2121 half_h = ( top > -bot ) ? top : -bot;
2122 if ( near_dist > 0.0 && ON_IsValid(near_dist) )
2123 {
2124 if ( angle )
2125 *angle = atan( sqrt(half_w*half_w + half_h*half_h)/near_dist );
2126 if ( angle_h )
2127 *angle_h = atan( half_h/near_dist );
2128 if ( angle_w )
2129 *angle_w = atan( half_w/near_dist );
2130 }
2131 rc = true;
2132 }
2133 return rc;
2134 }
2135
GetCameraAngle(double * angle) const2136 bool ON_Viewport::GetCameraAngle(
2137 double* angle
2138 ) const
2139 {
2140 double angle_h = 0.0;
2141 double angle_w = 0.0;
2142 bool rc = GetCameraAngle( NULL, &angle_h, &angle_w );
2143 if ( angle && rc ) {
2144 *angle = (angle_h < angle_w) ? angle_h : angle_w;
2145 }
2146 return rc;
2147 }
2148
SetCameraAngle(double angle)2149 bool ON_Viewport::SetCameraAngle( double angle )
2150 {
2151 bool rc = false;
2152 double r, d, aspect, half_w, half_h, near_dist, far_dist;
2153 if ( angle > 0.0 && angle < 0.5*ON_PI*(1.0-ON_SQRT_EPSILON) ) {
2154 if ( GetFrustum( NULL, NULL, NULL, NULL, &near_dist, &far_dist ) && GetFrustumAspect( aspect) ) {
2155 r = near_dist*tan(angle);
2156 // d = r/sqrt(1.0+aspect*aspect); // if angle is 1/2 diagonal angle
2157 d = r; // angle is 1/2 smallest angle
2158 if ( aspect >= 1.0 ) {
2159 // width >= height
2160 half_w = d*aspect;
2161 half_h = d;
2162 }
2163 else {
2164 // height > width
2165 half_w = d;
2166 half_h = d/aspect;
2167 }
2168 rc = SetFrustum( -half_w, half_w, -half_h, half_h, near_dist, far_dist );
2169 }
2170 }
2171 return rc;
2172 }
2173
2174 // This version of the function has "lens" misspelled.
GetCamera35mmLenseLength(double * lens_length) const2175 bool ON_Viewport::GetCamera35mmLenseLength( double* lens_length ) const
2176 {
2177 return GetCamera35mmLensLength( lens_length );
2178 }
2179
GetCamera35mmLensLength(double * lens_length) const2180 bool ON_Viewport::GetCamera35mmLensLength( double* lens_length ) const
2181 {
2182 // 35 mm film has a height of 24 mm and a width of 36 mm
2183 double film_r, view_r, half_w, half_h;
2184 double frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far;
2185 if ( !lens_length )
2186 return false;
2187 *lens_length = 0.0;
2188 if ( !GetFrustum( &frus_left, &frus_right, &frus_bottom, &frus_top,
2189 &frus_near, &frus_far ) )
2190 return false;
2191 if ( frus_near <= 0.0 )
2192 return false;
2193 half_w = ( frus_right > -frus_left ) ? frus_right : -frus_left;
2194 half_h = ( frus_top > -frus_bottom ) ? frus_top : -frus_bottom;
2195
2196 // 2009 May 8 Dale Lear - always use width in two point perspective
2197 view_r = (half_w <= half_h || IsTwoPointPerspectiveProjection()) ? half_w : half_h;
2198 film_r = 12.0;
2199 if ( view_r <= 0.0 )
2200 return false;
2201
2202 *lens_length = frus_near*film_r/view_r;
2203 return true;
2204 }
2205
2206 // This version of the function has "lens" misspelled.
SetCamera35mmLenseLength(double lens_length)2207 bool ON_Viewport::SetCamera35mmLenseLength( double lens_length )
2208 {
2209 return SetCamera35mmLensLength( lens_length );
2210 }
2211
SetCamera35mmLensLength(double lens_length)2212 bool ON_Viewport::SetCamera35mmLensLength( double lens_length )
2213 {
2214 // 35 mm film has a height of 24 mm and a width of 36 mm
2215 double film_r, view_r, half_w, half_h, s;
2216 double frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far;
2217 if ( !ON_IsValid(lens_length) || lens_length <= 0.0 )
2218 return false;
2219 if ( !GetFrustum( &frus_left, &frus_right, &frus_bottom, &frus_top,
2220 &frus_near, &frus_far ) )
2221 return false;
2222 if ( frus_near <= 0.0 )
2223 return false;
2224 half_w = ( frus_right > -frus_left ) ? frus_right : -frus_left;
2225 half_h = ( frus_top > -frus_bottom ) ? frus_top : -frus_bottom;
2226
2227 // 2009 May 8 Dale Lear - always use width in two point perspective
2228 view_r = (half_w <= half_h || IsTwoPointPerspectiveProjection()) ? half_w : half_h;
2229 film_r = 12.0;
2230 if ( view_r <= 0.0 )
2231 return false;
2232
2233 s = (film_r/view_r)*(frus_near/lens_length);
2234 if ( fabs(s-1.0) < 1.0e-6 )
2235 return true;
2236
2237 frus_left *= s;
2238 frus_right *= s;
2239 frus_bottom *= s;
2240 frus_top *= s;
2241 return SetFrustum( frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far );
2242 }
2243
GetXform(ON::coordinate_system srcCS,ON::coordinate_system destCS,ON_Xform & xform) const2244 bool ON_Viewport::GetXform(
2245 ON::coordinate_system srcCS,
2246 ON::coordinate_system destCS,
2247 ON_Xform& xform
2248 ) const
2249 {
2250 bool rc = false;
2251 ON_Xform x0, x1;
2252
2253 xform.Identity();
2254
2255 switch( srcCS )
2256 {
2257 case ON::world_cs:
2258 case ON::camera_cs:
2259 case ON::clip_cs:
2260 case ON::screen_cs:
2261 break;
2262 default:
2263 return false;
2264 }
2265 switch( destCS )
2266 {
2267 case ON::world_cs:
2268 case ON::camera_cs:
2269 case ON::clip_cs:
2270 case ON::screen_cs:
2271 break;
2272 default:
2273 return false;
2274 }
2275
2276 if (srcCS == destCS)
2277 return true;
2278
2279
2280 switch ( srcCS )
2281 {
2282
2283 case ON::world_cs:
2284 if ( !m_bValidCamera )
2285 break;
2286
2287 switch ( destCS )
2288 {
2289 case ON::camera_cs:
2290 xform.WorldToCamera( m_CamLoc, m_CamX, m_CamY, m_CamZ );
2291 rc = true;
2292 break;
2293
2294 case ON::clip_cs:
2295 rc = GetXform( ON::world_cs, ON::camera_cs, x0 );
2296 if (rc)
2297 rc = GetXform( ON::camera_cs, ON::clip_cs, x1 );
2298 if (rc)
2299 xform = x1*x0;
2300 break;
2301
2302 case ON::screen_cs:
2303 rc = GetXform( ON::world_cs, ON::clip_cs, x0 );
2304 if (rc)
2305 rc = GetXform( ON::clip_cs, ON::screen_cs, x1 );
2306 if (rc)
2307 xform = x1*x0;
2308 break;
2309
2310 case ON::world_cs:
2311 // Never happens. This is here to quiet g++ warnings.
2312 break;
2313 }
2314 break;
2315
2316 case ON::camera_cs:
2317 if ( !m_bValidCamera )
2318 break;
2319
2320 switch ( destCS )
2321 {
2322 case ON::world_cs:
2323 xform.CameraToWorld( m_CamLoc, m_CamX, m_CamY, m_CamZ );
2324 rc = true;
2325 break;
2326
2327 case ON::clip_cs:
2328 if ( m_bValidFrustum )
2329 {
2330 ON_Xform cam2clip;
2331 cam2clip.CameraToClip(
2332 IsPerspectiveProjection(),
2333 m_frus_left, m_frus_right,
2334 m_frus_bottom, m_frus_top,
2335 m_frus_near, m_frus_far );
2336 xform = m_clip_mods*cam2clip;
2337 rc = true;
2338 }
2339 break;
2340
2341 case ON::screen_cs:
2342 rc = GetXform( ON::camera_cs, ON::clip_cs, x0 );
2343 if (rc)
2344 rc = GetXform( ON::clip_cs, ON::screen_cs, x1 );
2345 if (rc)
2346 xform = x1*x0;
2347 break;
2348
2349 case ON::camera_cs:
2350 // Never happens. This is here to quiet g++ warnings.
2351 break;
2352 }
2353 break;
2354
2355 case ON::clip_cs:
2356 switch ( destCS )
2357 {
2358 case ON::world_cs:
2359 rc = GetXform( ON::clip_cs, ON::camera_cs, x0 );
2360 if (rc)
2361 rc = GetXform( ON::camera_cs, ON::world_cs, x1 );
2362 if (rc)
2363 xform = x1*x0;
2364 break;
2365
2366 case ON::camera_cs:
2367 if ( m_bValidFrustum )
2368 {
2369 ON_Xform clip2cam;
2370 clip2cam.ClipToCamera(
2371 IsPerspectiveProjection(),
2372 m_frus_left, m_frus_right,
2373 m_frus_bottom, m_frus_top,
2374 m_frus_near, m_frus_far );
2375 xform = clip2cam*m_clip_mods_inverse;
2376 rc = true;
2377 }
2378 break;
2379
2380 case ON::screen_cs:
2381 if ( m_bValidPort )
2382 {
2383 xform.ClipToScreen(
2384 m_port_left, m_port_right,
2385 m_port_bottom, m_port_top,
2386 m_port_near, m_port_far );
2387 rc = true;
2388 }
2389 break;
2390
2391 case ON::clip_cs:
2392 // Never happens. This is here to quiet g++ warnings.
2393 break;
2394 }
2395 break;
2396
2397 case ON::screen_cs:
2398 switch ( destCS )
2399 {
2400 case ON::world_cs:
2401 rc = GetXform( ON::screen_cs, ON::camera_cs, x0 );
2402 if (rc)
2403 rc = GetXform( ON::camera_cs, ON::world_cs, x1 );
2404 if (rc)
2405 xform = x1*x0;
2406 break;
2407 case ON::camera_cs:
2408 rc = GetXform( ON::screen_cs, ON::clip_cs, x0 );
2409 if (rc)
2410 rc = GetXform( ON::clip_cs, ON::camera_cs, x1 );
2411 if (rc)
2412 xform = x1*x0;
2413 break;
2414 case ON::clip_cs:
2415 if ( m_bValidPort ) {
2416 xform.ScreenToClip(
2417 m_port_left, m_port_right,
2418 m_port_bottom, m_port_top,
2419 m_port_near, m_port_far );
2420 rc = true;
2421 }
2422 break;
2423 case ON::screen_cs:
2424 // Never happens. This is here to quiet g++ warnings.
2425 break;
2426 }
2427 break;
2428
2429 }
2430
2431 return rc;
2432 }
2433
GetFrustumLine(double screenx,double screeny,ON_Line & world_line) const2434 bool ON_Viewport::GetFrustumLine( double screenx, double screeny, ON_Line& world_line ) const
2435 {
2436 ON_Xform s2c, c2w;
2437 ON_3dPoint c;
2438 ON_Line line;
2439 bool rc;
2440
2441 rc = GetXform( ON::screen_cs, ON::clip_cs, s2c );
2442 if ( rc )
2443 rc = GetXform( ON::clip_cs, ON::world_cs, c2w );
2444 if (rc )
2445 {
2446 // c = mouse point on near clipping plane
2447 c.x = s2c.m_xform[0][0]*screenx + s2c.m_xform[0][1]*screeny + s2c.m_xform[0][3];
2448 c.y = s2c.m_xform[1][0]*screenx + s2c.m_xform[1][1]*screeny + s2c.m_xform[1][3];
2449 c.z = 1.0;
2450 line.to = c2w*c; // line.to = near plane mouse point in world coords
2451 c.z = -1.0;
2452 line.from = c2w*c; // line.from = far plane mouse point in world coords
2453
2454 world_line = line;
2455 }
2456 return rc;
2457 }
2458
clipDist(const double * camLoc,const double * camZ,const double * P)2459 static double clipDist( const double* camLoc, const double* camZ, const double* P )
2460 {
2461 return (camLoc[0]-P[0])*camZ[0]+(camLoc[1]-P[1])*camZ[1]+(camLoc[2]-P[2])*camZ[2];
2462 }
2463
2464
SetFrustumNearFar(const double * box_min,const double * box_max)2465 bool ON_Viewport::SetFrustumNearFar(
2466 const double* box_min,
2467 const double* box_max
2468 )
2469 {
2470 bool rc = false;
2471 const double* box[2];
2472 int i,j,k;
2473 double n, f, d;
2474 double camLoc[3], camZ[3], P[3];
2475
2476 if ( !box_min )
2477 box_min = box_max;
2478 if ( !box_max )
2479 box_max = box_min;
2480 if ( !box_min )
2481 return false;
2482
2483 // 31 May 2007 Dale Lear RR 25980
2484 // Add validation of box_min and box_max.
2485 if ( !ON_IsValid(box_min[0]) || !ON_IsValid(box_min[1]) || !ON_IsValid(box_min[2]) )
2486 return false;
2487 if ( !ON_IsValid(box_max[0]) || !ON_IsValid(box_max[1]) || !ON_IsValid(box_max[2]) )
2488 return false;
2489 if ( box_min[0] > box_max[0]
2490 || box_min[1] > box_max[1]
2491 || box_min[2] > box_max[2]
2492 )
2493 {
2494 return false;
2495 }
2496 box[0] = box_min;
2497 box[1] = box_max;
2498
2499 if ( GetCameraFrame( camLoc, NULL, NULL, camZ ) ) {
2500 n = f = -1.0;
2501 for(i=0;i<2;i++)for(j=0;j<2;j++)for(k=0;k<2;k++) {
2502 P[0] = box[i][0];
2503 P[1] = box[j][1];
2504 P[2] = box[k][2];
2505 d = clipDist(camLoc,camZ,P);
2506 if (!i&&!j&&!k)
2507 n=f=d;
2508 else if ( d < n )
2509 n = d;
2510 else if ( d > f )
2511 f = d;
2512 }
2513 if ( !ON_IsValid(f) || !ON_IsValid(n) )
2514 return false;
2515 if ( f <= 0.0 )
2516 return false; // box is behind camera
2517 n *= 0.9375;
2518 f *= 1.0625;
2519 if ( n <= 0.0 )
2520 n = m__MIN_NEAR_OVER_FAR*f;
2521 if ( IsPerspectiveProjection() )
2522 rc = SetFrustumNearFar( n, f, m__MIN_NEAR_DIST, m__MIN_NEAR_OVER_FAR, 0.5*(n+f) );
2523 else
2524 rc = SetFrustumNearFar( n, f );
2525 }
2526 return rc;
2527 }
2528
SetFrustumNearFar(const double * center,double radius)2529 bool ON_Viewport::SetFrustumNearFar(
2530 const double* center,
2531 double radius
2532 )
2533 {
2534 bool rc = false;
2535 double n, f, d;
2536 double camLoc[3], camZ[3], P[3];
2537
2538 if ( !center
2539 || !ON_IsValid(center[0])
2540 || !ON_IsValid(center[1])
2541 || !ON_IsValid(center[2])
2542 || !ON_IsValid(radius)
2543 )
2544 {
2545 return false;
2546 }
2547
2548 if ( GetCameraFrame( camLoc, NULL, NULL, camZ ) )
2549 {
2550 d = fabs(radius);
2551 P[0] = center[0] + d*camZ[0];
2552 P[1] = center[1] + d*camZ[0];
2553 P[2] = center[2] + d*camZ[0];
2554 n = clipDist(camLoc,camZ,P);
2555 P[0] = center[0] - d*camZ[0];
2556 P[1] = center[1] - d*camZ[0];
2557 P[2] = center[2] - d*camZ[0];
2558 f = clipDist(camLoc,camZ,P);
2559 if ( !ON_IsValid(f) || !ON_IsValid(n) )
2560 return false;
2561 if ( f <= 0.0 )
2562 return false; // sphere is behind camera
2563 n *= 0.9375;
2564 f *= 1.0625;
2565 if ( n <= 0.0 )
2566 n = m__MIN_NEAR_OVER_FAR*f;
2567 if ( IsPerspectiveProjection() )
2568 rc = SetFrustumNearFar( n, f, m__MIN_NEAR_DIST, m__MIN_NEAR_OVER_FAR, 0.5*(n+f) );
2569 else
2570 rc = SetFrustumNearFar( n, f );
2571 }
2572 return rc;
2573 }
2574
SetFrustumNearFar(double n,double f)2575 bool ON_Viewport::SetFrustumNearFar( double n, double f )
2576 {
2577 // This is a bare bones setter. Except for the perspective 0 < n < f
2578 // requirement, do not add checking here.
2579 //
2580 // Use the ON_Viewport::SetFrustumNearFar( near_dist,
2581 // far_dist,
2582 // min_near_dist,
2583 // min_near_over_far,
2584 // target_dist );
2585 //
2586 // version if you need lots of validation and automatic fixing.
2587
2588 double d, frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far;
2589 bool rc = false;
2590 if ( ON_IsValid(n) && ON_IsValid(f) && n > 0.0 && f > n )
2591 {
2592 if ( GetFrustum( &frus_left, &frus_right,
2593 &frus_bottom, &frus_top,
2594 &frus_near, &frus_far ) )
2595 {
2596 // preserve valid frustum
2597 if ( IsPerspectiveProjection() )
2598 {
2599 d = n/frus_near;
2600 frus_left *= d;
2601 frus_right *= d;
2602 frus_bottom *= d;
2603 frus_top *= d;
2604 }
2605 frus_near = n;
2606 frus_far = f;
2607 rc = SetFrustum( frus_left, frus_right,
2608 frus_bottom, frus_top,
2609 frus_near, frus_far );
2610 }
2611 else
2612 {
2613 if ( IsPerspectiveProjection() && (n <= 1.0e-8 || f > 1.0001e8*n) )
2614 {
2615 ON_ERROR("ON_Viewport::SetFrustum - bogus perspective m_frus_near/far values - will crash MS OpenGL");
2616 }
2617 m_frus_near = n;
2618 m_frus_far = f;
2619 rc = true;
2620 }
2621 }
2622 return rc;
2623 }
2624
2625
ChangeToSymmetricFrustum(bool bLeftRightSymmetric,bool bTopBottomSymmetric,double target_distance)2626 bool ON_Viewport::ChangeToSymmetricFrustum(
2627 bool bLeftRightSymmetric,
2628 bool bTopBottomSymmetric,
2629 double target_distance
2630 )
2631 {
2632 if ( bLeftRightSymmetric && m_frus_left == -m_frus_right )
2633 bLeftRightSymmetric = false; // no left/right chnages required.
2634
2635 if ( bTopBottomSymmetric && m_frus_bottom == -m_frus_top )
2636 bTopBottomSymmetric = false; // no top/bottom chagnes required.
2637
2638 if ( !bLeftRightSymmetric && !bTopBottomSymmetric )
2639 return true; // no changes required
2640
2641 if ( !m_bValidFrustum )
2642 return false;
2643
2644 const double half_w = 0.5*(m_frus_right-m_frus_left);
2645 const double half_h = 0.5*(m_frus_top-m_frus_bottom);
2646 double dx = bLeftRightSymmetric ? (m_frus_right - half_w) : 0.0;
2647 double dy = bTopBottomSymmetric ? (m_frus_top - half_h) : 0.0;
2648 if ( bLeftRightSymmetric )
2649 {
2650 m_frus_right = half_w;
2651 m_frus_left = -m_frus_right;
2652 }
2653 if ( bTopBottomSymmetric )
2654 {
2655 m_frus_top = half_h;
2656 m_frus_bottom = -m_frus_top;
2657 }
2658
2659 // if possible, dolly the camera so the original
2660 // target plane is still visible.
2661 if ( m_bValidCamera && (dx != 0.0 || dy != 0.0 ) )
2662 {
2663 if ( ON::perspective_view == m_projection )
2664 {
2665 if ( m_frus_near > 0.0 )
2666 {
2667 if ( ON_UNSET_VALUE == target_distance )
2668 target_distance = TargetDistance(true);
2669 if ( ON_IsValid(target_distance) && target_distance > 0.0 )
2670 {
2671 double s = target_distance/m_frus_near;
2672 dx *= s;
2673 dy *= s;
2674 }
2675 }
2676 else
2677 {
2678 dx=dy = 0.0;
2679 }
2680 }
2681 if ( dx != 0.0 || dy != 0.0 )
2682 {
2683 ON_3dPoint cam_loc = m_CamLoc + dx*m_CamX + dy*m_CamY;
2684 SetCameraLocation(cam_loc);
2685 }
2686 }
2687
2688 return true;
2689 }
2690
2691
GetWorldToScreenScale(const ON_3dPoint & P,double * scale) const2692 bool ON_Viewport::GetWorldToScreenScale( const ON_3dPoint& P, double* scale ) const
2693 {
2694 if ( scale ) {
2695 ON_Xform w2s;
2696 ON_3dVector X;
2697 ON_3dPoint Q, ScrC, ScrQ;
2698 if (!GetCameraFrame( NULL, X, NULL, NULL ))
2699 return false;
2700 if (!GetXform( ON::world_cs, ON::screen_cs, w2s ))
2701 return false;
2702 Q = P+X;
2703 ScrC = w2s*P;
2704 ScrQ = w2s*Q;
2705 *scale = fabs(ScrC.x - ScrQ.x);
2706 }
2707 return true;
2708 }
2709
GetCoordinateSprite(int size,int scrx,int scry,int indx[3],double scr_coord[3][2]) const2710 bool ON_Viewport::GetCoordinateSprite(
2711 int size,
2712 int scrx, int scry,
2713 int indx[3], // axis order by depth
2714 double scr_coord[3][2] ) const
2715 {
2716 // size = length of axes in pixels
2717
2718 indx[0] = 0; indx[1] = 1; indx[2] = 2;
2719 scr_coord[0][0] = scr_coord[1][0] = scr_coord[2][0] = scrx;
2720 scr_coord[0][1] = scr_coord[1][1] = scr_coord[2][1] = scry;
2721
2722 ON_3dPoint C, XP, YP, ZP, ScrC, ScrXP;
2723 ON_3dVector X, Z, Scr[3];
2724 ON_Xform w2s;
2725 if (!GetFrustumCenter( C ) )
2726 return false;
2727 if (!GetCameraFrame( NULL, X, NULL, Z ))
2728 return false;
2729 if (!GetXform( ON::world_cs, ON::screen_cs, w2s ))
2730 return false;
2731
2732 // indx[] determines order that axes are drawn
2733 // sorted from back to front
2734 int i,j,k;
2735 for (i = 0; i < 2; i++) for (j = i+1; j <= 2; j++) {
2736 if (Z[indx[i]] > Z[indx[j]])
2737 {k = indx[i]; indx[i] = indx[j]; indx[j] = k;}
2738 }
2739
2740 // determine world length that corresponds to size pixels
2741 XP = C+X;
2742 ScrC = w2s*C;
2743 ScrXP = w2s*XP;
2744 if (ScrC.x == ScrXP.x)
2745 return false;
2746 double s = size/fabs( ScrC.x - ScrXP.x );
2747
2748 // transform world coord axes to screen
2749 XP = C;
2750 XP.x += s;
2751 YP = C;
2752 YP.y += s;
2753 ZP = C;
2754 ZP.z += s;
2755 Scr[0] = w2s*XP;
2756 Scr[1] = w2s*YP;
2757 Scr[2] = w2s*ZP;
2758
2759 double dx = scrx - ScrC.x;
2760 double dy = scry - ScrC.y;
2761 for (i=0;i<3;i++) {
2762 scr_coord[i][0] = dx + Scr[i].x;
2763 scr_coord[i][1] = dy + Scr[i].y;
2764 }
2765
2766 return true;
2767 }
2768
GetRelativeScreenCoordinates(int port_left,int port_right,int port_bottom,int port_top,ON_BOOL32 bSortPoints,int & x0,int & y0,int & x1,int & y1,double & s0,double & t0,double & s1,double & t1)2769 static ON_BOOL32 GetRelativeScreenCoordinates(
2770 int port_left, int port_right,
2771 int port_bottom, int port_top,
2772 ON_BOOL32 bSortPoints,
2773 int& x0, int& y0, int& x1, int& y1,
2774 double& s0, double& t0, double& s1, double& t1
2775 )
2776 {
2777 // convert screen rectangle into relative rectangle
2778 if ( bSortPoints ) {
2779 int i;
2780 if ( x0 > x1 ) {
2781 i = x0; x0 = x1; x1 = i;
2782 }
2783 if ( port_left > port_right ) {
2784 i = x0; x0 = x1; x1 = i;
2785 }
2786 if ( y0 > y1 ) {
2787 i = y0; y0 = y1; y1 = i;
2788 }
2789 if ( port_bottom > port_top ) {
2790 i = y0; y0 = y1; y1 = i;
2791 }
2792 }
2793
2794 s0 = ((double)(x0 - port_left))/((double)(port_right - port_left));
2795 s1 = ((double)(x1 - port_left))/((double)(port_right - port_left));
2796 t0 = ((double)(y0 - port_bottom))/((double)(port_top - port_bottom));
2797 t1 = ((double)(y1 - port_bottom))/((double)(port_top - port_bottom));
2798
2799 double tol = 0.001;
2800 if ( fabs(s0) <= tol ) s0 = 0.0; else if (fabs(s0-1.0) <= tol ) s0 = 1.0;
2801 if ( fabs(s1) <= tol ) s1 = 0.0; else if (fabs(s1-1.0) <= tol ) s1 = 1.0;
2802 if ( fabs(t0) <= tol ) t0 = 0.0; else if (fabs(t0-1.0) <= tol ) t0 = 1.0;
2803 if ( fabs(t1) <= tol ) t1 = 0.0; else if (fabs(t1-1.0) <= tol ) t1 = 1.0;
2804 if ( fabs(s0-s1) <= tol )
2805 return false;
2806 if ( fabs(t0-t1) <= tol )
2807 return false;
2808 return true;
2809 }
2810
ZoomToScreenRect(int x0,int y0,int x1,int y1)2811 bool ON_Viewport::ZoomToScreenRect( int x0, int y0, int x1, int y1 )
2812 {
2813 int port_left, port_right, port_bottom, port_top, port_near, port_far;
2814 if ( !GetScreenPort( &port_left, &port_right,
2815 &port_bottom, &port_top,
2816 &port_near, &port_far ) )
2817 return false;
2818
2819 // dolly camera sideways so it's looking at center of rectangle
2820 int dx = (x0+x1)/2;
2821 int dy = (y0+y1)/2;
2822 int cx = (port_left+port_right)/2;
2823 int cy = (port_bottom+port_top)/2;
2824
2825 ON_3dVector dolly_vector;
2826 if ( !GetDollyCameraVector( dx, dy, cx, cy, 0.5*(FrustumNear()+FrustumFar()), dolly_vector ) )
2827 return false;
2828 if ( !DollyCamera( dolly_vector ) )
2829 return false;
2830
2831 // adjust frustum
2832 dx = cx - dx;
2833 dy = cy - dy;
2834 x0 += dx;
2835 x1 += dx;
2836 y0 += dy;
2837 y1 += dy;
2838 double frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far;
2839 if ( !GetFrustum( &frus_left, &frus_right,
2840 &frus_bottom, &frus_top,
2841 &frus_near, &frus_far ) )
2842 return false;
2843 double s0,t0,s1,t1;
2844 if ( !GetRelativeScreenCoordinates(port_left, port_right, port_bottom, port_top,
2845 true,
2846 x0,y0,x1,y1,
2847 s0,t0,s1,t1) )
2848 return false;
2849 double w = frus_right - frus_left;
2850 double h = frus_top - frus_bottom;
2851 double a0 = (1.0-s0)*frus_left + s0*frus_right;
2852 double a1 = (1.0-s1)*frus_left + s1*frus_right;
2853 double b0 = (1.0-t0)*frus_bottom + t0*frus_top;
2854 double b1 = (1.0-t1)*frus_bottom + t1*frus_top;
2855 if ( -a0 > a1 ) a1 = -a0; else a0 = -a1;
2856 if ( -b0 > b1 ) b1 = -b0; else b0 = -b1;
2857 double d;
2858 if ( (b1-b0)*w < (a1-a0)*h ) {
2859 d = (a1-a0)*h/w;
2860 d = 0.5*(d - (b1-b0));
2861 b0 -= d;
2862 b1 += d;
2863 }
2864 else {
2865 d = (b1-b0)*w/h;
2866 d = 0.5*(d - (a1-a0));
2867 a0 -= d;
2868 a1 += d;
2869 }
2870
2871 return SetFrustum( a0, a1, b0, b1, frus_near, frus_far );
2872 }
2873
2874 /*
2875 ON_BOOL32 ON_Viewport::DollyToScreenRect( double view_plane_distance,
2876 int x0, int y0, int x1, int y1 )
2877 {
2878 // Only makes sense in a perspective projection. In a parallel projection,
2879 // I resort to ZoomToScreenRect(0 and the visual result is the same.
2880 if ( !IsPerspectiveProjection() )
2881 return ZoomToScreenRect( x0, y0, x1, y1 );
2882
2883 int port_left, port_right, port_bottom, port_top;
2884 if ( !GetScreenPort( &port_left, &port_right, &port_bottom, &port_top, NULL, NULL ) )
2885 return false;
2886 int dx = (x0+x1)/2;
2887 int dy = (y0+y1)/2;
2888 int cx = (port_left+port_right)/2;
2889 int cy = (port_bottom+port_top)/2;
2890 if ( !DollyAlongScreenChord( dx, dy, cx, cy ) )
2891 return false;
2892 dx = cx - dx;
2893 dy = cy - dy;
2894 x0 += dx;
2895 x1 += dx;
2896 y0 += dy;
2897 y1 += dy;
2898
2899 double frus_left, frus_right, frus_bottom, frus_top, frus_near, frus_far;
2900 if ( !GetFrustum( &frus_left, &frus_right,
2901 &frus_bottom, &frus_top,
2902 &frus_near, &frus_far ) )
2903 return false;
2904
2905 double s0, t0, s1, t1;
2906 if ( !GetRelativeScreenCoordinates(port_left, port_right, port_bottom, port_top,
2907 true,
2908 x0,y0,x1,y1,
2909 s0,t0,s1,t1) )
2910 return false;
2911
2912 double w = frus_right - frus_left;
2913 double h = frus_top - frus_bottom;
2914 double a0 = (1.0-s0)*frus_left + s0*frus_right;
2915 double a1 = (1.0-s1)*frus_left + s1*frus_right;
2916 double b0 = (1.0-t0)*frus_bottom + t0*frus_top;
2917 double b1 = (1.0-t1)*frus_bottom + t1*frus_top;
2918 if ( -a0 > a1 ) a1 = -a0; else a0 = -a1;
2919 if ( -b0 > b1 ) b1 = -b0; else b0 = -b1;
2920 double d;
2921 if ( (b1-b0)*w < (a1-a0)*h ) {
2922 d = (a1-a0)*h/w;
2923 d = 0.5*(d - h);
2924 b0 -= d;
2925 b1 += d;
2926 }
2927 else {
2928 d = (b1-b0)*w/h;
2929 d = 0.5*(d - w);
2930 a0 -= d;
2931 a1 += d;
2932 }
2933
2934 d = 0.5*((a1-a0)/w + (b1-b0)/h)*view_plane_distance;
2935 double delta = d - view_plane_distance;
2936
2937 frus_near += delta;
2938 frus_far += delta;
2939 if ( frus_near <= 0.0 ) {
2940 if ( frus_far <= 0.0 )
2941 frus_far = 100.0;
2942 frus_near = 0.001*frus_far;
2943 }
2944 if ( !SetFrustumNearFar( frus_near, frus_far ) )
2945 return false;
2946
2947 double camLoc[3], camY[3], camZ[3];
2948 if ( !GetCameraFrame( camLoc, NULL, camY, camZ ) )
2949 return false;
2950 camLoc[0] += delta*camZ[0];
2951 camLoc[1] += delta*camZ[1];
2952 camLoc[2] += delta*camZ[2];
2953 camZ[0] = -camZ[0];
2954 camZ[1] = -camZ[1];
2955 camZ[2] = -camZ[2];
2956 if ( !SetCamera( camLoc, camZ, camY ) )
2957 return false;
2958
2959 return true;
2960 }
2961 */
2962
Extents(double angle,const ON_BoundingBox & bbox)2963 bool ON_Viewport::Extents( double angle, const ON_BoundingBox& bbox )
2964 {
2965 double radius;
2966 double x, y, xmin, xmax, ymin, ymax;
2967 int i,j,k;
2968
2969 if ( !bbox.IsValid() || !IsValid() )
2970 return false;
2971 ON_3dVector camX = CameraX();
2972 ON_3dVector camY = CameraY();
2973 ON_3dPoint center = bbox.Center();
2974 xmin=xmax=ymin=ymax=0.0;
2975 for (i=0;i<2;i++) for (j=0;j<2;j++) for (k=0;k<2;k++) {
2976 ON_3dVector box_corner = bbox.Corner(i,j,k);
2977 x = camX*box_corner;
2978 y = camY*box_corner;
2979 if ( i==0&&j==0&&k==0) {
2980 xmin=xmax=x;
2981 ymin=ymax=y;
2982 }
2983 else {
2984 if ( x > xmax) xmax=x; else if (x < xmin) xmin = x;
2985 if ( y > ymax) ymax=y; else if (y < ymin) ymin = y;
2986 }
2987 }
2988 radius = xmax-xmin;
2989 if ( ymax-ymin > radius )
2990 radius = ymax-ymin;
2991 if ( radius <= ON_SQRT_EPSILON ) {
2992 radius = bbox.Diagonal().MaximumCoordinate();
2993 }
2994 radius *= 0.5;
2995 if ( radius <= ON_SQRT_EPSILON )
2996 radius = 1.0;
2997 return Extents( angle, center, radius );
2998 }
2999
Extents(double angle,const ON_3dPoint & center,double radius)3000 bool ON_Viewport::Extents( double angle, const ON_3dPoint& center, double radius )
3001 {
3002 if ( !IsValid() )
3003 return false;
3004
3005 double target_dist, near_dist, far_dist;
3006
3007 if ( radius <= 0.0 ||
3008 angle <= 0.0 ||
3009 angle >= 0.5*ON_PI*(1.0-ON_SQRT_EPSILON) )
3010 return false;
3011
3012 target_dist = radius/sin(angle);
3013 if ( !IsPerspectiveProjection() )
3014 {
3015 target_dist += 1.0625*radius;
3016 }
3017 near_dist = target_dist - 1.0625*radius;
3018 if ( near_dist < 0.0625*radius )
3019 near_dist = 0.0625*radius;
3020 if ( near_dist < m__MIN_NEAR_DIST )
3021 near_dist = m__MIN_NEAR_DIST;
3022 far_dist = target_dist + 1.0625*radius;
3023
3024 SetCameraLocation( center + target_dist*CameraZ() );
3025 if ( !SetFrustumNearFar( near_dist, far_dist ) )
3026 return false;
3027 if ( !SetCameraAngle( angle ) )
3028 return false;
3029
3030 return IsValid()?true:false;
3031 }
3032
Dump(ON_TextLog & dump) const3033 void ON_Viewport::Dump( ON_TextLog& dump ) const
3034 {
3035 dump.Print("ON_Viewport\n");
3036 dump.PushIndent();
3037
3038 dump.Print("Projection: ");
3039 switch(m_projection)
3040 {
3041 case ON::parallel_view:
3042 dump.Print("parallel\n");
3043 break;
3044 case ON::perspective_view:
3045 dump.Print("perspective\n");
3046 break;
3047 default:
3048 dump.Print("invalid\n");
3049 break;
3050 }
3051 dump.Print("Camera: (m_bValidCamera = %s)\n",(m_bValidCamera?"true":"false"));
3052 dump.PushIndent();
3053 dump.Print("Location: "); if ( CameraLocationIsLocked() ) dump.Print("(locked) "); dump.Print(m_CamLoc); dump.Print("\n");
3054 dump.Print("Direction: "); if ( CameraDirectionIsLocked() ) dump.Print("(locked) "); dump.Print(m_CamDir); dump.Print("\n");
3055 dump.Print("Up: "); if ( CameraUpIsLocked() ) dump.Print("(locked) "); dump.Print(m_CamUp); dump.Print("\n");
3056 dump.Print("X: "); dump.Print(m_CamX); dump.Print("\n");
3057 dump.Print("Y: "); dump.Print(m_CamY); dump.Print("\n");
3058 dump.Print("Z: "); dump.Print(m_CamZ); dump.Print("\n");
3059 dump.PopIndent();
3060 dump.Print("Target Point: "); dump.Print(m_target_point); dump.Print("\n");
3061 dump.Print("target distance %g\n",TargetDistance(true));
3062
3063 double frus_aspect=0.0;
3064 GetFrustumAspect(frus_aspect);
3065 dump.Print("Frustum: (m_bValidFrustum = %s)\n",(m_bValidFrustum?"true":"false"));
3066 dump.PushIndent();
3067 dump.Print("left/right symmetry locked = %s\n",FrustumIsLeftRightSymmetric()?"true":"false");
3068 dump.Print("top/bottom symmetry locked = %s\n",FrustumIsTopBottomSymmetric()?"true":"false");
3069 dump.Print("left: "); dump.Print(m_frus_left); dump.Print("\n");
3070 dump.Print("right: "); dump.Print(m_frus_right); dump.Print("\n");
3071 dump.Print("bottom: "); dump.Print(m_frus_bottom); dump.Print("\n");
3072 dump.Print("top: "); dump.Print(m_frus_top); dump.Print("\n");
3073 dump.Print("near: "); dump.Print(m_frus_near); dump.Print("\n");
3074 dump.Print("far: "); dump.Print(m_frus_far); dump.Print("\n");
3075 dump.Print("aspect (width/height): "); dump.Print(frus_aspect); dump.Print("\n");
3076 if ( ON::perspective_view == m_projection )
3077 {
3078 dump.PushIndent();
3079 dump.Print("near/far: %g\n",m_frus_near/m_frus_far);
3080 dump.Print("suggested minimum near: = %g\n",m__MIN_NEAR_DIST);
3081 dump.Print("suggested minimum near/far: = %g\n",m__MIN_NEAR_OVER_FAR);
3082 dump.PopIndent();
3083 }
3084 dump.PopIndent();
3085
3086 double port_aspect=0.0;
3087 GetScreenPortAspect(port_aspect);
3088 dump.Print("Port: (m_bValidPort = %s\n",(m_bValidPort?"true":"false"));
3089 dump.PushIndent();
3090 dump.Print("left: %d\n",m_port_left);
3091 dump.Print("right: %d\n",m_port_right);
3092 dump.Print("bottom: %d\n",m_port_bottom);
3093 dump.Print("top: %d\n",m_port_top);
3094 dump.Print("near: %d\n",m_port_near);
3095 dump.Print("far: %d\n",m_port_far);
3096 dump.Print("aspect (width/height): "); dump.Print(port_aspect); dump.Print("\n");
3097 dump.PopIndent();
3098
3099 dump.PopIndent();
3100 }
3101
GetPointDepth(ON_3dPoint point,double * near_dist,double * far_dist,bool bGrowNearFar) const3102 bool ON_Viewport::GetPointDepth(
3103 ON_3dPoint point,
3104 double* near_dist,
3105 double* far_dist,
3106 bool bGrowNearFar
3107 ) const
3108 {
3109 bool rc = false;
3110 if ( point.x != ON_UNSET_VALUE )
3111 {
3112 double depth = (m_CamLoc - point)*m_CamZ;
3113 if ( 0 != near_dist && (*near_dist == ON_UNSET_VALUE || !bGrowNearFar || *near_dist > depth) )
3114 *near_dist = depth;
3115 if ( 0 != far_dist && (*far_dist == ON_UNSET_VALUE || !bGrowNearFar || *far_dist < depth) )
3116 *far_dist = depth;
3117 rc = true;
3118 }
3119 return rc;
3120 }
3121
GetPointDepth(ON_3dPoint point,double * view_plane_depth) const3122 bool ON_Viewport::GetPointDepth(
3123 ON_3dPoint point,
3124 double* view_plane_depth
3125 ) const
3126 {
3127 bool rc = false;
3128 if ( point.x != ON_UNSET_VALUE )
3129 {
3130 double depth = (m_CamLoc - point)*m_CamZ;
3131 if ( 0 != view_plane_depth )
3132 *view_plane_depth = depth;
3133 rc = true;
3134 }
3135 return rc;
3136 }
3137
GetBoundingBoxDepth(ON_BoundingBox bbox,double * near_dist,double * far_dist,bool bGrowNearFar) const3138 bool ON_Viewport::GetBoundingBoxDepth(
3139 ON_BoundingBox bbox,
3140 double* near_dist,
3141 double* far_dist,
3142 bool bGrowNearFar
3143 ) const
3144 {
3145 // The Xbuffer[] stuff is to skip wasting time in unneeded constructors.
3146 // The buffers are double arrays to insure alignments are correct.
3147 ON_3dPoint* C;
3148 ON_3dPoint* P;
3149 ON_PlaneEquation* S;
3150 ON_Line* L;
3151 ON_3dPoint Q;
3152 double Pbuffer[(8+8+8+48)*(sizeof(P[0])/sizeof(double))];
3153 double Sbuffer[5*(sizeof(S[0])/sizeof(double))];
3154 double Lbuffer[4*(sizeof(L[0])/sizeof(double))];
3155 double d, t[2], v[4][8], v0, v1;
3156 const double tol = ON_SQRT_EPSILON*(1.0 + m_CamLoc.MaximumCoordinate());
3157 C = (ON_3dPoint*)Pbuffer;
3158 P = C+8;
3159 S = (ON_PlaneEquation*)Sbuffer;
3160 L = (ON_Line*)Lbuffer;
3161 unsigned int i, j, k, Pin, Pout, Pcount;
3162 bool rc;
3163 const bool bPerspectiveProjection = (ON::perspective_view == m_projection);
3164
3165 for (;;)
3166 {
3167 rc = bbox.GetCorners(C);
3168 if (!rc)
3169 break;
3170 rc = GetFrustumLeftPlaneEquation(S[0]);
3171 if (!rc)
3172 break;
3173 rc = GetFrustumRightPlaneEquation(S[1]);
3174 if (!rc)
3175 break;
3176 rc = GetFrustumBottomPlaneEquation(S[2]);
3177 if (!rc)
3178 break;
3179 rc = GetFrustumTopPlaneEquation(S[3]);
3180 if (!rc)
3181 break;
3182
3183 S[4].ON_3dVector::operator=(-m_CamZ);
3184 S[4].d = -S[4].ON_3dVector::operator*(m_CamLoc);
3185
3186 Pcount = 0;
3187 Pin = 0;
3188 Pout = 0;
3189
3190 for ( i = 0; i < 8; i++ )
3191 {
3192 k = 0;
3193 if ( (v[0][i] = S[0].ValueAt(C[i])) >= -tol )
3194 k |= 1;
3195 else
3196 Pout |= 1;
3197 if ( (v[1][i] = S[1].ValueAt(C[i])) >= -tol )
3198 k |= 2;
3199 else
3200 Pout |= 2;
3201 if ( (v[2][i] = S[2].ValueAt(C[i])) >= -tol )
3202 k |= 4;
3203 else
3204 Pout |= 4;
3205 if ( (v[3][i] = S[3].ValueAt(C[i])) >= -tol )
3206 k |= 8;
3207 else
3208 Pout |= 8;
3209
3210 if ( !bPerspectiveProjection || S[4].ValueAt(C[i]) > 0.0 )
3211 k |= 16;
3212
3213 Pin |= k;
3214 if ( (1|2|4|8|16) == k )
3215 {
3216 // C[i] is inside the infinte frustum
3217 P[Pcount++] = C[i];
3218 }
3219 }
3220
3221 if ( Pcount < 8 )
3222 {
3223 // some portion of bbox is outside the infinte frustum
3224 if ( (1|2|4|8|16) != Pin )
3225 {
3226 // bbox does not intersect the infinite frustum.
3227 rc = false;
3228 break;
3229 }
3230
3231 j = 0;
3232 if ( bPerspectiveProjection )
3233 {
3234 if ( bbox.MinimumDistanceTo(m_CamLoc) <= 0.0 )
3235 {
3236 // camera location is in the bounding box
3237 P[Pcount++] = m_CamLoc;
3238 j = 1; // j = 1 indicates m_CamLoc has been added to P[].
3239 }
3240 L[0].from = m_CamLoc;
3241 L[1].from = m_CamLoc;
3242 L[2].from = m_CamLoc;
3243 L[3].from = m_CamLoc;
3244 }
3245 else
3246 {
3247 rc = GetNearRect(L[0].from,L[1].from,L[2].from,L[3].from);
3248 if (!rc)
3249 break;
3250 }
3251
3252 rc = GetFarRect(L[0].to,L[1].to,L[2].to,L[3].to);
3253 if (!rc)
3254 break;
3255
3256 const unsigned int Linout[4] = {
3257 1|4, // intersection of left and bottom frustum sides
3258 2|4, // intersection of right and bottom frustum sides
3259 1|8, // intersection of left and top frustum sides
3260 2|8 // intersection of right and top frustum sides
3261 };
3262
3263 k = Pin & Pout;
3264 for ( i = 0; i < 4; i++ )
3265 {
3266 // The Linout[i] == ... test is true if bbox is on both sides
3267 // of both planes whose intersection defines the line L[i].
3268 // The fast integer test helps cull unnecessary calls to
3269 // the expensive ON_Intersect() function.
3270 if ( Linout[i] == (k & Linout[i])
3271 && ON_Intersect(bbox,L[i],tol,(ON_Interval*)t)
3272 )
3273 {
3274 if ( bPerspectiveProjection )
3275 {
3276 if ( t[1] < 0.0 )
3277 continue;
3278 if ( t[0] < 0.0 )
3279 {
3280 if ( 0 == j )
3281 {
3282 P[Pcount++] = m_CamLoc;
3283 j = 1; // j = 1 indicates m_CamLoc has been added to P[].
3284 }
3285 t[0] = t[1];
3286 }
3287 }
3288 P[Pcount++] = L[i].PointAt(t[0]);
3289 if ( t[1] > t[0] )
3290 P[Pcount++] = L[i].PointAt(t[1]);
3291 }
3292 }
3293
3294 // intersect box edges with frustum sides
3295 // The 12 bbox edges have endpoints
3296 // C[e[*][0]] and C[E[*][1]]
3297 const unsigned int e[12][2] = {
3298 {0,1},{2,3},{4,5},{6,7},
3299 {0,2},{1,3},{4,6},{5,7},
3300 {0,4},{1,5},{2,6},{3,7}};
3301
3302 for ( i = 0; i < 4; i++ )
3303 {
3304 for ( j = 0; j < 12; j++ )
3305 {
3306 v0 = v[i][e[j][0]];
3307 v1 = v[i][e[j][1]];
3308 if ( v0*v1 < 0.0 )
3309 {
3310 // this box edge crosses the frustum side plane
3311 d = v0/(v0-v1);
3312 P[Pcount++] = Q = (1.0-d)*C[e[j][0]] + d*C[e[j][1]];
3313 // verify that Q is in the frustum
3314 for ( k = 0; k < 4; k++ )
3315 {
3316 if ( i != k && S[k].ValueAt(Q) <= -tol )
3317 {
3318 // Q is not in the view frustum
3319 Pcount--;
3320 break;
3321 }
3322 }
3323 }
3324 }
3325 }
3326 if ( 0 == Pcount )
3327 {
3328 rc = false;
3329 break;
3330 }
3331 }
3332
3333 t[0] = t[1] = (m_CamLoc - P[0])*m_CamZ;
3334 for ( i = 1; i < Pcount; i++ )
3335 {
3336 d = (m_CamLoc - P[i])*m_CamZ;
3337 if ( d < t[0] )
3338 t[0] = d;
3339 else if ( d > t[1] )
3340 t[1] = d;
3341 }
3342
3343 if ( bPerspectiveProjection )
3344 {
3345 if ( t[1] < 0.0 )
3346 {
3347 rc = false;
3348 break;
3349 }
3350 if ( t[0] < 0.0 )
3351 t[0] = 0.0;
3352 }
3353
3354 if ( 0 != near_dist && (!bGrowNearFar || !ON_IsValid(*near_dist) || t[0] < *near_dist) )
3355 *near_dist = t[0];
3356 if ( 0 != far_dist && (!bGrowNearFar || !ON_IsValid(*far_dist) || t[1] > *far_dist) )
3357 *far_dist = t[1];
3358
3359 rc = true;
3360 break;
3361 }
3362
3363 return rc;
3364 }
3365
GetSphereDepth(ON_Sphere sphere,double * near_dist,double * far_dist,bool bGrowNearFar) const3366 bool ON_Viewport::GetSphereDepth(
3367 ON_Sphere sphere,
3368 double* near_dist,
3369 double* far_dist,
3370 bool bGrowNearFar
3371 ) const
3372 {
3373 bool rc = GetPointDepth( sphere.Center(), near_dist, far_dist, bGrowNearFar );
3374 if ( rc && sphere.Radius() > 0.0 )
3375 {
3376 if ( 0 != near_dist )
3377 *near_dist -= sphere.Radius();
3378 if ( 0 != far_dist )
3379 *far_dist += sphere.Radius();
3380 }
3381 return rc;
3382 }
3383
SetFrustumNearFar(double near_dist,double far_dist,double min_near_dist,double min_near_over_far,double target_dist)3384 bool ON_Viewport::SetFrustumNearFar(
3385 double near_dist,
3386 double far_dist,
3387 double min_near_dist,
3388 double min_near_over_far,
3389 double target_dist
3390 )
3391 {
3392 double relative_depth_bias = 0.0;
3393 return SetFrustumNearFar(
3394 near_dist,
3395 far_dist,
3396 min_near_dist,
3397 min_near_over_far,
3398 target_dist,
3399 relative_depth_bias
3400 );
3401 }
3402
SetFrustumNearFar(double near_dist,double far_dist,double min_near_dist,double min_near_over_far,double target_dist,double relative_depth_bias)3403 bool ON_Viewport::SetFrustumNearFar(
3404 double near_dist,
3405 double far_dist,
3406 double min_near_dist,
3407 double min_near_over_far,
3408 double target_dist,
3409 double relative_depth_bias
3410 )
3411 {
3412 if ( !ON_IsValid(near_dist)
3413 || !ON_IsValid(far_dist)
3414 || near_dist > far_dist )
3415 {
3416 return false;
3417 }
3418
3419 // min_near_over_far needs to be < 1 and should be in the
3420 // range 1e-6 to 1e-2. By setting negative min's to zero,
3421 // the code below is simplified but still ignores a negative
3422 // input.
3423 const double tiny = ON_ZERO_TOLERANCE;
3424 const double MIN_NEAR_DIST = ( ON_IsValid(m__MIN_NEAR_DIST) && m__MIN_NEAR_DIST <= tiny )
3425 ? m__MIN_NEAR_DIST
3426 : ON_Viewport::DefaultMinNearDist;
3427 const double MIN_NEAR_OVER_FAR = ( ON_IsValid(m__MIN_NEAR_OVER_FAR)
3428 && m__MIN_NEAR_OVER_FAR > tiny
3429 && m__MIN_NEAR_OVER_FAR < 1.0-tiny )
3430 ? m__MIN_NEAR_OVER_FAR
3431 : ON_Viewport::DefaultMinNearOverFar;
3432
3433 // 30 May Dale Lear
3434 // Add checks for validity of min_near_dist and min_near_over_far
3435 if ( !ON_IsValid(min_near_dist) || min_near_dist <= tiny )
3436 {
3437 min_near_dist = MIN_NEAR_DIST;
3438 }
3439
3440 if ( !ON_IsValid(min_near_over_far)
3441 || min_near_over_far <= tiny
3442 || min_near_over_far >= 1.0-tiny )
3443 {
3444 min_near_over_far = MIN_NEAR_OVER_FAR;
3445 }
3446
3447 if ( IsPerspectiveProjection() )
3448 {
3449 // make sure 0 < near_dist < far_dist
3450 if ( near_dist < min_near_dist )
3451 near_dist = min_near_dist;
3452
3453 if ( far_dist <= near_dist+tiny )
3454 {
3455 far_dist = 100.0*near_dist;
3456 if ( target_dist > near_dist+min_near_dist && far_dist <= target_dist+min_near_dist )
3457 {
3458 far_dist = 2.0*target_dist - near_dist;
3459 }
3460 if ( near_dist < min_near_over_far*far_dist )
3461 far_dist = near_dist/min_near_over_far;
3462 }
3463 // The 1.0001 fudge factor is to ensure successive calls to this function
3464 // give identical results.
3465 while ( near_dist < 1.0001*min_near_over_far*far_dist )
3466 {
3467 // need to move near and far closer together
3468 if ( ON_IsValid(target_dist) && near_dist < target_dist && target_dist < far_dist )
3469 {
3470 // STEP 1
3471 // If near and far are a long ways from the target
3472 // point, move them towards the target so the
3473 // fine tuning in step 2 makes sense.
3474 if ( target_dist/far_dist < min_near_over_far )
3475 {
3476 if ( near_dist/target_dist >= sqrt(min_near_over_far) )
3477 {
3478 // assume near_dist is good and just pull back far_dist
3479 far_dist = near_dist/min_near_over_far;
3480 break;
3481 }
3482 else
3483 {
3484 // move far_dist to within striking distance of the target
3485 // and let STEP 2 fine tune things.
3486 far_dist = target_dist/min_near_over_far;
3487 }
3488 }
3489
3490 if ( near_dist/target_dist < min_near_over_far )
3491 {
3492 if ( target_dist/far_dist <= sqrt(min_near_over_far)
3493 && far_dist <= 4.0*target_dist )
3494 {
3495 // assume far_dist is good and just move up near_dist
3496 near_dist = far_dist*min_near_over_far;
3497 break;
3498 }
3499 else
3500 {
3501 // move near_dist to within striking distance of the target
3502 // and let STEP 2 fine tune things.
3503 near_dist = target_dist*min_near_over_far;
3504 }
3505 }
3506
3507 // STEP 2
3508 // Move near and far towards target by
3509 // an amount proportional to current
3510 // distances from the target.
3511
3512 double b = (far_dist - target_dist)*min_near_over_far + (target_dist - near_dist);
3513 if ( b > 0.0)
3514 {
3515 double s = target_dist*(1.0 - min_near_over_far)/b;
3516 if ( s > 1.0 || s <= ON_ZERO_TOLERANCE || !ON_IsValid(s) )
3517 {
3518 if ( s > 1.00001 || s <= ON_ZERO_TOLERANCE )
3519 {
3520 // should never happen
3521 ON_ERROR("ON_Viewport::SetFrustumNearFar arithmetic problem 1.");
3522 }
3523 s = 1.0;
3524 }
3525
3526 // 19 Jan 2010, Mikko:
3527 // Reordered the operations to guarantee n==near_dist and f==far_dist
3528 // when s==1.0. The old system generated bogus problem reports when the dist
3529 // difference was big.
3530 double n = s*near_dist + target_dist*(1.0-s);
3531 double f = s*far_dist + target_dist*(1.0-s);
3532 //double n = target_dist + s*(near_dist-target_dist);
3533 //double f = target_dist + s*(far_dist-target_dist);
3534
3535 #if defined(ON_DEBUG)
3536 double m = ((f != 0.0) ? n/f : 0.0)/min_near_over_far;
3537 if ( m < 0.95 || m > 1.05 )
3538 {
3539 ON_ERROR("ON_Viewport::SetFrustumNearFar arithmetic problem 2.");
3540 }
3541 #endif
3542
3543 if ( n < near_dist || n >= target_dist)
3544 {
3545 ON_ERROR("ON_Viewport::SetFrustumNearFar arithmetic problem 3.");
3546 if ( target_dist < f && f < far_dist )
3547 n = min_near_over_far*f;
3548 else
3549 n = near_dist;
3550 }
3551 if ( f > far_dist || f <= target_dist )
3552 {
3553 ON_ERROR("ON_Viewport::SetFrustumNearFar arithmetic problem 4.");
3554 if ( near_dist < n && n < target_dist )
3555 f = n/min_near_over_far;
3556 else
3557 f = far_dist;
3558 }
3559
3560 if ( n < min_near_over_far*f )
3561 n = min_near_over_far*f;
3562 else
3563 f = n/min_near_over_far;
3564
3565 near_dist = n;
3566 far_dist = f;
3567 }
3568 else
3569 {
3570 near_dist = min_near_over_far*far_dist;
3571 }
3572 }
3573 else if ( ON_IsValid(target_dist) && fabs(far_dist-target_dist) > fabs(near_dist-target_dist) )
3574 {
3575 far_dist = near_dist/min_near_over_far;
3576 }
3577 else
3578 {
3579 near_dist = min_near_over_far*far_dist;
3580 }
3581 break;
3582 }
3583 }
3584 else
3585 {
3586 // parallel projection
3587 if ( far_dist <= near_dist+tiny)
3588 {
3589 double d = fabs(near_dist)*0.125;
3590 if ( d <= MIN_NEAR_DIST || d < tiny || d < min_near_dist )
3591 d = 1.0;
3592 near_dist -= d;
3593 far_dist += d;
3594 }
3595
3596 if ( near_dist < min_near_dist || near_dist < MIN_NEAR_DIST )
3597 {
3598 if ( !m_bValidCamera )
3599 return false;
3600 // move camera back in parallel projection so everything shows
3601 double h = fabs(m_frus_top - m_frus_bottom);
3602 double w = fabs(m_frus_right - m_frus_left);
3603 double r = 0.5*((h > w) ? h : w);
3604 double n = 3.0*r;
3605 if (n < 2.0*min_near_dist )
3606 n = 2.0*min_near_dist;
3607 if ( n < 2.0*MIN_NEAR_DIST )
3608 n = 2.0*MIN_NEAR_DIST;
3609 double d = n-near_dist;
3610 ON_3dPoint new_loc = CameraLocation() + d*CameraZ();
3611 SetCameraLocation(new_loc);
3612 if ( m_bValidFrustum && fabs(m_frus_near) >= d*ON_SQRT_EPSILON )
3613 {
3614 m_frus_near += d;
3615 m_frus_far += d;
3616 }
3617 near_dist = n;
3618 far_dist += d;
3619 target_dist += d;
3620 if ( far_dist < near_dist )
3621 {
3622 // could happen if d is < ON_EPSILON*far_dist
3623 far_dist = 1.125*near_dist;
3624 }
3625 }
3626 }
3627
3628 // call bare bones setter
3629 bool rc = SetFrustumNearFar( near_dist, far_dist );
3630
3631 // if depth bias will be applied, then make an attempt
3632 // to adust the frustum's near plane to prevent
3633 // clipping biased objects. This post-adjustment
3634 // fixes display bugs like # 87514.
3635 if ( rc
3636 && relative_depth_bias > 0.0 && relative_depth_bias <= 0.5
3637 && m_frus_near > min_near_dist
3638 && m_frus_far > m_frus_near
3639 && m_frus_near > MIN_NEAR_DIST
3640 )
3641 {
3642 const double near0 = m_frus_near;
3643 const double far0 = m_frus_far;
3644 double bias_3d = 1.001*relative_depth_bias*(m_frus_far - m_frus_near);
3645 double near1 = m_frus_near - bias_3d;
3646 if ( IsPerspectiveProjection() )
3647 {
3648 if ( near1 < min_near_over_far*far0 || near1 < MIN_NEAR_OVER_FAR*far0 )
3649 {
3650 if (near0 - near1 > 0.01*near0)
3651 near1 = 0.99*near0;
3652 }
3653 }
3654
3655 // It is important that this test be applied in perspective
3656 // and parallel views. Otherwise the camera location in
3657 // parallel view will creep back when SetFrustumNearFar()
3658 // is called multiple times.
3659 if ( !(near1 >= min_near_dist && near1 >= MIN_NEAR_DIST) )
3660 {
3661 near1 = (min_near_dist >= MIN_NEAR_DIST)
3662 ? min_near_dist
3663 : MIN_NEAR_DIST;
3664 }
3665
3666 if ( near1 < near0 )
3667 {
3668 #if defined(ON_DEBUG)
3669 const ON_3dPoint debug_camloc0(m_CamLoc);
3670 #endif
3671 if ( IsPerspectiveProjection() )
3672 {
3673 rc = SetFrustumNearFar( near1, far0 );
3674 if (!rc)
3675 rc = SetFrustumNearFar( near0, far0 );
3676 }
3677 else
3678 {
3679 // call this function again with relative_depth_bias = 0.0
3680 // to get cameral location positioned correctly when near1
3681 // is too small or negative.
3682 rc = SetFrustumNearFar(
3683 near1, far0,
3684 min_near_dist, min_near_over_far,
3685 target_dist,
3686 0.0
3687 );
3688 if (!rc)
3689 rc = SetFrustumNearFar(
3690 near0, far0,
3691 min_near_dist, min_near_over_far,
3692 target_dist,
3693 0.0
3694 );
3695 }
3696 #if defined(ON_DEBUG)
3697 if ( debug_camloc0 != m_CamLoc )
3698 {
3699 ON_WARNING("Relative depth bias changed camera location.");
3700 }
3701 #endif
3702 }
3703 }
3704
3705 return rc;
3706 }
3707
3708
GetFrustumLeftPlane(ON_Plane & left_plane) const3709 bool ON_Viewport::GetFrustumLeftPlane(
3710 ON_Plane& left_plane
3711 ) const
3712 {
3713 bool rc = m_bValidCamera && m_bValidFrustum;
3714 if (rc)
3715 {
3716 if ( IsPerspectiveProjection() )
3717 {
3718 ON_2dVector v(m_frus_near,m_frus_left);
3719 rc = v.Unitize();
3720 left_plane.origin = m_CamLoc;
3721 left_plane.xaxis = v.y*m_CamX - v.x*m_CamZ;
3722 left_plane.yaxis = m_CamY;
3723 left_plane.zaxis = v.x*m_CamX + v.y*m_CamZ;
3724 }
3725 else
3726 {
3727 left_plane.origin = m_CamLoc + m_frus_left*m_CamX;
3728 left_plane.xaxis = -m_CamZ;
3729 left_plane.yaxis = m_CamY;
3730 left_plane.zaxis = m_CamX;
3731 }
3732 left_plane.UpdateEquation();
3733 }
3734 return rc;
3735 }
3736
GetFrustumLeftPlaneEquation(ON_PlaneEquation & left_plane_equation) const3737 bool ON_Viewport::GetFrustumLeftPlaneEquation(
3738 ON_PlaneEquation& left_plane_equation
3739 ) const
3740 {
3741 bool rc = m_bValidCamera && m_bValidFrustum;
3742 if (rc)
3743 {
3744
3745 if ( IsPerspectiveProjection() )
3746 {
3747 ON_2dVector v(m_frus_near,m_frus_left);
3748 if ( 0 != (rc = v.Unitize()) )
3749 {
3750 left_plane_equation.ON_3dVector::operator=(v.x*m_CamX + v.y*m_CamZ);
3751 left_plane_equation.d = -left_plane_equation.ON_3dVector::operator*(m_CamLoc);
3752 }
3753 }
3754 else
3755 {
3756 left_plane_equation.ON_3dVector::operator=(m_CamX);
3757 left_plane_equation.d = -left_plane_equation.ON_3dVector::operator*(m_CamLoc + m_frus_left*m_CamX);
3758 }
3759 }
3760 return rc;
3761 }
3762
3763
GetFrustumRightPlane(ON_Plane & right_plane) const3764 bool ON_Viewport::GetFrustumRightPlane(
3765 ON_Plane& right_plane
3766 ) const
3767 {
3768 bool rc = m_bValidCamera && m_bValidFrustum;
3769 if (rc)
3770 {
3771 if ( IsPerspectiveProjection() )
3772 {
3773 ON_2dVector v(m_frus_near,-m_frus_right);
3774 rc = v.Unitize();
3775 right_plane.origin = m_CamLoc;
3776 right_plane.xaxis = v.y*m_CamX + v.x*m_CamZ;
3777 right_plane.yaxis = m_CamY;
3778 right_plane.zaxis = -v.x*m_CamX + v.y*m_CamZ;
3779 }
3780 else
3781 {
3782 right_plane.origin = m_CamLoc + m_frus_right*m_CamX;
3783 right_plane.xaxis = m_CamZ;
3784 right_plane.yaxis = m_CamY;
3785 right_plane.zaxis = -m_CamX;
3786 }
3787 right_plane.UpdateEquation();
3788 }
3789 return rc;
3790 }
3791
GetFrustumRightPlaneEquation(ON_PlaneEquation & right_plane_equation) const3792 bool ON_Viewport::GetFrustumRightPlaneEquation(
3793 ON_PlaneEquation& right_plane_equation
3794 ) const
3795 {
3796 bool rc = m_bValidCamera && m_bValidFrustum;
3797 if (rc)
3798 {
3799
3800 if ( IsPerspectiveProjection() )
3801 {
3802 ON_2dVector v(m_frus_near,-m_frus_right);
3803 if ( 0 != (rc = v.Unitize()) )
3804 {
3805 right_plane_equation.ON_3dVector::operator=(-v.x*m_CamX + v.y*m_CamZ);
3806 right_plane_equation.d = -right_plane_equation.ON_3dVector::operator*(m_CamLoc);
3807 }
3808 }
3809 else
3810 {
3811 right_plane_equation.ON_3dVector::operator=(-m_CamX);
3812 right_plane_equation.d = -right_plane_equation.ON_3dVector::operator*(m_CamLoc + m_frus_right*m_CamX);
3813 }
3814 }
3815 return rc;
3816 }
3817
3818
GetFrustumBottomPlane(ON_Plane & bottom_plane) const3819 bool ON_Viewport::GetFrustumBottomPlane(
3820 ON_Plane& bottom_plane
3821 ) const
3822 {
3823 bool rc = m_bValidCamera && m_bValidFrustum;
3824 if (rc)
3825 {
3826 if ( IsPerspectiveProjection() )
3827 {
3828 ON_2dVector v(m_frus_near,m_frus_bottom);
3829 rc = v.Unitize();
3830 bottom_plane.origin = m_CamLoc;
3831 bottom_plane.xaxis = -v.y*m_CamY + v.x*m_CamZ;
3832 bottom_plane.yaxis = m_CamX;
3833 bottom_plane.zaxis = v.x*m_CamY + v.y*m_CamZ;
3834 }
3835 else
3836 {
3837 bottom_plane.origin = m_CamLoc + m_frus_bottom*m_CamY;
3838 bottom_plane.xaxis = m_CamZ;
3839 bottom_plane.yaxis = m_CamX;
3840 bottom_plane.zaxis = m_CamY;
3841 }
3842 bottom_plane.UpdateEquation();
3843 }
3844 return rc;
3845 }
3846
3847
GetFrustumBottomPlaneEquation(ON_PlaneEquation & bottom_plane_equation) const3848 bool ON_Viewport::GetFrustumBottomPlaneEquation(
3849 ON_PlaneEquation& bottom_plane_equation
3850 ) const
3851 {
3852 bool rc = m_bValidCamera && m_bValidFrustum;
3853 if (rc)
3854 {
3855
3856 if ( IsPerspectiveProjection() )
3857 {
3858 ON_2dVector v(m_frus_near,m_frus_bottom);
3859 if ( 0 != (rc = v.Unitize()) )
3860 {
3861 bottom_plane_equation.ON_3dVector::operator=(v.x*m_CamY + v.y*m_CamZ);
3862 bottom_plane_equation.d = -bottom_plane_equation.ON_3dVector::operator*(m_CamLoc);
3863 }
3864 }
3865 else
3866 {
3867 bottom_plane_equation.ON_3dVector::operator=(m_CamY);
3868 bottom_plane_equation.d = -bottom_plane_equation.ON_3dVector::operator*(m_CamLoc + m_frus_bottom*m_CamY);
3869 }
3870 }
3871 return rc;
3872 }
3873
3874
GetFrustumTopPlane(ON_Plane & top_plane) const3875 bool ON_Viewport::GetFrustumTopPlane(
3876 ON_Plane& top_plane
3877 ) const
3878 {
3879 bool rc = m_bValidCamera && m_bValidFrustum;
3880 if (rc)
3881 {
3882 if ( IsPerspectiveProjection() )
3883 {
3884 ON_2dVector v(m_frus_near,-m_frus_top);
3885 rc = v.Unitize();
3886 top_plane.origin = m_CamLoc;
3887 top_plane.xaxis = -v.y*m_CamY - v.x*m_CamZ;
3888 top_plane.yaxis = m_CamX;
3889 top_plane.zaxis = -v.x*m_CamY + v.y*m_CamZ;
3890 }
3891 else
3892 {
3893 top_plane.origin = m_CamLoc + m_frus_top*m_CamY;
3894 top_plane.xaxis = -m_CamZ;
3895 top_plane.yaxis = m_CamX;
3896 top_plane.zaxis = -m_CamY;
3897 }
3898 top_plane.UpdateEquation();
3899 }
3900 return rc;
3901 }
3902
GetFrustumTopPlaneEquation(ON_PlaneEquation & top_plane_equation) const3903 bool ON_Viewport::GetFrustumTopPlaneEquation(
3904 ON_PlaneEquation& top_plane_equation
3905 ) const
3906 {
3907 bool rc = m_bValidCamera && m_bValidFrustum;
3908 if (rc)
3909 {
3910
3911 if ( IsPerspectiveProjection() )
3912 {
3913 ON_2dVector v(m_frus_near,-m_frus_top);
3914 if ( 0 != (rc = v.Unitize()) )
3915 {
3916 top_plane_equation.ON_3dVector::operator=(-v.x*m_CamY + v.y*m_CamZ);
3917 top_plane_equation.d = -top_plane_equation.ON_3dVector::operator*(m_CamLoc);
3918 }
3919 }
3920 else
3921 {
3922 top_plane_equation.ON_3dVector::operator=(-m_CamY);
3923 top_plane_equation.d = -top_plane_equation.ON_3dVector::operator*(m_CamLoc + m_frus_top*m_CamY);
3924 }
3925 }
3926 return rc;
3927 }
3928
GetViewScale(double * x,double * y) const3929 void ON_Viewport::GetViewScale( double* x, double* y ) const
3930 {
3931 if ( x ) *x = 1.0;
3932 if ( y ) *y = 1.0;
3933 if ( !m_clip_mods.IsIdentity()
3934 && 0.0 == m_clip_mods.m_xform[3][0]
3935 && 0.0 == m_clip_mods.m_xform[3][1]
3936 && 0.0 == m_clip_mods.m_xform[3][2]
3937 && 1.0 == m_clip_mods.m_xform[3][3]
3938 )
3939 {
3940 // 04 November 2011 S. Baer (RR93636)
3941 // See comments in SetViewScale about why we are ignoring the test for 1
3942 // on either sx or sy
3943 double sx = m_clip_mods.m_xform[0][0];
3944 double sy = m_clip_mods.m_xform[1][1];
3945 if ( sx > ON_ZERO_TOLERANCE
3946 && sy > ON_ZERO_TOLERANCE
3947 && 0.0 == m_clip_mods.m_xform[0][1]
3948 && 0.0 == m_clip_mods.m_xform[0][2]
3949 && 0.0 == m_clip_mods.m_xform[1][0]
3950 && 0.0 == m_clip_mods.m_xform[1][2]
3951 // && (1.0 == sx || 1.0 == sy )
3952 )
3953 {
3954 if ( x ) *x = sx;
3955 if ( y ) *y = sy;
3956 }
3957 }
3958 }
3959
3960 //bool ON_Viewport::ScaleView( double x, double y, double z )
3961 //{
3962 // // z ignored on purpose - it was a mistake to include z
3963 // return (!IsPerspectiveProjection()) ? SetViewScale(x,y) : false;
3964 //}
3965
SetViewScale(double x,double y)3966 bool ON_Viewport::SetViewScale( double x, double y )
3967 {
3968 // 22 May Dale Lear
3969 // View scaling should have been done by adjusting the
3970 // frustum left/right top/bottom but I was stupid and added a clipmodxform
3971 // that is more trouble than it is worth.
3972 // Someday I will fix this. In the mean time, I want all scaling requests
3973 // to flow through SetViewScale/GetViewScale so I can easly find and fix
3974 // things when I have time to do it right.
3975 // 04 November 2011 S. Baer (RR93636)
3976 // This function is used for printer calibration and it is commonly possible
3977 // to need to apply a scale in both x and y. The reason for the need of x
3978 // or y to be one is because the view scale is encoded in the clip mod xform
3979 // and it is hard to be sure that we could accurately extract these values
3980 // when calling GetViewScale. Removing the requirement to have one of the
3981 // values == 1
3982 bool rc = false;
3983 if ( !IsPerspectiveProjection()
3984 && x > ON_ZERO_TOLERANCE && ON_IsValid(x)
3985 && y > ON_ZERO_TOLERANCE && ON_IsValid(y)
3986 // && (1.0 == x || 1.0 == y)
3987 )
3988 {
3989 ON_Xform xform(1.0);
3990 xform.m_xform[0][0] = x;
3991 xform.m_xform[1][1] = y;
3992 rc = SetClipModXform(xform);
3993 }
3994 return rc;
3995 }
3996
ClipCoordDepthBias(double relative_depth_bias,double clip_z,double clip_w) const3997 double ON_Viewport::ClipCoordDepthBias( double relative_depth_bias, double clip_z, double clip_w ) const
3998 {
3999 double d;
4000 if ( m_frus_far > m_frus_near
4001 && 0.0 != relative_depth_bias
4002 && 0.0 != clip_w
4003 )
4004 {
4005 if ( ON::perspective_view == m_projection )
4006 {
4007 // To get the formula for the code in this claus:
4008 //
4009 // Set M = [Camera2Clip]*[translation by (0,0,relative_depth_bias*(f-n)]*[Clip2Camera]
4010 // Note that M maps clipping coordinates to clipping coordinates.
4011 //
4012 // Calculate M([x,y,z,w]) = [p,q,r,s]
4013 //
4014 // This function returns (r/s - z/w)*w
4015 //
4016 // If you are actually doing this calculation and trying to
4017 // get the formula used in the code below, it helps to notice
4018 // that (f+n)/(f-n) = a/b.
4019 //
4020 // Note that there "should" be a small adjustment to the
4021 // x and y coordinates that is not performed by tweaking
4022 // the z clipping coordiante
4023 // z += vp->ClipCoordDepthBias( rel_bias, z, w );
4024 // but the effect is actually better when the goal is to
4025 // make wires that are on shaded surfaces appear because
4026 // their horizons are not altered.
4027 //
4028 // This method is more complicated that adding a constant
4029 // depth buffer bias but is required for high quality images
4030 // when values of far/near get to be around 1e4 or larger.
4031 //
4032 double a = m_frus_far + m_frus_near;
4033 double b = m_frus_far - m_frus_near;
4034 double c = 0.5*relative_depth_bias/(m_frus_far*m_frus_near);
4035 double t = a + b*clip_z/clip_w;
4036 d = c*t*t*clip_w/(1.0 - c*b*t);
4037 }
4038 else
4039 {
4040 // The "2.0*" is here because clipping coordinates run from
4041 // -1 to +1, a distance of 2 units.
4042 d = 2.0*relative_depth_bias*clip_w;
4043 }
4044 }
4045 else
4046 {
4047 d = 0.0;
4048 }
4049 return d;
4050 }
4051
GetClipCoordDepthBiasXform(double relative_depth_bias,ON_Xform & clipbias) const4052 bool ON_Viewport::GetClipCoordDepthBiasXform(
4053 double relative_depth_bias,
4054 ON_Xform& clipbias
4055 ) const
4056 {
4057 bool rc = false;
4058
4059 while ( 0.0 != relative_depth_bias
4060 && m_frus_far > m_frus_near
4061 )
4062 {
4063 if ( ON::perspective_view == m_projection )
4064 {
4065 ON_Xform clip2cam, cam_delta(1.0), cam2clip;
4066 if ( !cam2clip.CameraToClip(true,m_frus_left,m_frus_right,m_frus_bottom,m_frus_top,m_frus_near,m_frus_far) )
4067 break;
4068 if ( !clip2cam.ClipToCamera(true,m_frus_left,m_frus_right,m_frus_bottom,m_frus_top,m_frus_near,m_frus_far) )
4069 break;
4070 cam_delta.m_xform[2][3] = relative_depth_bias*(m_frus_far-m_frus_near);
4071 clipbias = cam2clip*cam_delta*clip2cam;
4072 }
4073 else
4074 {
4075 clipbias.Identity();
4076 clipbias.m_xform[2][3] = 2.0*relative_depth_bias;
4077 }
4078 rc = true;
4079 break;
4080 }
4081
4082 if (!rc)
4083 clipbias.Identity();
4084
4085 return rc;
4086 }
4087
SetClipModXform(ON_Xform clip_mod_xform)4088 bool ON_Viewport::SetClipModXform( ON_Xform clip_mod_xform )
4089 {
4090 bool rc = false;
4091 ON_Xform clip_mod_inverse_xform = clip_mod_xform;
4092 rc = clip_mod_inverse_xform.Invert();
4093 if ( rc )
4094 {
4095 ON_Xform id = clip_mod_inverse_xform*clip_mod_xform;
4096 double e;
4097 int i, j;
4098 for ( i = 0; i < 4 && rc; i++ ) for ( j = 0; j < 4 && rc; j++ )
4099 {
4100 e = ( i == j ) ? 1.0 : 0.0;
4101 if ( fabs(id.m_xform[i][j] - e) > ON_SQRT_EPSILON )
4102 {
4103 rc = false;
4104 }
4105 }
4106 if (rc)
4107 {
4108 m_clip_mods = clip_mod_xform;
4109 m_clip_mods_inverse = clip_mod_inverse_xform;
4110 }
4111 }
4112 return rc;
4113 }
4114
ClipModXformIsIdentity() const4115 bool ON_Viewport::ClipModXformIsIdentity() const
4116 {
4117 return m_clip_mods.IsIdentity();
4118 }
4119
ClipModXform() const4120 ON_Xform ON_Viewport::ClipModXform() const
4121 {
4122 return m_clip_mods;
4123 }
4124
ClipModInverseXform() const4125 ON_Xform ON_Viewport::ClipModInverseXform() const
4126 {
4127 return m_clip_mods_inverse;
4128 }
4129
SetTargetPoint(ON_3dPoint target_point)4130 bool ON_Viewport::SetTargetPoint( ON_3dPoint target_point )
4131 {
4132 bool rc = (target_point.IsValid() || (ON_UNSET_POINT == target_point));
4133 if (rc)
4134 m_target_point = target_point;
4135 return rc;
4136 }
4137
FrustumCenterPoint(double target_distance) const4138 ON_3dPoint ON_Viewport::FrustumCenterPoint( double target_distance ) const
4139 {
4140 double s,dx,dy,dz;
4141 ON_3dPoint target_point = ON_3dPoint::UnsetPoint;
4142
4143 if (!m_bValidCamera || !m_bValidFrustum)
4144 return target_point;
4145
4146 if ( ON_UNSET_VALUE == target_distance && m_bValidFrustum
4147 && m_frus_near > 0.0 && m_frus_far >= m_frus_near
4148 )
4149 {
4150 target_distance = 0.5*(m_frus_near+m_frus_far);
4151 if ( target_distance < m_frus_near )
4152 target_distance = m_frus_near;
4153 else if ( target_distance > m_frus_far )
4154 target_distance = m_frus_far;
4155 }
4156
4157 if ( !ON_IsValid(target_distance) || target_distance <= 0.0 )
4158 return target_point;
4159
4160 if ( m_bValidFrustum )
4161 {
4162 s = (ON::perspective_view == m_projection && m_frus_near > 0.0)
4163 ? 0.5*target_distance/m_frus_near
4164 : 0.5;
4165 dx = FrustumIsLeftRightSymmetric()
4166 ? 0.0
4167 : s*(m_frus_right+m_frus_left);
4168 dy = FrustumIsTopBottomSymmetric()
4169 ? 0.0
4170 : s*(m_frus_top+m_frus_bottom);
4171 }
4172 else
4173 {
4174 dx = dy = 0.0;
4175 }
4176 dz = -target_distance;
4177
4178 // Done this way instead of using ON_3dPoint/ON_3dVector arithmetic so the
4179 // optimizer can generate maximum precision when using 64 bit mantissas.
4180 target_point.x = (m_CamLoc.x + dx*m_CamX.x + dy*m_CamY.x + dz*m_CamZ.x);
4181 target_point.y = (m_CamLoc.y + dx*m_CamX.y + dy*m_CamY.y + dz*m_CamZ.y);
4182 target_point.z = (m_CamLoc.z + dx*m_CamX.z + dy*m_CamY.z + dz*m_CamZ.z);
4183
4184 return target_point;
4185 }
4186
TargetPoint() const4187 ON_3dPoint ON_Viewport::TargetPoint() const
4188 {
4189 return m_target_point;
4190 }
4191
4192
TargetDistance(bool bUseFrustumCenterFallback) const4193 double ON_Viewport::TargetDistance( bool bUseFrustumCenterFallback ) const
4194 {
4195 double d = ON_UNSET_VALUE;
4196 if ( m_bValidCamera )
4197 {
4198 if ( bUseFrustumCenterFallback && !m_bValidFrustum )
4199 bUseFrustumCenterFallback = false;
4200 if ( m_target_point.IsValid() )
4201 {
4202 d = (m_CamLoc - m_target_point)*m_CamZ;
4203 if ( bUseFrustumCenterFallback && (!ON_IsValid(d) || d <= 0.0) )
4204 d = ON_UNSET_VALUE;
4205 }
4206 if ( bUseFrustumCenterFallback
4207 && ON_UNSET_VALUE == d
4208 && m_frus_far >= m_frus_near
4209 )
4210 {
4211 d = 0.5*(m_frus_near+m_frus_far);
4212 if ( d < m_frus_near ) d = m_frus_near; else if (d > m_frus_far) d = m_frus_far;
4213 if ( d <= 0.0 )
4214 d = ON_UNSET_VALUE;
4215 }
4216 }
4217 return d;
4218 }
4219
SetViewportId(const ON_UUID & id)4220 bool ON_Viewport::SetViewportId( const ON_UUID& id)
4221 {
4222 // Please discuss any code changes with Dale Lear.
4223 // You should NEVER change the viewport id once
4224 // it is set.
4225 bool rc = (0 == memcmp(&m_viewport_id,&id,sizeof(m_viewport_id)));
4226 if ( !rc && m_viewport_id == ON_nil_uuid )
4227 {
4228 m_viewport_id = id;
4229 rc = true;
4230 }
4231 return rc;
4232 }
4233
ChangeViewportId(const ON_UUID & viewport_id)4234 void ON_Viewport::ChangeViewportId(const ON_UUID& viewport_id)
4235 {
4236 m_viewport_id = viewport_id; // <- good place for a breakpoint
4237 }
4238
4239
ViewportId(void) const4240 ON_UUID ON_Viewport::ViewportId(void) const
4241 {
4242 return m_viewport_id;
4243 }
4244
4245
4246 class ON_PgonPt
4247 {
4248 public:
4249 ON_3dPoint m_P;
4250 ON_2dVector m_Q;
4251 double m_negcotangle;
4252 };
4253
4254 static
comparePptAngle(const void * pa,const void * pb)4255 int comparePptAngle( const void* pa, const void* pb )
4256 {
4257 double a = ((const ON_PgonPt*)pa)->m_negcotangle;
4258 double b = ((const ON_PgonPt*)pb)->m_negcotangle;
4259 if ( a == b )
4260 {
4261 a = ((const ON_PgonPt*)pa)->m_Q.LengthSquared();
4262 b = ((const ON_PgonPt*)pb)->m_Q.LengthSquared();
4263 }
4264 return ((a>b) ? 1 : ((a==b) ? 0 : -1));
4265 }
4266
ON_IntersectViewFrustumPlane(const ON_Viewport & vp,const ON_PlaneEquation & plane_equation,ON_SimpleArray<ON_3dPoint> & points)4267 bool ON_IntersectViewFrustumPlane(
4268 const ON_Viewport& vp,
4269 const ON_PlaneEquation& plane_equation,
4270 ON_SimpleArray<ON_3dPoint>& points
4271 )
4272 {
4273
4274 double left, right, bottom, top, near_dist, far_dist;
4275 double v[8], v0, v1, s;
4276 ON_PgonPt ppt, ppt_list[24];
4277 ON_3dPoint F[8], P0, P1, P;
4278 ON_2dVector D;
4279 const ON_3dPoint C = vp.CameraLocation();
4280 const ON_3dVector X = vp.CameraX();
4281 const ON_3dVector Y = vp.CameraY();
4282 const ON_3dVector Z = -vp.CameraZ();
4283 int e[12][2] = {{0,1},{1,2},{2,3},{3,0},
4284 {4,5},{5,6},{6,7},{7,4},
4285 {0,4},{1,5},{2,6},{3,7}};
4286 int i, i0, i1;
4287 int ppt_count = 0;
4288
4289 if ( !vp.IsValidCamera() || !vp.GetFrustum(&left,&right,&bottom,&top,&near_dist,&far_dist) )
4290 return false;
4291
4292 const ON_Plane plane(plane_equation);
4293 if ( !plane.IsValid() )
4294 return false;
4295
4296 s = ON::perspective_view == vp.Projection()
4297 ? far_dist/near_dist
4298 : 1.0;
4299 F[0] = C + left*X + bottom*Y + near_dist*Z;
4300 F[1] = C + right*X + bottom*Y + near_dist*Z;
4301 F[2] = C + right*X + top*Y + near_dist*Z;
4302 F[3] = C + left*X + top*Y + near_dist*Z;
4303
4304 F[4] = C + s*left*X + s*bottom*Y + far_dist*Z;
4305 F[5] = C + s*right*X + s*bottom*Y + far_dist*Z;
4306 F[6] = C + s*right*X + s*top*Y + far_dist*Z;
4307 F[7] = C + s*left*X + s*top*Y + far_dist*Z;
4308
4309 for ( i = 0; i < 8; i++ )
4310 {
4311 v[i] = plane_equation.ValueAt(F[i]);
4312 }
4313
4314 for ( i = 0; i < 12; i++ )
4315 {
4316 v0 = v[e[i][0]];
4317 v1 = v[e[i][1]];
4318 P0 = F[e[i][0]];
4319 P1 = F[e[i][1]];
4320 if ( (v0 <= 0.0 && v1 >= 0.0) || (v0 >= 0.0 && v1 <= 0.0) )
4321 {
4322 if ( v0 == v1 )
4323 {
4324 ppt_list[ppt_count++].m_P = P0;
4325 ppt_list[ppt_count++].m_P = P1;
4326 }
4327 else
4328 {
4329 s = v1/(v1-v0);
4330 P = s*P0 + (1.0-s)*P1;
4331 ppt_list[ppt_count++].m_P = P;
4332 }
4333 }
4334 }
4335
4336 if ( ppt_count <= 0 )
4337 return true; // plane misses frustum
4338
4339 i0 = 0;
4340 for ( i = 0; i < ppt_count; i++ )
4341 {
4342 plane.ClosestPointTo( ppt_list[i].m_P, &ppt_list[i].m_Q.x, &ppt_list[i].m_Q.y );
4343 if ( ppt_list[i].m_Q.y < ppt_list[i0].m_Q.y
4344 || (ppt_list[i].m_Q.y == ppt_list[i0].m_Q.y && ppt_list[i].m_Q.x < ppt_list[i0].m_Q.x) )
4345 i0 = i;
4346 }
4347
4348 // Use Gram scan to get the convex hull and save it in points[].
4349 // See http://en.wikipedia.org/wiki/Graham_scan for details.
4350
4351 // put point with smallest m_Q.y coordinate in ppt_list[0].
4352 ppt = ppt_list[i0];
4353 if ( i0 )
4354 {
4355 ppt_list[i0] = ppt_list[0];
4356 ppt_list[0] = ppt;
4357 i0 = 0;
4358 }
4359
4360 // sort points by the angle (ppt_list[i].m_Q = ppt_list[0].m_Q) makes
4361 // with the positve x axis. This is the same as sorting them by
4362 // -cot(angle) = -deltax/deltay.
4363 ppt_list[0].m_negcotangle = -ON_DBL_MAX; // -cot(0) = - infinity
4364 for ( i = 1; i < ppt_count; i++ )
4365 {
4366 ppt_list[i].m_Q.x -= ppt_list[0].m_Q.x;
4367 ppt_list[i].m_Q.y -= ppt_list[0].m_Q.y;
4368 ppt_list[i].m_negcotangle = (0.0 >= ppt_list[i].m_Q.y) ? -ON_DBL_MAX : -ppt_list[i].m_Q.x/ppt_list[i].m_Q.y;
4369 }
4370 ppt_list[0].m_Q.x = 0.0;
4371 ppt_list[0].m_Q.y = 0.0;
4372 ON_qsort(ppt_list+1,ppt_count-1,sizeof(ppt_list[0]),comparePptAngle);
4373
4374 points.Append(ppt_list[0].m_P);
4375 i0 = 0;
4376 i1 = 1;
4377 D = ppt_list[i1].m_Q - ppt_list[i0].m_Q;
4378
4379 for ( i = 2; i < ppt_count; i++ )
4380 {
4381 if ( (ppt_list[i].m_Q.y - ppt_list[i0].m_Q.y)*D.x <= (ppt_list[i].m_Q.x - ppt_list[i0].m_Q.x)*D.y )
4382 {
4383 // ppt_list[i0], ppt_list[i1], ppt_list[i] is a "right" turn or colinear.
4384 // Drop ppt_list[i1].
4385 i1 = i;
4386 }
4387 else
4388 {
4389 // ppt_list[i0], ppt_list[i1], ppt_list[i] is a "left" turn.
4390 points.Append(ppt_list[i1].m_P);
4391 i0 = i1;
4392 i1 = i;
4393 }
4394 D = ppt_list[i1].m_Q - ppt_list[i0].m_Q;
4395 }
4396
4397 if ( i1 > i0 )
4398 points.Append(ppt_list[i1].m_P);
4399
4400 return true;
4401 }
4402
GetPerspectiveClippingPlaneConstraints(ON_3dPoint camera_location,unsigned int depth_buffer_bit_depth,double * min_near_dist,double * min_near_over_far)4403 void ON_Viewport::GetPerspectiveClippingPlaneConstraints(
4404 ON_3dPoint camera_location,
4405 unsigned int depth_buffer_bit_depth,
4406 double* min_near_dist,
4407 double* min_near_over_far
4408 )
4409 {
4410 double nof, n, d;
4411
4412 if ( camera_location.IsValid() )
4413 {
4414 /*
4415
4416 // This code was used prior to 14 July 2011.
4417 //
4418 d = camera_location.DistanceTo(ON_3dPoint::Origin);
4419 if ( d >= 1.0e5 )
4420 {
4421 if ( depth_buffer_bit_depth >= 32 )
4422 depth_buffer_bit_depth -= 24;
4423 else
4424 depth_buffer_bit_depth = 8;
4425 }
4426 else if ( d >= 1.0e4 )
4427 {
4428 if ( depth_buffer_bit_depth >= 24 )
4429 depth_buffer_bit_depth -= 16;
4430 else
4431 depth_buffer_bit_depth = 8;
4432 }
4433 else if ( d >= 1.0e3 )
4434 {
4435 if ( depth_buffer_bit_depth >= 16 )
4436 depth_buffer_bit_depth -= 8;
4437 else
4438 depth_buffer_bit_depth = 8;
4439 }
4440 */
4441
4442 // 14 July 2011 - Dale Lear
4443 // The reductions above were too harsh and were
4444 // generating clipping artifacts in the perspective
4445 // view in bug report 88216. Changing to
4446 // to the code below gets rid of those
4447 // artifacts at the risk of having a meaninless
4448 // view to clip transform if the transformation is
4449 // calculated with single precision numbers.
4450 // If these values require further tuning, please
4451 // discuss changes with me and attach example files
4452 // to bug report 88216.
4453 d = camera_location.MaximumCoordinate();
4454 if ( d > 1.0e6 && depth_buffer_bit_depth >= 16 )
4455 depth_buffer_bit_depth -= 8;
4456 }
4457
4458 if ( depth_buffer_bit_depth >= 32 )
4459 {
4460 nof = 0.0001;
4461 n = 0.001;
4462 }
4463 else if ( depth_buffer_bit_depth >= 24 )
4464 {
4465 nof = 0.0005;
4466 n = 0.005;
4467 }
4468 else if ( depth_buffer_bit_depth >= 16 )
4469 {
4470 nof = 0.005;
4471 n = 0.005;
4472 }
4473 else
4474 {
4475 nof = 0.01;
4476 n = 0.01;
4477 }
4478
4479 if ( min_near_dist )
4480 *min_near_dist = n;
4481 if ( min_near_over_far )
4482 *min_near_over_far = nof;
4483 }
4484