C++ 在特定位置缩放图像 - 居中

C++ Zoom Image at specific location - centered

所以我想给地图添加纹理,但问题是我不太明白为什么在使用不同的缩放尺寸缩放时它没有到达正确的位置。

我通常会尝试将背景上的纹理位置设置为我的位置,使自己在框架中居中:例如,我的纹理大小为 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

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

这是我前段时间写的一个例子,展示了如何进行允许平移和缩放 dc 的计算。

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

    // 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,看起来像这样: