草庐IT

c++ - 根据鼠标位置放大窗口

coder 2024-01-31 原文

您好,我现在正在用 C++ 编写一个 win32 应用程序,我真的很难放大我的窗口内容。这是我开始使用的伪代码来完成缩放:

// point One
int XPointOne = -200;
int YPointTwo = 0;

// point Two
int XPointTwo = 200;
int YPointTwo = 0;

// Draw point function.
DrawPoint(XCoordinate * ScalingFactor, YCoordinate * ScalingFactor) {
    ....
}

我的坐标系设置为原点在窗口的中心。我想在使用鼠标滚轮时进行缩放。上述解决方案的问题是缩放总是从窗口的中心发生。当您的鼠标不在窗口中央时,这看起来有点难看。我想放大鼠标所在的区域,但找不到合适的算法来计算 x 和 y 方向的偏移量。例如,如果鼠标的坐标为 (-200, 0),那么点一的坐标应为 (-200, 0),点二的坐标应为 (600, 0),比例因子为 2。我已经尝试了很多东西,但没有让它起作用,尤其是当鼠标在缩放之间移动到其他位置时,一切都变得一团糟。任何人都知道如何解决这个问题?

这是我的应用程序的一些示例代码。第一个片段是我处理 WM_MOUSEWHEEL 消息的回调函数。

VOID OnMouseWheel(WPARAM const& WParam, LPARAM const& LParam) {
    if(GET_WHEEL_DELTA_WPARAM(WParam) > 0)
    {
        // Zoom in
        Draw.ScaleFactor += 0.1;
    }
    else
    {
         // Zoom out
    }
}

Draw 只是一个包装 GDI 函数的类。它有一个比例因子成员。下面的代码片段是我的 Draw 对象的 DrawCircle 成员函数,它使用比例因子在屏幕上正确显示圆。

VOID DrawCircle(DOUBLE const& XCoordinate, DOUBLE const& YCoordinate, DOUBLE const& Radius, COLORREF const& Color) {
    HBRUSH Brush = CreateSolidBrush(Color);
    HBRUSH OldBrush = (HBRUSH)SelectObject(this->MemoryDC, Brush);

    Ellipse(this->MemoryDC, (INT) ((XCoordinate - Radius) * this->ScaleFactor), 
        -(INT)((YCoordinate + Radius) * this->ScaleFactor), 
         (INT)((XCoordinate + Radius) * this->ScaleFactor), 
        -(INT)((YCoordinate - Radius) * this->ScaleFactor)); 
    SelectObject(this->MemoryDC, OldBrush);
    DeleteObject(Brush);
 }

如您所见,我的 DrawCircle 函数在应用当前比例因子时没有考虑鼠标位置。

编辑

好的,我更接近解决方案了,这里是我的鼠标回调函数的更新版本。

VOID OnMouseWheel(WPARAM const& WParam, LPARAM const& LParam) {
    // Get Mouse position in real coordinates and not window coordinates.
    INT XOffset = (Window.GetClientWidth() / -2) + XMousePos;
    INT YOffset = (Window.GetClientHeight() / 2) - YMousePos;


    if(GET_WHEEL_DELTA_WPARAM(WParam) > 0)
    {
        Draw.ScaleFactor += 0.1;
        Draw.XOffsetScale = -XOffset * (Draw.ScaleFactor - 1.0);
        Draw.YOffsetScale = YOffset * (Draw.ScaleFactor - 1.0);
    }
    else
    {
        // ...
    }
}

这里是画圆的函数。

VOID DrawCircle(DOUBLE const& XCoordinate, DOUBLE const& YCoordinate, DOUBLE const& Radius, COLORREF const& Color) {
        HBRUSH Brush = CreateSolidBrush(Color);
        HBRUSH OldBrush = (HBRUSH)SelectObject(this->MemoryDC, Brush);

        Ellipse(this->MemoryDC, (INT) ((XCoordinate - Radius) * this->ScaleFactor + XOffsetScale) , 
            -(INT)((YCoordinate + Radius) * this->ScaleFactor - YOffsetScale), 
            (INT)((XCoordinate + Radius) * this->ScaleFactor + XOffsetScale), 
            -(INT)((YCoordinate - Radius) * this->ScaleFactor - YOffsetScale)); 
        SelectObject(this->MemoryDC, OldBrush);
        DeleteObject(Brush);
 }

只要我将鼠标保持在同一位置,它就可以工作,但是当我移动到另一个位置时,它不会按预期缩放一次,之后它会再次正确缩放。也许这有点帮助。

提前致谢!

已解决

好的, 我现在解决了我的问题。我只是根据鼠标位置乘以比例因子移动坐标系的原点。感谢您的回答。

最佳答案

最“通用”的解决方案使用矩阵变换,但这里有一个简化的解释。以下伪代码可能对您有所帮助:

/*
    VARIABLES (all in space coordinates, not pixel coordinates):

      input:
        viewRect = rectangle of the viewed area
        zoomFactor = factor of zoom relative to viewRect, ex 1.1
        mousePos = position of the mouse

      output:
        zoomedRect = viexRect after zoom
*/

