Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Need Help on determining lines or some sketch is drawn on picturebox . 2

Status
Not open for further replies.

hujur

Programmer
Feb 14, 2003
20
0
0
US
Hello,

In the project user has given the ability to draw lines and boxes, free hand sketches. But now am stuck how to give them the funtionality to delete the particular sketch that they have drawn on the picture box.

How to determine that user has clicked on the line drawn on the picturebox or clicked on the blank space on the picturebox?

Thanks for the reply in advance!

Regards.
 
I just wrote code for this a few days ago for one of my projects, so here it is:

I wrote this to look for clicks on lines only. Looking for shapes would be somewhat different.

First, I needed a way to detect if I wanted to check for a line being clicked on. I used the Mousedown event for the picturebox, and checked to see if the ALT key was being held down at the same time as the click. If it was, I ran a subroutine to check the lines.

Private Sub PictureBox_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

If (Button = vbLeftButton) And ( Shift = 4 ) Then
' user is alt-clicking on picturebox, looking for a line
LineTest X, Y
End If

End Sub


The LineTest Sub looks like this. Note that I added all of the lines into a Collection as they were placed into the PictureBox, and I am using For Each to loop through them. There are other ways to do the looping.

I have set a fixed number (45) in this code, that determines the number of twips around the line that a click will still result in a valid "hit" on the line. I call this the "FuzzFactor".

Public Sub LineTest(X As Single, Y As Single)
Dim I As Integer
Dim MyLine As Line

' loop through all lines
For Each MyLine In Trunklines
' test each line to see if it has been "hit" by the click

With MyLine
' check and see if this is a straight line
If (.X1 = .X2) Then
' this is a straight vertical line
If Abs(.X1 - X) > 45 Then GoTo 1
If (.Y1 <= .Y2) And ((.Y1 - Y > 45) Or (Y - .Y2 > 45)) Then GoTo 1
If (.Y1 > .Y2) And ((.Y2 - Y > 45) Or (Y - .Y1 > 45)) Then GoTo 1

' If I made it to here, it should be within this line
' enter your code here for a successful &quot;hit&quot;

Exit Sub

ElseIf (.Y1 = .Y2) Then
' this is a straight horizontal line
If Abs(.Y1 - Y) > 45 Then GoTo 1
If (.X1 <= .X2) And ((.X1 - X > 45) Or (X - .X2 > 45)) Then GoTo 1
If (.X1 > .X2) And ((.X2 - X > 45) Or (X - .X1 > 45)) Then GoTo 1

' made it to here, it should be within this line
' enter your code here for a successful &quot;hit&quot;

Exit Sub

End If

' check any angled lines with this function
If LineHit(X, Y, .X1, .Y1, .X2, .Y2, 45) = True Then
' this click is within the fuzzfactor
' code here to handle successful hit

Exit Sub
End If
End With
1: Next MyLine

End Sub


And then I have a function that checks all angled lines.

Public Function LineHit(ByVal X As Single, ByVal Y As Single, X1 As Single, Y1 As Single, X2 As Single, Y2 As Single, FuzzFactor As Integer) As Boolean
Dim TestY As Single
Dim TestX As Single
Dim MyAngle As Double

MyAngle = Atn(Abs(Y2 - Y1) / Abs(X2 - X1))

If (X1 < X2) And (Y1 < Y2) Then
Y = Y - Y1
X = X - X1

ElseIf (X1 > X2) And (Y1 < Y2) Then
Y = Y2 - Y
X = X - X2

ElseIf (X1 > X2) And (Y1 > Y2) Then
Y = Y - Y2
X = X - X2

Else '(X1 < X2) And (Y1 > Y2)
Y = Y1 - Y
X = X - X1

End If

If MyAngle > 0.78 Then
' this is greater than ( aprox ) 45 degree angle. Use X to test
TestX = Y / Tan(MyAngle)
If Abs(TestX - X) < FuzzFactor Then LineHit = True Else LineHit = False
Else
' this is less than 45 degree angle. Use Y to test.
TestY = Tan(MyAngle) * X
If Abs(TestY - Y) < FuzzFactor Then LineHit = True Else LineHit = False
End If

End Function



That's all the code. I wrote this pretty quickly, so I'm sure there is a shorter way of doing it.

If you have any questions, please ask.

Robert


 
Hi,

I think you might be after something different, the way I interpret the question - do you simply want to know whether the user has clicked on a line that they drew in the Picture Box?

If so, use Point(x,y) to return the colour of the pixel at the given coordinates. If the returned value is the same as the PictureBox Backcolor then there's no pixel:

Eg:

If (Picture1.Point(X, Y) = Picture1.Backcolor) Then
' Nothing here
Else
' Ooh, a bit of drawing here
End If

- Andy.
 
AndyGroom,

Yes, they clicked on a point that's not the same as the picbox background color, but *which* line did they click on?

Also, you have to get the mouse pointer directly on the line before the .point will work at all. You know how hard that is with a line that's set to border = 1?

My method will return the specific line that was clicked on. In the case of overlapping lines, and the click point is near the overlap, the the line returned would be the first one in the loop. ( Although the code could be modified to return all lines that match )

Robert
 
Hi Vampire,

You're right, however the original question was ambiguous as to whether Hujur was drawing directly onto the picture box (how I interpreted it) or whether he was choosing two points and positioning a VB line between the two points (how you interpreted it).

I simply assumed that it was more likely that he was drawing on a picture box and probably didn't know how to detect whether the mouse was over a line or not; since there are no replies from Hujur either way I guess we'll never know!

Surely your remark about how hard it is to click on a line applies equally to lines drawn at 1 pixel in thickness and VB lines that are 1 pixel in thickness...

