diff options
author | Daria Volvenkova <d.volvenkova@corp.mail.ru> | 2016-06-20 15:09:09 +0300 |
---|---|---|
committer | Daria Volvenkova <d.volvenkova@corp.mail.ru> | 2016-07-07 15:31:08 +0300 |
commit | fdf4f75816a9ba288d3d0d5108f13054890ad555 (patch) | |
tree | 262483bcadefe5b8e3f927853625e5d109ccd425 /geometry | |
parent | eb300949c0f0a17b14976fac3ed047a3ae3e43df (diff) |
Automatic perspective angle calculation in ScreenBase.
Diffstat (limited to 'geometry')
-rw-r--r-- | geometry/rect2d.hpp | 6 | ||||
-rw-r--r-- | geometry/screenbase.cpp | 164 | ||||
-rw-r--r-- | geometry/screenbase.hpp | 20 |
3 files changed, 134 insertions, 56 deletions
diff --git a/geometry/rect2d.hpp b/geometry/rect2d.hpp index d49fa049d0..57f7b676b3 100644 --- a/geometry/rect2d.hpp +++ b/geometry/rect2d.hpp @@ -288,6 +288,12 @@ namespace m2 } template <typename T> + inline bool IsEqualSize(Rect<T> const & r1, Rect<T> const & r2, double epsX, double epsY) + { + return fabs(r1.SizeX() - r2.SizeX()) < epsX && fabs(r1.SizeY() - r2.SizeY()) < epsY; + } + + template <typename T> inline m2::Rect<T> const Add(m2::Rect<T> const & r, m2::Point<T> const & p) { return m2::Rect<T>( diff --git a/geometry/screenbase.cpp b/geometry/screenbase.cpp index 7dc94b4efd..fb462fe11c 100644 --- a/geometry/screenbase.cpp +++ b/geometry/screenbase.cpp @@ -16,18 +16,19 @@ double constexpr kEndPerspectiveScale1 = 0.3e-5; double constexpr kEndPerspectiveScale2 = 0.13e-5; ScreenBase::ScreenBase() : - m_PixelRect(0, 0, 640, 480), + m_ViewportRect(0, 0, 640, 480), + m_PixelRect(m_ViewportRect), m_Scale(0.1), m_Angle(0.0), m_Org(320, 240), - m_3dFOV(0.0), + m_3dFOV(kPerspectiveAngleFOV), m_3dNearZ(0.001), m_3dFarZ(0.0), m_3dAngleX(0.0), m_3dMaxAngleX(0.0), - m_3dScaleX(1.0), - m_3dScaleY(1.0), + m_3dScale(1.0), m_isPerspective(false), + m_isAutoPerspective(true), m_GlobalRect(m_Org, ang::AngleD(0), m2::RectD(-320, -240, 320, 240)), m_ClipRect(m2::RectD(0, 0, 640, 480)) { @@ -43,7 +44,7 @@ ScreenBase::ScreenBase(m2::RectI const & pxRect, m2::AnyRectD const & glbRect) ScreenBase::ScreenBase(ScreenBase const & s, m2::PointD const & org, double scale, double angle) - : m_PixelRect(s.m_PixelRect), + : m_ViewportRect(s.m_ViewportRect), m_Scale(scale), m_Angle(angle), m_Org(org) { UpdateDependentParameters(); @@ -51,6 +52,8 @@ ScreenBase::ScreenBase(ScreenBase const & s, void ScreenBase::UpdateDependentParameters() { + m_PixelRect = CalculatePixelRect(m_Scale); + m_PtoG = math::Shift( /// 5. shifting on (E0, N0) math::Rotate( /// 4. rotating on the screen angle math::Scale( /// 3. scaling to translate pixel sizes to global @@ -79,38 +82,46 @@ void ScreenBase::UpdateDependentParameters() m_GlobalRect = m2::AnyRectD(m_Org, m_Angle, m2::RectD(-szX, -szY, szX, szY)); m_ClipRect = m_GlobalRect.GetGlobalRect(); + + double const kEps = 1e-5; + double angle = CalculatePerspectiveAngle(m_Scale); + m_isPerspective = angle > 0.0; + if (fabs(angle - m_3dAngleX) > kEps) + { + m_3dMaxAngleX = angle; + m_3dScale = CalculateScale3d(angle); + SetRotationAngle(angle); + } } -double ScreenBase::CalculatePerspectiveAngle(double scale) +double ScreenBase::CalculatePerspectiveAngle(double scale) const { + if (!m_isAutoPerspective) + return m_3dAngleX; + if (scale > kStartPerspectiveScale1) return 0.0; - else if (scale > kEndPerspectiveScale1) + + if (scale > kEndPerspectiveScale1) { double const k = (kStartPerspectiveScale1 - scale) / (kStartPerspectiveScale1 - kEndPerspectiveScale1); return kMaxPerspectiveAngle1 * k; } - else if (scale > kEndPerspectiveScale2) + + if (scale > kEndPerspectiveScale2) { double const k = (kEndPerspectiveScale1 - scale) / (kEndPerspectiveScale1 - kEndPerspectiveScale2); return kMaxPerspectiveAngle1 + (kMaxPerspectiveAngle2 - kMaxPerspectiveAngle1) * k; } - else - return kMaxPerspectiveAngle2 * 0.99; + + return kMaxPerspectiveAngle2 * 0.99; } void ScreenBase::UpdatePerspectiveParameters() { double const angle = CalculatePerspectiveAngle(m_Scale); if (angle > 0.0) - { - if (!m_isPerspective || (angle < kMaxPerspectiveAngle1 && m_3dMaxAngleX > kMaxPerspectiveAngle1)) - ApplyPerspective(angle, kMaxPerspectiveAngle1, kPerspectiveAngleFOV); - else if (angle > m_3dMaxAngleX) - ApplyPerspective(angle, kMaxPerspectiveAngle2, kPerspectiveAngleFOV); - else - SetRotationAngle(angle); - } + ApplyPerspective(angle, angle, kPerspectiveAngleFOV); else if (m_isPerspective) ResetPerspective(); } @@ -132,6 +143,48 @@ void ScreenBase::SetFromRect(m2::AnyRectD const & glbRect) SetFromRects(glbRect, m_PixelRect); } +void ScreenBase::SetFromRect2d(m2::AnyRectD const & glbRect) +{ + double hScale = glbRect.GetLocalRect().SizeX() / PixelRectIn3d().SizeX(); + double vScale = glbRect.GetLocalRect().SizeY() / PixelRectIn3d().SizeY(); + double scale = max(hScale, vScale); + + m_Scale = scale; + m_Angle = glbRect.Angle(); + m_Org = glbRect.GlobalCenter(); + + UpdateDependentParameters(); + UpdatePerspectiveParameters(); + +/* m2::PointD g_target(0.0, -glbRect.GetLocalRect().SizeY() * scale / vScale / 2.0); + g_target.Rotate(glbRect.Angle().val()); + g_target = glbRect.GlobalCenter() + g_target; + m2::PointD p_target(m_PixelRect.SizeX() / 2.0, m_PixelRect.SizeY()); + MatchGandP(g_target, p_target);*/ + + MatchGandP3d(glbRect.GlobalCenter(), PixelRectIn3d().Center()); +} + +void ScreenBase::SetFromParams(m2::PointD const & org, double angle, double scale) +{ + m_Scale = scale; + m_Angle = angle; + m_Org = org; + UpdateDependentParameters(); +} + +void ScreenBase::MatchGandP(m2::PointD const & g, m2::PointD const & p) +{ + m2::PointD g_current = PtoG(p); + SetOrg(m_Org - g_current + g); +} + +void ScreenBase::MatchGandP3d(m2::PointD const & g, m2::PointD const &p3d) +{ + m2::PointD g_current = PtoG(P3dtoP(p3d)); + SetOrg(m_Org - g_current + g); +} + void ScreenBase::SetOrg(m2::PointD const & p) { m_Org = p; @@ -164,7 +217,7 @@ void ScreenBase::Rotate(double angle) void ScreenBase::OnSize(m2::RectI const & r) { - m_PixelRect = m2::RectD(r); + m_ViewportRect = m2::RectD(r); UpdateDependentParameters(); } @@ -173,11 +226,6 @@ void ScreenBase::OnSize(int x0, int y0, int w, int h) OnSize(m2::RectI(x0, y0, x0 + w, y0 + h)); } -double ScreenBase::GetMinPixelRectSize() const -{ - return min(m_PixelRect.SizeX(), m_PixelRect.SizeY()); -} - void ScreenBase::SetScale(double scale) { m_Scale = scale; @@ -294,6 +342,32 @@ void ScreenBase::ExtractGtoPParams(MatrixT const & m, dy = m(2, 1); } +double ScreenBase::CalculateScale3d(double rotationAngle) const +{ + double const halfFOV = m_3dFOV / 2.0; + double const cameraZ = 1.0 / tan(halfFOV); + + // Ratio of the expanded plane's size to the original size. + double const y3dScale = cos(rotationAngle) + sin(rotationAngle) * tan(halfFOV + rotationAngle); + double const x3dScale = 1.0 + 2 * sin(rotationAngle) * cos(halfFOV) / (cameraZ * cos(halfFOV + rotationAngle)); + + return max(x3dScale, y3dScale); +} + +m2::RectD ScreenBase::CalculatePixelRect(double scale) const +{ + double const angle = CalculatePerspectiveAngle(scale); + if (angle > 0.0) + { + double const scale3d = CalculateScale3d(angle); + + return m2::RectD(m2::PointD(0.0, 0.0), + m2::PointD(m_ViewportRect.maxX(), m_ViewportRect.maxY()) * scale3d); + } + + return m_ViewportRect; +} + // Place the camera at the distance, where it gives the same view of plane as the // orthogonal projection does. Calculate what part of the map would be visible, // when it is rotated through maxRotationAngle around its near horizontal side. @@ -304,31 +378,21 @@ void ScreenBase::ApplyPerspective(double currentRotationAngle, double maxRotatio ASSERT_GREATER_OR_EQUAL(maxRotationAngle, 0.0, ()); ASSERT_LESS(maxRotationAngle, math::pi2, ()); - if (m_isPerspective) - ResetPerspective(); +// if (m_isPerspective) +// ResetPerspective(); m_isPerspective = true; m_3dMaxAngleX = maxRotationAngle; m_3dFOV = angleFOV; - double const halfFOV = m_3dFOV / 2.0; - double const cameraZ = 1.0 / tan(halfFOV); - - // Ratio of the expanded plane's size to the original size. - m_3dScaleY = cos(m_3dMaxAngleX) + sin(m_3dMaxAngleX) * tan(halfFOV + m_3dMaxAngleX); - m_3dScaleX = 1.0 + 2 * sin(m_3dMaxAngleX) * cos(halfFOV) / (cameraZ * cos(halfFOV + m_3dMaxAngleX)); - - m_3dScaleX = m_3dScaleY = max(m_3dScaleX, m_3dScaleY); - - double const dy = m_PixelRect.SizeY() * (m_3dScaleX - 1.0); - - m_PixelRect.setMaxX(m_PixelRect.maxX() * m_3dScaleX); - m_PixelRect.setMaxY(m_PixelRect.maxY() * m_3dScaleY); - - Move(0.0, dy / 2.0); + double const old_dy = m_ViewportRect.SizeY() * (m_3dScale - 1.0); + m_3dScale = CalculateScale3d(m_3dMaxAngleX); + double const new_dy = m_ViewportRect.SizeY() * (CalculateScale3d(m_3dMaxAngleX) - 1.0); SetRotationAngle(currentRotationAngle); + + Move(0.0, (new_dy - old_dy) / 2.0); } // Place the camera at the distance, where it gives the same view of plane as the @@ -347,12 +411,12 @@ void ScreenBase::SetRotationAngle(double rotationAngle) double const halfFOV = m_3dFOV / 2.0; double const cameraZ = 1.0 / tan(halfFOV); - double const offsetZ = cameraZ + sin(m_3dAngleX) * m_3dScaleY; - double const offsetY = cos(m_3dAngleX) * m_3dScaleX - 1.0; + double const offsetZ = cameraZ + sin(m_3dAngleX) * m_3dScale; + double const offsetY = cos(m_3dAngleX) * m_3dScale - 1.0; Matrix3dT scaleM = math::Identity<double, 4>(); - scaleM(0, 0) = m_3dScaleX; - scaleM(1, 1) = m_3dScaleY; + scaleM(0, 0) = m_3dScale; + scaleM(1, 1) = m_3dScale; Matrix3dT rotateM = math::Identity<double, 4>(); rotateM(1, 1) = cos(m_3dAngleX); @@ -365,7 +429,7 @@ void ScreenBase::SetRotationAngle(double rotationAngle) translateM(3, 2) = offsetZ; Matrix3dT projectionM = math::Zero<double, 4>(); - m_3dFarZ = cameraZ + 2.0 * sin(m_3dAngleX) * m_3dScaleY; + m_3dFarZ = cameraZ + 2.0 * sin(m_3dAngleX) * m_3dScale; projectionM(0, 0) = projectionM(1, 1) = cameraZ; projectionM(2, 2) = m_3dAngleX != 0.0 ? (m_3dFarZ + m_3dNearZ) / (m_3dFarZ - m_3dNearZ) : 0.0; @@ -381,14 +445,14 @@ void ScreenBase::ResetPerspective() { m_isPerspective = false; - double const dy = m_PixelRect.SizeY() * (1.0 - 1.0 / m_3dScaleX); + double const dy = m_PixelRect.SizeY() * (1.0 - 1.0 / m_3dScale); - m_PixelRect.setMaxX(m_PixelRect.maxX() / m_3dScaleX); - m_PixelRect.setMaxY(m_PixelRect.maxY() / m_3dScaleY); + m_PixelRect.setMaxX(m_PixelRect.maxX() / m_3dScale); + m_PixelRect.setMaxY(m_PixelRect.maxY() / m_3dScale); Move(0, -dy / 2.0); - m_3dScaleX = m_3dScaleY = 1.0; + m_3dScale = 1.0; m_3dAngleX = 0.0; m_3dMaxAngleX = 0.0; m_3dFOV = 0.0; diff --git a/geometry/screenbase.hpp b/geometry/screenbase.hpp index 8f99f76dbd..c3cf5eefe2 100644 --- a/geometry/screenbase.hpp +++ b/geometry/screenbase.hpp @@ -15,6 +15,7 @@ public: using Vector3dT = math::Matrix<double, 1, 4>; private: + m2::RectD m_ViewportRect; m2::RectD m_PixelRect; double m_Scale; @@ -26,9 +27,9 @@ private: double m_3dFarZ; double m_3dAngleX; double m_3dMaxAngleX; - double m_3dScaleX; - double m_3dScaleY; + double m_3dScale; bool m_isPerspective; + bool m_isAutoPerspective; protected: /// @group Dependent parameters @@ -64,6 +65,9 @@ public: m2::PointD const & org, double scale, double angle); void SetFromRect(m2::AnyRectD const & rect); + void SetFromRect2d(m2::AnyRectD const & glbRect); + + void SetFromParams(m2::PointD const & org, double angle, double scale); void SetFromRects(m2::AnyRectD const & glbRect, m2::RectD const & pxRect); void SetOrg(m2::PointD const & p); @@ -115,6 +119,9 @@ public: void GtoP(m2::RectD const & gr, m2::RectD & sr) const; void PtoG(m2::RectD const & pr, m2::RectD & gr) const; + void MatchGandP(m2::PointD const & g, m2::PointD const & p); + void MatchGandP3d(m2::PointD const & g, m2::PointD const & p3d); + void GetTouchRect(m2::PointD const & pixPoint, double pixRadius, m2::AnyRectD & glbRect) const; void GetTouchRect(m2::PointD const & pixPoint, double const pxWidth, double const pxHeight, m2::AnyRectD & glbRect) const; @@ -134,7 +141,7 @@ public: double GetRotationAngle() const { return m_3dAngleX; } double GetMaxRotationAngle() const { return m_3dMaxAngleX; } double GetAngleFOV() const { return m_3dFOV; } - double GetScale3d() const { return m_3dScaleX; } + double GetScale3d() const { return m_3dScale; } m2::PointD P3dtoP(m2::PointD const & pt) const; @@ -148,17 +155,18 @@ public: m2::RectD PixelRectIn3d() const { - return m2::RectD(0.0, 0.0, m_PixelRect.maxX() / m_3dScaleX, m_PixelRect.maxY() / m_3dScaleY); + return m_ViewportRect; } - double GetMinPixelRectSize() const; + double CalculateScale3d(double rotationAngle) const; + m2::RectD CalculatePixelRect(double scale) const; + double CalculatePerspectiveAngle(double scale) const; /// Compute arbitrary pixel transformation, that translates the (oldPt1, oldPt2) -> (newPt1, newPt2) static MatrixT const CalcTransform(m2::PointD const & oldPt1, m2::PointD const & oldPt2, m2::PointD const & newPt1, m2::PointD const & newPt2, bool allowRotate); - static double CalculatePerspectiveAngle(double scale); /// Setting GtoP matrix extracts the Angle and m_Org parameters, leaving PixelRect intact void SetGtoPMatrix(MatrixT const & m); |