/*
    A little schema:

      viewRect
    *-----------------------------------------------------------------------*
    |                       ^                                               |
    |                       | d_up                                          |
    |        zoomedRect     v                                               |
    |      *-----------------------------------------*                      |
    |d_left|                                         |       d_right        |
    |<---->|                mousePos                 |<-------------------->|
    |      |                    +                    |                      |
    |      |                                         |                      |
    |      |                                         |                      |
    |      *-----------------------------------------*                      |
    |                       ^                                               |
    |                       |                                               |
    |                       |                                               |
    |                       | d_down                                        |
    |                       |                                               |
    |                       v                                               |
    *-----------------------------------------------------------------------*

    dX = d_left + d_right
    dY = d_up + d_down
    The origin of rects is the upper left corner.
*/

/*
    First, find differences of size between zoomed rect and original rect
    Here, 1 / zoomFactor is used, because computations are made relative to the
    original view area, not the final rect):
*/
dX = viewRect.width * (1 - 1 / zoomFactor)
dY = viewRect.height * (1 - 1 / zoomFactor)

/*
    Second, find d_* using the position of the mouse.
    pX = position of the mouse along X axis, relative to viewRect (percentage)
    pY = position of the mouse along Y axis, relative to viewRect (percentage)
    The value of d_right and d_down is not computed because is not directly needed
    in the final result.
*/
pX = (mousePos.X - viewRect.X) / viewRect.width
pY = (mousePos.Y - viewRect.Y) / viewRect.height

d_left = pX * dX
d_up = pY * dY

/*
    Third and last, compute the output rect
*/
zoomedRect = viewRect
zoomedRect.X += d_left
zoomedRect.Y += d_up
zoomedRect.width -= dX
zoomedRect.height -= dY

// That's it!

对于您的问题,您需要将 View (您的窗口)与场景(绘制的对象)分开。您应该有一个绘制部分(或全部)场景的函数:

void drawScene(Rect viewArea);

和一个缩放区域的函数(使用之前介绍的算法):

Rect zoomArea(Rect rectToZoom, Point zoomCenter, double factor);

现在,您的回调要简单得多:

VOID OnMouseWheel(WPARAM const& WParam, LPARAM const& LParam)
{
    // Get the position of the mouse relative to the window (in percent)
    double XMouseRel = XMousePos / double(Window.GetClientWidth());
    double YMouseRel = YMousePos / double(Window.GetClientHeight());

    // Get Mouse position in scene coordinates and not window coordinates.
    // viewArea is in scene coordinates
    // window = your window or your draw information on the scene
    // The following assumes that you're using a scene with X left-to-right and
    // Y top-to-bottom.
    double XMouse = window.viewArea.width * XMouseRel + window.viewArea.upperleft.X;
    double YMouse = window.viewArea.height * YMouseRel + window.viewArea.upperleft.Y;

    // Zoom parameters
    double zFactor = 0.1 * GET_WHEEL_DELTA_WPARAM(WParam);
    Rect viewArea = getViewArea(); // or something like this
    Point zCenter(XMouse,YMouse);

    // Zoom
    Rect zoomedRect = zoomArea(viewArea,zCenter,zFactor);
    drawScene(zoomedRect);
}

关于c++ - 根据鼠标位置放大窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13316481/

有关c++ - 根据鼠标位置放大窗口的更多相关文章

  1. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  4. ruby - 正则表达式在哪个位置失败? - 2

    我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束

  5. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  6. ruby - 如何使用 Selenium Webdriver 根据 div 的内容执行操作? - 2

    我有一个使用SeleniumWebdriver和Nokogiri的Ruby应用程序。我想选择一个类,然后对于那个类对应的每个div,我想根据div的内容执行一个Action。例如,我正在解析以下页面:https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies这是一个搜索结果页面,我正在寻找描述中包含“Adoption”一词的第一个结果。因此机器人应该寻找带有className:"result"的div,对于每个检查它的.descriptiondiv是否包含单词“adoption

  7. ruby - 如何根据长度将路径数组转换为嵌套数组或散列 - 2

    我需要根据字符串路径的长度将字符串路径数组转换为符号、哈希和数组的数组给定以下数组:array=["info","services","about/company","about/history/part1","about/history/part2"]我想生成以下输出,对不同级别进行分组,根据级别的结构混合使用符号和对象。产生以下输出:[:info,:services,about:[:company,history:[:part1,:part2]]]#altsyntax[:info,:services,{:about=>[:company,{:history=>[:part1,:pa

  8. ruby - (Ruby || Python) 窗口管理器 - 2

    我想用这两种语言中的任何一种(最好是ruby​​)制作一个窗口管理器。老实说,除了我需要加载某种X模块外,我不知道从哪里开始。因此,如果有人有线索,如果您能指出正确的方向,那就太好了。谢谢 最佳答案 XCB,X的下一代API使用XML格式定义X协议(protocol),并使用脚本生成特定语言绑定(bind)。它在概念上与SWIG类似,只是它描述的不是CAPI,而是X协议(protocol)。目前,C和Python存在绑定(bind)。理论上,Ruby端口只是编写一个从XML协议(protocol)定义语言到Ruby的翻译器的问题。生

  9. ruby - 下载位置 Selenium-webdriver Cucumber Chrome - 2

    我将Cucumber与Ruby结合使用。通过Selenium-Webdriver在Chrome中运行测试时,我想将下载位置更改为测试文件夹而不是用户下载文件夹。我当前的chrome驱动程序是这样设置的:Capybara.default_driver=:seleniumCapybara.register_driver:seleniumdo|app|Capybara::Selenium::Driver.new(app,:browser=>:chrome,desired_capabilities:{'chromeOptions'=>{'args'=>%w{window-size=1920,1

  10. arrays - Ruby 数组 += vs 推送 - 2

    我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

随机推荐