- Andy.
 
Andy,

Yes, it's the same on both lines.

It would have been a lot simplier if MS had given us a built-in click event for the line control, but that's life...

Robert
 
I've put a fairly nice solution together using Regions...
 
...which I should have posted. For the purposes of the example you will need a form with an array of Line controls on it (the routine for checking if we're clicking on a line actually takes a RECT structure defining the start and end points of the line, but for the example it was just easier to plonk the line controls down). The example also assumes that you are drawing on the form, but it should be fairly easy to see how to extend the core function to deal with any object that has an hDC. And that's it. Just paste the following code into the form:
[tt]
Option Explicit

' Essential declares to do the trick...
Private Declare Function BeginPath Lib &quot;gdi32&quot; (ByVal hdc As Long) As Long
Private Declare Function EndPath Lib &quot;gdi32&quot; (ByVal hdc As Long) As Long
Private Declare Function MoveToEx Lib &quot;gdi32&quot; (ByVal hdc As Long, ByVal x As Long, ByVal y As Long, lpPoint As POINTAPI) As Long
Private Declare Function LineTo Lib &quot;gdi32&quot; (ByVal hdc As Long, ByVal x As Long, ByVal y As Long) As Long
Private Declare Function GetPath Lib &quot;gdi32&quot; (ByVal hdc As Long, lpPoint As POINTAPI, lpTypes As Byte, ByVal nSize As Long) As Long
Private Declare Function WidenPath Lib &quot;gdi32&quot; (ByVal hdc As Long) As Long
Private Declare Function PathToRegion Lib &quot;gdi32&quot; (ByVal hdc As Long) As Long
Private Declare Function DeleteObject Lib &quot;gdi32&quot; (ByVal hObject As Long) As Long
Private Declare Function PtInRegion Lib &quot;gdi32&quot; (ByVal hRgn As Long, ByVal x As Long, ByVal y As Long) As Long

Private Type POINTAPI
x As Long
y As Long
End Type


Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function SetRect Lib &quot;user32&quot; (lpRect As RECT, ByVal X1 As Long, ByVal Y1 As Long, ByVal X2 As Long, ByVal Y2 As Long) As Long


Private Sub Form_MouseDown(Button As Integer, Shift As Integer, x As Single, y As Single)
Dim lp As Long
Dim LineRect As RECT

' Check out each line in the relevant control array
For lp = Line1.lbound To Line1.ubound
' We're moving to a RECT because the linecheck routine is written to b more generic
' than just handling VB's line controls
SetRect LineRect, Line1(lp).X1, Line1(lp).Y1, Line1(lp).X2, Line1(lp).Y2
If IsLine(LineRect, x, y, 5) Then
Line1(lp).Visible = False
End If
Next
End Sub

' This is the essential function
Private Function IsLine(myLine As RECT, x As Single, y As Single, Optional PixelFuzz As Long = 5) As Boolean
Dim myPoint As POINTAPI
Dim OldWidth As Long
Dim OldScale As Long
Dim hRgn As Long

OldWidth = Form1.DrawWidth
OldScale = Form1.ScaleMode

Form1.DrawWidth = Form1.DrawWidth + PixelFuzz * 2 ' Allow width on either side
Form1.ScaleMode = vbPixels

' Ok create a path that matches the line
BeginPath Form1.hdc
MoveToEx Form1.hdc, myLine.Left, myLine.Top, myPoint
LineTo Form1.hdc, myLine.Right, myLine.Bottom
EndPath Form1.hdc

' Do the magic...
WidenPath Form1.hdc ' Add the Pixel Fuzz Factor, so we can click 'near' the line
hRgn = PathToRegion(Form1.hdc) ' Turn our widened path into a Windows region
IsLine = PtInRegion(hRgn, x, y) ' Are we clicking in the region that we have created?

'Clean up our GDI object and the form settings
DeleteObject hRgn
Form1.DrawWidth = OldWidth
Form1.ScaleMode = OldScale
End Function
 
strongm,

That works pretty good. I'd thought about regions before I started writing my code, but I didn't know that such a thing as &quot;WidenPath&quot; existed.

It's still a little sensitive as to where you click on it. When I tried it I still had to get the mouse pointer pratically right on the line before it would respond. Any way to get it to make the fuzz a little fuzzier, so to speak? [smile]

Robert
 
Oops - mea culpa. In this specific example you need to make sure the Form's scale mode is in pixels before calling the IsLine function for the PixelFuzz factor to work correctly. If you don't, the fuzz factor works in the units of the Form (which, as you know, is likely to be twips by default). On the other hand this means you can remoe the code to capture and restore the current scale mode from the function...(I guess this counts as a silver lining)
 
I should also say here that, whilst potentially alien to many VB programmers, paths and regions are your friends. There are a number of useful tricks you can pull with them that are otherwise quite hard to achieve.
 
Mind you, we still don't know if any of this was helpful to hujur...
 
Nice piece of work from johnwm*. Specially the trick done by WidenPath to incorporate the Fuzz Factor.
 
Oh --- I am extremely sorry about the mixup.
I should have written strongm instead of johnwm.
 
Sorry guys for the late reply.

In my program In the mouseDowna event am taking the (X,Y) co-ordinates and adding tO the collection and reading back from collections and by using vb Line command drawing the lines on picture box. In this program user is drawing the house outlines, within that house outline he can also draw some other lines too or may be set of some area again for that am taking (x,y) co-or and drawing lines. I have given a menu option to user to delete line but I wasn't sure How To determine that user has clicked on what line.

I have to try the above code and I will let you all know soon.

Thanks Much much much much much to all of you for all your help.


Best Regards!






 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top