/* $NoKeywords: $ */ /* // // Copyright (c) 1993-2013 Robert McNeel & Associates. All rights reserved. // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert // McNeel & Associates. // // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF // MERCHANTABILITY ARE HEREBY DISCLAIMED. // // For complete openNURBS copyright information see . // //////////////////////////////////////////////////////////////// */ #include "opennurbs.h" #if !defined(ON_COMPILING_OPENNURBS) // This check is included in all opennurbs source .c and .cpp files to insure // ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled. // When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined // and the opennurbs .h files alter what is declared and how it is declared. #error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs #endif ON_AerialPhotoImage::ON_AerialPhotoImage() : m_id(ON_nil_uuid) , m_image_width_pixels(0) , m_image_height_pixels(0) { } void ON_AerialPhotoImage::Unset() { m_name.Destroy(); m_id = ON_nil_uuid; m_camera_position.Unset(); m_image_frustum.Unset(); m_image_file_name.Destroy(); m_image_width_pixels = 0; m_image_height_pixels = 0; } void ON_AerialPhotoImage::SetName( const wchar_t* name ) { m_name = name; } void ON_AerialPhotoImage::GetName( ON_wString& name ) const { name = m_name.Array(); } void ON_AerialPhotoImage::SetId( ON_UUID id ) { m_id = id; } ON_UUID ON_AerialPhotoImage::Id() const { return m_id; } bool ON_AerialPhotoImage::CameraPositionIsSet() const { return m_camera_position.IsSet(); } bool ON_AerialPhotoImage::CameraLocationIsSet() const { return m_camera_position.LocationIsSet(); } bool ON_AerialPhotoImage::CameraOrientationIsSet() const { return m_camera_position.OrientationIsSet(); } void ON_AerialPhotoImage::SetCameraPosition( ON_AerialPhotoCameraPosition camera_position ) { m_camera_position = camera_position; } void ON_AerialPhotoImage::GetCameraPosition( ON_AerialPhotoCameraPosition& camera_position ) const { camera_position = m_camera_position; } void ON_AerialPhotoImage::UnsetCameraPosition() { m_camera_position.Unset(); } ///////////////////////////////////////////////////////// // // Image frustum interface // bool ON_AerialPhotoImage::ImageFrustumIsSet() const { return m_image_frustum.IsSet(); } void ON_AerialPhotoImage::SetImageFrustum( ON_AerialPhotoImageFrustum image_frustum ) { m_image_frustum = image_frustum; } void ON_AerialPhotoImage::GetImageFrustum( ON_AerialPhotoImageFrustum& image_frustum ) const { image_frustum = m_image_frustum; } void ON_AerialPhotoImage::UnsetImageFrustum() { m_image_frustum.Unset(); } bool ON_AerialPhotoImage::ImageFileNameIsSet() const { return (m_image_file_name.Length() > 0); } bool ON_AerialPhotoImage::ImageSizeIsSet() const { return (m_image_width_pixels > 0 && m_image_height_pixels > 0); } bool ON_AerialPhotoImage::SetImageSize( int image_width_pixels, int image_height_pixels ) { bool rc = false; if ( image_width_pixels > 0 && image_height_pixels > 0 ) { m_image_width_pixels = image_width_pixels; m_image_height_pixels = image_height_pixels; rc = true; } else UnsetImageSize(); return rc; } bool ON_AerialPhotoImage::GetImageSize( int* width_pixels, int* height_pixels ) const { if ( 0 != width_pixels ) *width_pixels = m_image_width_pixels; if ( 0 != height_pixels ) *height_pixels = m_image_height_pixels; return ImageSizeIsSet(); } void ON_AerialPhotoImage::UnsetImageSize() { m_image_width_pixels = 0; m_image_height_pixels = 0; } void ON_AerialPhotoImage::SetImageFileName( const wchar_t* image_file_name ) { m_image_file_name = image_file_name; } void ON_AerialPhotoImage::GetImageFileName( ON_wString& image_file_name ) const { // copy array const wchar_t* s = static_cast< const wchar_t* >(m_image_file_name); image_file_name = s; } void ON_AerialPhotoImage::UnsetImageFileName() { m_image_file_name.Destroy(); } ON_AerialPhotoImageFrustum::ON_AerialPhotoImageFrustum() { Unset(); } bool ON_AerialPhotoImageFrustum::IsSet() const { return HeightIsSet() && CornersAreSet() && UnitSystemIsSet(); } void ON_AerialPhotoImageFrustum::Unset() { m_height = ON_UNSET_VALUE; m_corners[0] = ON_2dPoint::UnsetPoint; m_corners[1] = ON_2dPoint::UnsetPoint; m_corners[2] = ON_2dPoint::UnsetPoint; m_corners[3] = ON_2dPoint::UnsetPoint; m_unit_system = ON_UnitSystem::None; } bool ON_AerialPhotoImageFrustum::HeightIsSet() const { return (m_height > 0.0 && ON_IsValid(m_height)); } bool ON_AerialPhotoImageFrustum::CornersAreSet() const { for(;;) { if ( !m_corners[0].IsValid() ) break; if ( !m_corners[1].IsValid() ) break; if ( !m_corners[2].IsValid() ) break; if ( !m_corners[3].IsValid() ) break; // corners must form a non-empty convex 2d region and // be in counter-clockwise order. They can be a // the corners of a 3 or 4 sided region. double max_z = 0.0; double z; ON_2dVector A, B; for ( int i = 0; i < 4; i++ ) { A = m_corners[(i+3)%4] - m_corners[i]; B = m_corners[(i+1)%4] - m_corners[i]; z = B.x*A.y - A.x*B.y; if (z >= 0.0 && ON_IsValid(z)) { if ( z > max_z ) max_z = z; continue; } return false; } if ( max_z > 0.0 ) return true; break; } return false; } bool ON_AerialPhotoImageFrustum::UnitSystemIsSet() const { return (m_unit_system.IsSet()); } ON_AerialPhotoCameraPosition::ON_AerialPhotoCameraPosition() : m_status(0) { Unset(); } void ON_AerialPhotoCameraPosition::Unset() { UnsetUnitSystem(); UnsetLocation(); UnsetOrientation(); } bool ON_AerialPhotoCameraPosition::IsSet() const { return UnitSystemIsSet() && LocationIsSet() && OrientationIsSet(); } bool ON_AerialPhotoCameraPosition::UnitSystemIsSet() const { return m_unit_system.IsSet(); } bool ON_AerialPhotoCameraPosition::SetUnitSystem( ON::LengthUnitSystem unit_system ) { if ( ON::LengthUnitSystem::None != unit_system && ON::LengthUnitSystem::CustomUnits != unit_system && unit_system == ON::LengthUnitSystemFromUnsigned(static_cast(unit_system)) ) { m_unit_system = ON_UnitSystem(unit_system); } else Unset(); return UnitSystemIsSet(); } bool ON_AerialPhotoCameraPosition::SetUnitSystem ( ON_UnitSystem unit_system ) { if ( unit_system.IsSet() ) m_unit_system = unit_system; else Unset(); return UnitSystemIsSet(); } ON_UnitSystem ON_AerialPhotoCameraPosition::UnitSystem() const { return m_unit_system; } bool ON_AerialPhotoCameraPosition::GetUnitSystem( ON_UnitSystem& unit_system ) const { unit_system = m_unit_system; return UnitSystemIsSet(); } void ON_AerialPhotoCameraPosition::UnsetUnitSystem() { m_unit_system = ON_UnitSystem(ON::LengthUnitSystem::None); } bool ON_AerialPhotoCameraPosition::LocationIsSet() const { return m_location.IsValid(); } bool ON_AerialPhotoCameraPosition::SetLocation( ON_3dPoint camera_location ) { const bool rc = camera_location.IsValid(); if (rc) m_location = camera_location; else UnsetLocation(); return rc; } bool ON_AerialPhotoCameraPosition::GetLocation( ON_3dPoint& camera_location ) const { camera_location = Location(); return LocationIsSet(); } ON_3dPoint ON_AerialPhotoCameraPosition::Location() const { return m_location; } void ON_AerialPhotoCameraPosition::UnsetLocation() { m_status &= 0xFE; // clear bit 1 m_location = ON_3dPoint::UnsetPoint; } bool ON_AerialPhotoCameraPosition::OrientationIsSet() const { return ( 0 != (m_status & 2) && m_orientation_angles_radians.IsValid() && m_orientation_angles_degrees.IsValid() && m_orientation_right.IsValid() && m_orientation_up.IsValid() && m_orientation_direction.IsValid() && m_orientation_rotation.IsValid() ); } void ON_AerialPhotoCameraPosition::UnsetOrientation() { m_status &= 0xFD; // clear bit 2 m_orientation_angles_radians.Set(ON_UNSET_VALUE,ON_UNSET_VALUE,ON_UNSET_VALUE); m_orientation_angles_degrees.Set(ON_UNSET_VALUE,ON_UNSET_VALUE,ON_UNSET_VALUE); m_orientation_rotation = ON_Xform::Zero4x4; m_orientation_right = ON_3dVector::ZeroVector; m_orientation_up = ON_3dVector::ZeroVector; m_orientation_direction = ON_3dVector::ZeroVector; } bool ON_AerialPhotoCameraPosition::GetOrientationRotation( ON_Xform& camera_rotaion ) const { camera_rotaion = m_orientation_rotation; return (0 != (m_status & 2)); } /* Description: Get a right handed ortho normal camera frame. Parameters: camera_X - [out] world coordinate unit vector pointing to the right in the camera image = OrientationRotation * (1,0,0) camera_Y - [out] world coordinate unit vector in the camera up direction. = OrientationRotation * (0,1,0) camera_Z - [out] world coordinate unit vector pointing into the cameara (from the image toward the camera). = OrientationRotation * (0,0,1) */ bool ON_AerialPhotoCameraPosition::GetOrientationFrame( ON_3dVector& camera_X, ON_3dVector& camera_Y, ON_3dVector& camera_Z ) const { camera_X.x = m_orientation_rotation.m_xform[0][0]; camera_X.y = m_orientation_rotation.m_xform[1][0]; camera_X.z = m_orientation_rotation.m_xform[2][0]; camera_Y.x = m_orientation_rotation.m_xform[0][1]; camera_Y.y = m_orientation_rotation.m_xform[1][1]; camera_Y.z = m_orientation_rotation.m_xform[2][1]; camera_Z.x = m_orientation_rotation.m_xform[0][2]; camera_Z.y = m_orientation_rotation.m_xform[1][2]; camera_Z.z = m_orientation_rotation.m_xform[2][2]; return (0 != (m_status & 2)); } ON_Xform ON_AerialPhotoCameraPosition::OrientationRotation() const { return m_orientation_rotation; } bool ON_AerialPhotoCameraPosition::SetOrientationVectors( ON_3dVector camera_up, ON_3dVector camera_right, ON_3dVector camera_direction ) { ON_3dVector X(camera_right); ON_3dVector Y(camera_up); ON_3dVector Z(-camera_direction); bool bHaveX = ( X.IsValid() && X.Unitize() ); bool bHaveY = ( Y.IsValid() && Y.Unitize() ); bool bHaveZ = ( Z.IsValid() && Z.Unitize() ); if ( !bHaveX && bHaveY && bHaveZ ) { X = ON_CrossProduct(Y,Z); bHaveX = X.Unitize(); if ( bHaveX ) camera_right = X; } else if ( !bHaveY && bHaveZ && bHaveX ) { Y = ON_CrossProduct(Z,X); bHaveY = Y.Unitize(); if ( bHaveY ) camera_up = Y; } else if ( !bHaveZ && bHaveX && bHaveY ) { Z = ON_CrossProduct(X,Y); bHaveZ = Z.Unitize(); if ( bHaveZ ) camera_direction = -Z; } bool rc = false; for (;;) { if ( !bHaveX || !bHaveY || !bHaveZ ) break; double max_dot = 0.0; double d = fabs(X*Y); if ( d > max_dot ) max_dot = d; d = fabs(Y*Z); if ( d > max_dot ) max_dot = d; d = fabs(Z*X); if ( d > max_dot ) max_dot = d; if ( max_dot > 1.0e-8 ) break; d = Z*ON_CrossProduct(X,Y); if ( !(d > 0.0) ) break; ON_Xform R; R.m_xform[0][0] = X.x; R.m_xform[0][1] = Y.x; R.m_xform[0][2] = Z.x; R.m_xform[0][3] = 0.0; R.m_xform[1][0] = X.y; R.m_xform[1][1] = Y.y; R.m_xform[1][2] = Z.y; R.m_xform[1][3] = 0.0; R.m_xform[2][0] = X.z; R.m_xform[2][1] = Y.z; R.m_xform[2][2] = Z.z; R.m_xform[2][3] = 0.0; R.m_xform[3][0] = 0.0; R.m_xform[3][1] = 0.0; R.m_xform[3][2] = 0.0; R.m_xform[3][3] = 1.0; rc = SetOrientationRotation(R); if (rc) { // preserve exact values of input parameters m_orientation_right = camera_right; m_orientation_up = camera_up; m_orientation_direction = camera_direction; } break; } if (!rc) { UnsetOrientation(); } return rc; } bool ON_AerialPhotoCameraPosition::SetOrientationRotation( ON_Xform camera_rotation ) { m_orientation_rotation = camera_rotation; const ON_3dVector X(camera_rotation.m_xform[0][0],camera_rotation.m_xform[1][0],camera_rotation.m_xform[2][0]); const ON_3dVector Y(camera_rotation.m_xform[0][1],camera_rotation.m_xform[1][1],camera_rotation.m_xform[2][1]); const ON_3dVector Z(camera_rotation.m_xform[0][2],camera_rotation.m_xform[1][2],camera_rotation.m_xform[2][2]); m_orientation_right = X; m_orientation_up = Y; m_orientation_direction = -Z; double s1, c1, s2, c2, s3, c3; s2 = Z.x; double a = 1.0 - s2*s2; c2 = (a > 0.0) ? sqrt(a) : 0.0; if ( fabs(c2) <= ON_EPSILON || fabs(s2) >= 1.0-ON_EPSILON ) { s2 = (s2 < 0.0) ? -1.0 : 1.0; c2 = 0.0; } else if ( fabs(s2) <= ON_EPSILON || fabs(c2) >= 1.0-ON_EPSILON ) { c2 = (c2 < 0.0) ? -1.0 : 1.0; s2 = 0.0; } if ( c2 > 0.0 ) { s1 = -Z.y/c2; c1 = Z.z/c2; s3 = -Y.x/c2; c3 = X.x/c2; } else { s1 = 0.0; c1 = 1.0; s3 = ( X.y == Y.z ) ? X.y : 0.5*(X.y + Y.z); c3 = ( Y.y == -X.z ) ? Y.y : 0.5*(Y.y - X.z); } m_orientation_angles_radians.x = atan2(s1,c1); m_orientation_angles_radians.y = atan2(s2,c2); m_orientation_angles_radians.z = atan2(s2,c3); m_orientation_angles_degrees.x = m_orientation_angles_radians.x*180.0/ON_PI; m_orientation_angles_degrees.y = m_orientation_angles_radians.y*180.0/ON_PI; m_orientation_angles_degrees.z = m_orientation_angles_radians.z*180.0/ON_PI; m_status |= 2; return true; } bool ON_AerialPhotoCameraPosition::SetOrientationAnglesDegrees( double omega_degrees, double phi_degrees, double kappa_degrees ) { bool rc = false; if ( ON_IsValid(omega_degrees) && ON_IsValid(phi_degrees) && ON_IsValid(kappa_degrees) ) { rc = SetOrientationAnglesRadians(omega_degrees*ON_PI/180.0,phi_degrees*ON_PI/180.0,kappa_degrees*ON_PI/180.0); // Directly set m_camera_angles_degrees after SetCameraAnglesRadians() // so we don't loose a few bits of precision lost by converting to radians // and then converting back to degrees. m_orientation_angles_degrees.Set(omega_degrees,phi_degrees,kappa_degrees); } else { UnsetOrientation(); } return rc; } bool ON_AerialPhotoCameraPosition::SetOrientationAnglesRadians( double omega_radians, double phi_radians, double kappa_radians ) { bool rc = false; if ( ON_IsValid(omega_radians) && ON_IsValid(phi_radians) && ON_IsValid(kappa_radians) ) { m_orientation_angles_radians.Set(omega_radians,phi_radians,kappa_radians); m_orientation_angles_degrees.Set(omega_radians*180.0/ON_PI,phi_radians*180.0/ON_PI,kappa_radians*180.0/ON_PI); const double c1 = cos(omega_radians); const double s1 = sin(omega_radians); const double c2 = cos(phi_radians); const double s2 = sin(phi_radians); const double c3 = cos(kappa_radians); const double s3 = sin(kappa_radians); const ON_3dVector X( c3*c2, s3*c1 + c3*s2*s1, s3*s1 - c3*s2*c1); const ON_3dVector Y(-s3*c2, c3*c1 - s3*s2*s1, c3*s1 + s3*s2*c1); const ON_3dVector Z( s2, -c2*s1, c2*c1); ON_Xform R; R.m_xform[0][0] = X.x; R.m_xform[0][1] = Y.x; R.m_xform[0][2] = Z.x; R.m_xform[0][3] = 0.0; R.m_xform[1][0] = X.y; R.m_xform[1][1] = Y.y; R.m_xform[1][2] = Z.y; R.m_xform[1][3] = 0.0; R.m_xform[2][0] = X.z; R.m_xform[2][1] = Y.z; R.m_xform[2][2] = Z.z; R.m_xform[2][3] = 0.0; R.m_xform[3][0] = 0.0; R.m_xform[3][1] = 0.0; R.m_xform[3][2] = 0.0; R.m_xform[3][3] = 1.0; m_orientation_rotation = R; m_orientation_right = X; m_orientation_up = Y; m_orientation_direction = -Z; m_status |= 2; rc = true; { // Debugging validation tests const ON_3dVector x0(1.0,0.0,0.0); const ON_3dVector y0(0.0,1.0,0.0); const ON_3dVector z0(0.0,0.0,1.0); const ON_3dVector x1 = x0; const ON_3dVector y1 = c1*y0 + s1*z0; const ON_3dVector z1 = -s1*y0 + c1*z0; const ON_3dVector y2 = y1; const ON_3dVector z2 = s2*x1 + c2*z1; const ON_3dVector x2 = c2*x1 - s2*z1; const ON_3dVector z3 = z2; const ON_3dVector x3 = c3*x2 + s3*y2; const ON_3dVector y3 = -s3*x2 + c3*y2; /* const ON_3dVector x1 = x0; const ON_3dVector y1 = (0.0, c1, s1); const ON_3dVector z1 = (0.0, -s1, c1); const ON_3dVector x2 = (c2, s2*s1, -s2*c1); // c2*x1 - s2*z1; const ON_3dVector y2 = (0.0, c1, s1); const ON_3dVector z2 = (s2, -c2*s1, c2*c1); // s2*x1 + c2*z1 */ ON_Xform r1; r1.Rotation(omega_radians,x0,ON_3dPoint::Origin); ON_3dVector r1y0 = r1*y0; ON_Xform r2; r2.Rotation(phi_radians,r1y0,ON_3dPoint::Origin); ON_3dVector r2r1z0 = r2*r1*z0; ON_Xform r3; r3.Rotation(kappa_radians,r2r1z0,ON_3dPoint::Origin); ON_Xform r321 = r3*r2*r1; ON_Xform zero = r321 - R; double a, b; double max_zero = 0.0; for ( int i = 0; i < 4; i++ ) for ( int j = 0; j < 4; j++ ) { a = fabs(zero[i][j]); if (a > max_zero ) max_zero = a; } b = fabs((x3-X).MaximumCoordinate()); if ( b > max_zero ) max_zero = b; b = fabs((y3-Y).MaximumCoordinate()); if ( b > max_zero ) max_zero = b; b = fabs((z3-Z).MaximumCoordinate()); if ( b > max_zero ) max_zero = b; if ( max_zero > 1.0e-15 ) ON_ERROR("Transform trauma"); } } else { UnsetOrientation(); } return rc; } bool ON_AerialPhotoImage::GetViewProjection( ON_BoundingBox target_bbox, ON_Viewport& viewport ) const { viewport = ON_Viewport::DefaultTopViewYUp; if ( !this->ImageSizeIsSet() ) return false; const int image_width_pixels = m_image_width_pixels; const int image_height_pixels = m_image_height_pixels; const ON_AerialPhotoImageFrustum& pg_if( m_image_frustum ); if ( !pg_if.IsSet() ) return false; const ON_AerialPhotoCameraPosition& pg_cp( m_camera_position ); if ( !pg_cp.LocationIsSet() ) return false; if ( !pg_cp.OrientationIsSet() ) return false; ON_3dPoint camera_location; if ( !pg_cp.GetLocation(camera_location) || !camera_location.IsValid() ) return false; ON_Xform camera_rotation; if ( !pg_cp.GetOrientationRotation(camera_rotation) || !camera_rotation.IsValid() ) return false; ON_Plane camera_plane; if (!pg_cp.GetOrientationFrame(camera_plane.xaxis,camera_plane.yaxis,camera_plane.zaxis)) return false; camera_plane.origin = camera_location; camera_plane.UpdateEquation(); const double frustum_to_position_unit_scale = ON::UnitScale( pg_if.m_unit_system, pg_cp.UnitSystem() ); if ( !ON_IsValid(frustum_to_position_unit_scale) || !(frustum_to_position_unit_scale > 0.0) ) return false; // The values of frus_near and frus_far are adjusted later // so the view's frustum contains the region in target_bbox. double frus_near = pg_if.m_height*frustum_to_position_unit_scale; if ( !ON_IsValid(frus_near) || !(frus_near > 0.0) ) return false; double frus_far = 128.0*frus_near; double frus_left, frus_right; double frus_bottom, frus_top; frus_left = frus_right = pg_if.m_corners[0].x; frus_bottom = frus_top = pg_if.m_corners[0].y; for ( size_t i = 1; i < sizeof(pg_if.m_corners)/sizeof(pg_if.m_corners[0]); i++ ) { if ( pg_if.m_corners[i].x < frus_left ) frus_left = pg_if.m_corners[i].x; else if ( pg_if.m_corners[i].x > frus_right ) frus_right = pg_if.m_corners[i].x; if ( pg_if.m_corners[i].y < frus_bottom ) frus_bottom = pg_if.m_corners[i].y; else if ( pg_if.m_corners[i].y > frus_top ) frus_top = pg_if.m_corners[i].y; } frus_left *= frustum_to_position_unit_scale; frus_right *= frustum_to_position_unit_scale; frus_bottom *= frustum_to_position_unit_scale; frus_top *= frustum_to_position_unit_scale; bool rc = false; ON_Viewport vp; for(;;) { if ( !vp.SetProjection( ON::perspective_view ) ) break; if ( !vp.SetCameraLocation( pg_cp.Location() ) ) break; if ( !vp.SetCameraDirection( -camera_plane.zaxis ) ) break; if ( !vp.SetCameraUp( camera_plane.yaxis ) ) break; if ( !vp.SetFrustum(frus_left,frus_right,frus_bottom,frus_top,frus_near,frus_far) ) break; if ( !vp.SetScreenPort(0,image_width_pixels,0,image_height_pixels) ) break; double bbox_near = ON_UNSET_VALUE; double bbox_far = ON_UNSET_VALUE; if ( vp.GetBoundingBoxDepth(target_bbox,&bbox_near,&bbox_far,false) ) { if ( frus_near > 0.0 && ON_IsValid(bbox_near) && frus_near < bbox_near ) { double s = 1.0; while ( 2.0*s*frus_near <= bbox_near ) s *= 2.0; if ( s >= 2.0 ) { frus_near *= s; frus_left *= s; frus_right *= s; frus_bottom *= s; frus_top *= s; frus_far = 1.0625*frus_near; } } if ( ON_IsValid(bbox_far) && frus_far < bbox_far ) { frus_far = 1.0625*bbox_far; const double max_frus_far = 67108864.0*frus_near; // 2^26 * frus_near if ( !(frus_far <= max_frus_far) ) frus_far = max_frus_far; } if ( !vp.SetFrustum(frus_left,frus_right,frus_bottom,frus_top,frus_near,frus_far) ) break; } rc = vp.IsValid() ? true : false; if ( rc ) viewport = vp; break; } return rc; }