【问题标题】:C++ Zoom Image at specific location - centered特定位置的 C++ 缩放图像 - 居中
【发布时间】:2021-04-29 13:34:48
【问题描述】:

所以我想为地图添加纹理,但问题是我无法完全理解为什么在使用不同缩放大小进行缩放时它没有到达正确的位置。

我通常会尝试将背景上的纹理位置设置为我的位置,使自己保持在框架的中心:例如,我的纹理大小是 1500x1600,并且我位于该图片中的 X140、Y590(是的,坐标被正确检索,因为我已经用控制台检查过),放大一些值并缩放纹理并将其位置设置为我所在的位置。

代码如下:

if (!areTexturesInit) {
        InitiateTextures();
        areTexturesInit = true;
    }

    wxBitmap bit(imageTest);
    wxPaintDC dc(this);

    double zoomSize = 0.9; // here I'm applying the zooming proportions ( 0.1 - bigger size of the texture, 0.9 - more zoomed in )

    this->SetSize(wxSize(386, 386)); // size of the main frame

    backgroundSize.x = GetSize().x; // get the size of the main frame
    backgroundSize.y = GetSize().y;

    middlePoint.x = (backgroundSize.x / 2); // calculate the middle point of the frame
    middlePoint.y = (backgroundSize.y / 2);


    mapSizeX = 25600 / -zoomSize; // scale vs zoom size
    mapSizeY = 25600 / zoomSize;

    Vector3 myPosition;

    GetPlayerPosition(&myPosition); // gets my location

    float TextureCoordinateX = middlePoint.x + (myPosition.x / mapSizeX) * backgroundSize.x;
    float TextureCoordinateY = middlePoint.y - (myPosition.y / mapSizeY) * backgroundSize.y;
    
    dc.DrawBitmap(bit, TextureCoordinateX, TextureCoordinateY);

    Vector3 myPosOnMap = PositionToMapPosition(myPosition, myPosition); // calculates my position on the map vs mapSizeX and Y & rotates vector
    dc.SetPen(wxPen(wxColor(255, 0, 0), 4));
    dc.DrawRectangle(wxRect(myPosOnMap.x, myPosOnMap.y, 2, 2)); // draws me on the map with a red square

问题是我认为我在某处搞砸了缩放部分,但我已经挠头 2 天了,我无法完全弄清楚我做错了什么/我必须做什么解决这个问题。

如果有人对我应该做什么有任何想法,我将不胜感激。

非常感谢!

我附上了一些演示,所以你可以看到我在说什么:https://imgur.com/a/U2GWSQ8

  • 第一张图片的“zoomSize”为 0.9;
  • 第二张图片的“zoomSize”为 0.67 - 这很有效,但我需要将其更改为不同的 zoomSizes,这会出现问题。

