2010-03-19 34 views
7

Tôi có một mũi tên được vẽ giữa hai đối tượng trên một Winform..NET Xác định chuột là trên đường vẽ giữa hai điểm tùy ý

Cách đơn giản nhất để xác định rằng con chuột của tôi hiện đang di chuột qua hoặc gần, dòng này.

Tôi đã xem xét kiểm tra xem điểm chuột có cắt một hình vuông được xác định và ngoại suy bởi hai điểm hay không, tuy nhiên điều này chỉ khả thi nếu hai điểm có giá trị x hoặc y rất giống nhau. Tôi nghĩ, vấn đề này có lẽ là nhiều hơn trong các đại số tuyến tính chứ không phải là lượng giác đơn giản, và trong khi tôi nhớ các khía cạnh đơn giản hơn của ma trận, vấn đề này vượt quá kiến ​​thức về đại số tuyến tính của tôi.

Mặt khác, nếu thư viện .NET có thể đối phó với hàm, thậm chí tốt hơn.

CHỈNH SỬA Cảm ơn câu trả lời, có một số rất tốt, tất cả đều xứng đáng được gắn thẻ là đã trả lời.

Tôi đã chọn câu trả lời của Coincoin khi được chấp nhận, vì tôi thích nó có thể được áp dụng cho bất kỳ hình dạng nào, tuy nhiên đã kết thúc việc thực hiện phương trình của Tim Robinson, vì nó xuất hiện hiệu quả hơn nhiều với một phương trình đơn giản hơn là tạo mới đường dẫn đồ họa và bút, như trong trường hợp của tôi tôi cần phải làm điều đó trênMouseMove cho 1-n mối quan hệ khác nhau (rõ ràng sẽ có một số bộ nhớ đệm và tối ưu hóa, nhưng điểm vẫn còn)

Vấn đề chính với phương trình là nó xuất hiện để điều trị các dòng như vô hạn, vì vậy tôi đã thêm một thử nghiệm giới hạn là tốt.

Mã (cắt ban đầu, tôi sẽ có lẽ neaten nó một chút), cho những người quan tâm, dưới

if (Math.Sqrt(Math.Pow(_end.X - _start.X, 2) + 
      Math.Pow(_end.Y - _start.Y, 2)) == 0) 
    { 
     _isHovering = 
      new RectangleF(e.X, e.Y, 1, 1).IntersectsWith(_bounds); 
    } 
    else 
    { 
     float threshold = 10.0f; 

     float distance = (float)Math.Abs( 
      (((_end.X - _start.X) * (_start.Y - e.Y)) - 
      ((_start.X - e.X) * (_end.Y - _start.Y)))/
      Math.Sqrt(Math.Pow(_end.X - _start.X, 2) + 
      Math.Pow(_end.Y - _start.Y, 2))); 

     _isHovering = (
      distance <= threshold && 
       new RectangleF(e.X, e.Y, 1, 1).IntersectsWith(_bounds) 
      ); 
    } 

và _bounds được định nghĩa là:

_bounds = new Rectangle(
    Math.Min(_start.X, _end.X), 
    Math.Min(_start.Y, _end.Y), 
    Math.Abs(_start.X - _end.X), Math.Abs(_start.Y - _end.Y)); 
+0

tôi cần, cũng có thể, để đưa vào tài khoản ngưỡng khi tôi làm div bởi 0 đặc biệt trường hợp kiểm tra – johnc

Trả lời

7

Nếu bạn muốn easly làm các xét nghiệm đánh vào hình vẽ tùy ý, bạn có thể tạo ra một con đường có chứa bản vẽ của bạn, sau đó rộng khẩu nạp liệu con đường và thực hiện một thử nghiệm tầm nhìn sử dụng chỉ các chức năng khung.

Ví dụ, ở đây chúng tôi tạo ra một con đường với một dòng:

GraphicsPath path = new GraphicsPath(); 

path.AddLine(x1, y1, x2, y2); 
path.CloseFigure(); 

Sau đó, mở rộng đường dẫn và tạo ra một khu vực để kiểm tra kết quả:

path.Widen(new Pen(Color.Black, 3)); 
region = new Region(path); 

Cuối cùng, kiểm tra kết quả:

region.IsVisible(point); 

Ưu điểm của phương pháp đó là có thể dễ dàng mở rộng đến splines, mũi tên, vòng cung, hoặc bất kỳ thứ gì có thể vẽ bằng GDI +. Có thể sử dụng cùng một đường dẫn trong cả logic HitTestDraw bằng cách trích xuất nó.

