如何在旋转画布上精确绘制橡皮筋选择矩形?

时间:2014-11-09 15:12:30

标签: c# graphics gdi

这是在画布上绘制的橡皮筋选择矩形。我的问题是,如果画布内容没有旋转,很容易获得正确的矩形大小。但是一旦旋转,矩形就不再与光标一起变大。我需要橡皮筋与屏幕保持平行

   var dragPt = new PointF(e.Position.X - G.ReferenceOffset.X, e.Position.Y - G.ReferenceOffset.Y);

    var rotation = ADEEnvironment.RotateAngle;

    var width = (dragPt.X - pressPt.X);
    var height = (dragPt.Y - pressPt.Y);

代码非常简单。我在鼠标按下时捕获鼠标的位置:pressPt。在鼠标移动事件中,我获取当前鼠标位置dragPt并计算橡皮筋矩形的宽度和高度,并使用这些值创建一个矩形,其原点位于pressPt。

如果画布的相机没有旋转,这可以正常工作。当我旋转显示器时,我需要橡皮筋与屏幕保持对齐,而不是画在画布上。它我只是留下它旋转绘制的橡皮筋。

如果我旋转橡皮筋矩形使其返回与屏幕对齐,则矩形不再正确调整大小。所以在经历了很多混乱之后我尝试了一些三角函数:

    var width = (float)((dragPt.X - pressPt.X) / Math.Cos(rotation));
    var height = (float)((dragPt.Y - pressPt.Y) / Math.Cos(rotation));

这不起作用并且变得非常混乱,因为旋转角度可以是0> 0的任何值。 360

我查看了有关如何创建选择矩形的其他代码,其中包含此问题的答案:How to make a resizeable rectangle selection tool?  但我想尽可能使用我的基本代码,因为它与我使用的图形引擎(Piccolo)有关。

我会张贴一些截图,但我无法捕捉橡皮筋。我认为这更像是一个数学问题,而且应该很容易修复,但我无法确定要对旋转显示的效果进行何种数学计算。

1 个答案:

答案 0 :(得分:2)

此代码使用Paint事件来绘制

  • 旋转画布上的一个固定矩形
  • 未经过翻新的副本
  • 未旋转的橡皮筋

  • 并检查示例rectanlge的角落


// one example 'object'
Rectangle R0 = new Rectangle(182,82,31,31);

// a few helpers
Point curMouse = Point.Empty;
Point downMouse = Point.Empty;
Rectangle RM = Rectangle.Empty;

float angle = 30;
Point center = new Point(-55, -22);

private void canvas_Paint(object sender, PaintEventArgs e)
{
    // preprare the canvas to rotate around a center point:
    e.Graphics.TranslateTransform(center.X , center.Y);
    e.Graphics.RotateTransform(angle);
    e.Graphics.TranslateTransform(-center.X, -center.Y);
    // draw one object and reset
    e.Graphics.DrawRectangle(Pens.Green, R0);
    e.Graphics.ResetTransform();

    // for testing (and hittesting): this is the unrotated obejct:
    e.Graphics.DrawRectangle(Pens.LightGray, R0);

    // allowing for any way the rubber band is drawn..
    // ..should be moved to a helper function!  
    Size S = new Size( Math.Abs(downMouse.X - curMouse.X), 
                       Math.Abs(downMouse.Y - curMouse.Y));
    Point P0 = new Point(Math.Min(downMouse.X, curMouse.X), 
                       Math.Min(downMouse.Y, curMouse.Y));
    RM = new Rectangle(P0, S);
    // the ruber band
    e.Graphics.DrawRectangle(Pens.Red, RM);

}

private void canvas_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    curMouse = e.Location;
    canvas.Invalidate();
}

private void canvas_MouseDown(object sender, MouseEventArgs e)
{
    downMouse = e.Location;
    curMouse = e.Location;
}

IMO,更有趣的部分是决定选择哪些对象。是否会计算任何交叉点还是应该完全包含它?

我找到了一个很好的旋转代码in this post并添加了一个示例来检查固定的Rectangle

当然,更复杂的对象会调用更多涉及的点列表。要获得真正准确的结果,您甚至可能需要GraphicsPaths以及他们支持的Regions上的设置操作;但也许一个简单的凸壳会做..

当然,您需要存储旋转的点而不是重新计算它们。

static Point RotatePoint(Point pointToRotate, Point centerPoint, double angleInDegrees)
{
    double angleInRadians = angleInDegrees * (Math.PI / 180);
    double cosTheta = Math.Cos(angleInRadians);
    double sinTheta = Math.Sin(angleInRadians);
    return new Point
    {
        X =
            (int)
            (cosTheta * (pointToRotate.X - centerPoint.X) -
            sinTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.X),
        Y =
            (int)
            (sinTheta * (pointToRotate.X - centerPoint.X) +
            cosTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.Y)
    };
}

private void canvas_MouseUp(object sender, MouseEventArgs e)
{

    List<Point> points = new List<Point>();
    points.Add(RotatePoint(new Point(R0.Left, R0.Top), center, angle));
    points.Add(RotatePoint(new Point(R0.Right, R0.Top), center, angle) );
    points.Add(RotatePoint(new Point(R0.Right, R0.Bottom), center, angle) );
    points.Add(RotatePoint(new Point(R0.Left, R0.Bottom), center, angle));

    bool ok = true;
    foreach (Point pt in points) if (!RM.Contains(pt)) ok = false;

    if (ok) this.Text = "HIT"; else this.Text = "no hit";
}