【问题讨论】:

    标签: c++ wxwidgets


    【解决方案1】:

    平移和缩放 dc 非常复杂。它需要使用 3 个独立的坐标系,而且很容易意外地在错误的坐标系中工作。

    这是我不久前写的一个例子,它展示了如何进行计算以使 dc 可以平移和缩放。

    听起来您对 pan 部分不感兴趣,因此您可以忽略所有允许用户设置自己的 pan 的东西。但是,仍然需要使用仅用于缩放的平移矢量,以便将缩放中心定位在正确的位置。

        // For compilers that support precompilation, includes "wx/wx.h".
    #include "wx/wxprec.h"
    
    #ifdef __BORLANDC__
        #pragma hdrstop
    #endif
    
    // for all others, include the necessary headers (this file is usually all you
    // need because it includes almost all "standard" wxWidgets headers)
    #ifndef WX_PRECOMP
        #include "wx/wx.h"
    #endif
    
    #include <wx/graphics.h>
    #include <wx/dcbuffer.h>
    
    class PanAndZoomCanvas:public wxWindow
    {
        public:
            PanAndZoomCanvas(wxWindow *parent,
                             wxWindowID id = wxID_ANY,
                             const wxPoint &pos=wxDefaultPosition,
                             const wxSize &size=wxDefaultSize,
                             long style=0,
                             const wxString &name="PanAndZoomCanvas");
            wxRect2DDouble GetUntransformedRect() const;
    
        protected:
            void DoDrawCanvas(wxGraphicsContext*);
    
        private:
            void OnPaint(wxPaintEvent&);
    
            void OnMouseWheel(wxMouseEvent&);
    
            void OnLeftDown(wxMouseEvent&);
            void OnMotion(wxMouseEvent&);
            void OnLeftUp(wxMouseEvent&);
            void OnCaptureLost(wxMouseCaptureLostEvent&);
    
            void ProcessPan(const wxPoint&,bool);
            void FinishPan(bool);
    
            int m_zoomFactor;
    
            wxPoint2DDouble m_panVector;
            wxPoint2DDouble m_inProgressPanVector;
            wxPoint m_inProgressPanStartPoint;
            bool m_panInProgress;
    };
    
    PanAndZoomCanvas::PanAndZoomCanvas(wxWindow *parent, wxWindowID id,
                                       const wxPoint &pos, const wxSize &size,
                                       long style, const wxString &name)
                     :wxWindow(parent,  id, pos, size,  style, name)
    {
        Bind(wxEVT_PAINT,&PanAndZoomCanvas::OnPaint,this);
        Bind(wxEVT_MOUSEWHEEL,&PanAndZoomCanvas::OnMouseWheel,this);
        Bind(wxEVT_LEFT_DOWN,&PanAndZoomCanvas::OnLeftDown,this);
    
        SetBackgroundStyle(wxBG_STYLE_PAINT);
    
        m_zoomFactor = 100;
    
        m_panVector = wxPoint2DDouble(0,0);
        m_inProgressPanStartPoint = wxPoint(0,0);
        m_inProgressPanVector = wxPoint2DDouble(0,0);
        m_panInProgress = false;
    }
    
    void PanAndZoomCanvas::DoDrawCanvas(wxGraphicsContext* gc)
    {
        gc->SetPen(*wxBLACK_PEN);
    
        wxGraphicsPath path = gc->CreatePath();
        path.MoveToPoint(100,100);
        path.AddLineToPoint(300,100);
        path.AddLineToPoint(300,300);
        path.CloseSubpath();
        gc->StrokePath(path);
    }
    
    void PanAndZoomCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
    {
        wxAutoBufferedPaintDC dc(this);
        dc.Clear();
    
        wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
    
        if ( gc )
        {
            double a = m_zoomFactor / 100.0;
            wxPoint2DDouble totalPan = m_panVector + m_inProgressPanVector;
    
            gc->Translate(-totalPan.m_x, -totalPan.m_y);
            gc->Scale(a, a);
    
            DoDrawCanvas(gc);
    
            delete gc;
        }
    }
    
    void PanAndZoomCanvas::OnMouseWheel(wxMouseEvent& event)
    {
        if ( m_panInProgress )
        {
            FinishPan(false);
        }
    
        int rot = event.GetWheelRotation();
        int delta = event.GetWheelDelta();
    
        int oldZoom = m_zoomFactor;
        m_zoomFactor += 10*(rot/delta);
    
        if ( m_zoomFactor<10 )
        {
            m_zoomFactor = 10;
        }
    
        if ( m_zoomFactor>800)
        {
            m_zoomFactor = 800;
        }
    
        double a = oldZoom / 100.0;
        double b = m_zoomFactor / 100.0;
    
        // Set the panVector so that the point below the cursor in the new
        // scaled/panned cooresponds to the same point that is currently below it.
        wxPoint2DDouble uvPoint = event.GetPosition();
        wxPoint2DDouble stPoint = uvPoint + m_panVector;
        wxPoint2DDouble xypoint  = stPoint/a;
        wxPoint2DDouble newSTPoint  = b * xypoint;
        m_panVector = newSTPoint - uvPoint;
    
        Refresh();
    }
    
    void PanAndZoomCanvas::ProcessPan(const wxPoint& pt, bool refresh)
    {
        m_inProgressPanVector = m_inProgressPanStartPoint - pt;
    
        if ( refresh )
        {
            Refresh();
        }
    }
    
    void PanAndZoomCanvas::FinishPan(bool refresh)
    {
        if ( m_panInProgress )
        {
            SetCursor(wxNullCursor);
    
            if ( HasCapture() )
            {
                ReleaseMouse();
            }
    
            Unbind(wxEVT_LEFT_UP, &PanAndZoomCanvas::OnLeftUp, this);
            Unbind(wxEVT_MOTION, &PanAndZoomCanvas::OnMotion, this);
            Unbind(wxEVT_MOUSE_CAPTURE_LOST, &PanAndZoomCanvas::OnCaptureLost, this);
    
            m_panVector += m_inProgressPanVector;
            m_inProgressPanVector = wxPoint2DDouble(0,0);
            m_panInProgress = false;
    
            if ( refresh )
            {
                Refresh();
            }
        }
    }
    
    wxRect2DDouble PanAndZoomCanvas::GetUntransformedRect() const
    {
        double a = m_zoomFactor / 100.0;
    
        wxSize sz = GetSize();
        wxPoint2DDouble zero  = m_panVector/a;
    
        return wxRect2DDouble(zero.m_x, zero.m_y, sz.GetWidth()/a, sz.GetHeight()/a);
    }
    
    void PanAndZoomCanvas::OnLeftDown(wxMouseEvent& event)
    {
        wxCursor cursor(wxCURSOR_HAND);
        SetCursor(cursor);
    
        m_inProgressPanStartPoint = event.GetPosition();
        m_inProgressPanVector = wxPoint2DDouble(0,0);
        m_panInProgress = true;
    
        Bind(wxEVT_LEFT_UP, &PanAndZoomCanvas::OnLeftUp, this);
        Bind(wxEVT_MOTION, &PanAndZoomCanvas::OnMotion, this);
        Bind(wxEVT_MOUSE_CAPTURE_LOST, &PanAndZoomCanvas::OnCaptureLost, this);
    
        CaptureMouse();
    }
    
    void PanAndZoomCanvas::OnMotion(wxMouseEvent& event)
    {
        ProcessPan(event.GetPosition(), true);
    }
    
    void PanAndZoomCanvas::OnLeftUp(wxMouseEvent& event)
    {
        ProcessPan(event.GetPosition(), false);
        FinishPan(true);
    }
    
    void PanAndZoomCanvas::OnCaptureLost(wxMouseCaptureLostEvent&)
    {
        FinishPan(true);
    }
    
    class MyFrame : public wxFrame
    {
        public:
            MyFrame(wxWindow* parent, int id = wxID_ANY, wxString title = "Demo",
                    wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize,
                    int style = wxDEFAULT_FRAME_STYLE );
    };
    
    MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos
                     , wxSize size, int style )
            :wxFrame( parent, id, title, pos, size, style )
    {
        PanAndZoomCanvas* canvas = new PanAndZoomCanvas(this);
    }
    
    class myApp : public wxApp
    {
        public:
            virtual bool OnInit()
            {
                MyFrame* frame = new MyFrame(NULL);
                frame->Show();
                return true;
            }
    };
    
    wxIMPLEMENT_APP(myApp);
    

    在 Windows 上,如下所示:

    【讨论】:

    • 谢谢,我会看看如何从您的示例中处理它。看起来比我做的要多得多。我认为放大某个点会更容易。
    • 我已经设法让纹理缩放,但我在何时平移某个给定点(我的位置与纹理 X&Y)的过程中迷失了方向。我正在使用计时器强制刷新以使帧每 100 毫秒重绘一次-> 动态更新与位置。我会看看我能用那个做什么。我会让线程打开以防更多回复。稍后我会标记你的回复。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-02
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-20
    相关资源
    最近更新 更多