Dưới đây là mã kết hợp nó tất cả:

public GraphicsPath Path 
{ 
    get { 
     GraphicsPath path = new GraphicsPath(); 
     path.AddLine(x1, y1, x2, y2); 
     path.CloseFigure(); 

     return path; 
    } 
} 

bool HitTest(Point point) 
{ 
    using(Pen new pen = Pen(Color.Black, 3)) 
    using(GraphicsPaht path = Path) 
    { 
     path.Widen(pen); 

     using(Region region = new Region(path)) 
      return region.IsVisible(point); 
    } 
} 


void Draw(Graphics graphics) 
{ 
    using(Pen pen = new Pen(Color.Blue, 0)) 
    using(GraphicsPaht path = Path) 
     graphics.DrawPath(pen, path); 
} 
+0

Rất tốt. ... thực sự ... –

+0

Rất tốt, cảm ơn – johnc

0

Check-out MouseEnter (đối tượng người gửi, EventArgs e). Bẫy khi nó "vào" vùng kiểm soát.

+0

Kiểm tra MouseEnter vào những gì? Đây là một dòng được vẽ bởi đối tượng đồ họa GDI? – johnc

+0

Ah. Tôi không biết bạn đã vẽ nó trực tiếp. Nếu đó là một điều khiển winforms, nó đi kèm dây với các sự kiện chuột. Bạn có thể có thể kế thừa từ một cơ sở điều khiển và ghi đè lên bản vẽ để chọn lên. –

+0

Cảm ơn, nhưng đó là cách quá không hiệu quả để sử dụng một cơ sở kiểm soát trong trường hợp này, tôi sợ. – johnc

4

Để trả lời "Con chuột có di chuột qua dòng này không?", Bạn cần phải kiểm tra giao điểm đường thẳng. Tuy nhiên, vì bạn đang hỏi "là con chuột gần đường?", Có vẻ như bạn muốn tính toán khoảng cách giữa điểm chuột và đường kẻ.

Dưới đây là một lời giải thích hợp lý thấu đáo về khoảng cách điểm-line: http://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html

Tôi muốn nói rằng bạn cần phải thực hiện công thức này trong mã của bạn: (bị đánh cắp từ wolfram.com)

http://mathworld.wolfram.com/images/equations/Point-LineDistance2-Dimensional/NumberedEquation8.gif

đâu:

  • (x0, x0) là vị trí của con trỏ chuột
  • (x1, y1) là một đầu của dòng
  • (x2 , y2) là đầu kia của dòng
  • |n|Math.Abs(n)
  • Nửa dưới là Math.Sqrt
  • Bạn có thể bỏ qua các |v.r| nếu bạn muốn
+0

Tôi sẽ cung cấp cho nó một shot và cho bạn biết. Cảm ơn. – johnc

+0

@Tim Robinson, tôi đã triển khai phương trình, xem câu hỏi – johnc

1

Bạn cần phải xây dựng hai (danh nghĩa) đường ranh giới song song với con đường lý tưởng. Sau đó, bạn chỉ cần tính toán, cho mỗi vị trí chuột, cho dù chuột ở bên ngoài hay bên trong kênh được hình thành bởi những dòng đó.

Bạn không cần tính khoảng cách từ con chuột đến dòng chính.

+0

Ý tưởng hay [văn bản tùy ý tạo nên độ dài nhận xét bắt buộc] – johnc

2

Tôi sẽ tính phương trình Độ dốc-chặn (y = mx + b) cho dòng của tôi và sau đó sử dụng điều đó để kiểm tra tọa độ chuột. Bạn có thể dễ dàng đặt một phạm vi xung quanh y để xem bạn có "gần gũi" không.

Chỉnh sửa mẫu.

Tôi nghĩ rằng một cái gì đó như thế này hoạt động:

PointF currentPoint; 
PointF p1, p2; 
float threshold = 2.0f; 
float m = (p1.Y - p2.Y)/(p1.X - p2.X); 
float b = p1.Y - (m * p1.X); 

if (Math.Abs(((m * currentPoint.X) + b) - currentPoint.Y) <= threshold) 
{ 
    //On it. 
} 
+0

Tôi thích hiệu quả của điều này – johnc

+0

+1 Đây là những gì tôi đã đề xuất, nhưng anh ấy đã thời gian để viết ra toán. – egrunin

+1

Điều này sẽ không hoạt động đối với các đường dọc, trong đó p1.X == p2.X, vì bạn sẽ được chia cho 0. Đường thẳng đứng không có độ dốc xác định. –

Các vấn đề liên quan