Page 1 of 1

CrossHair - Electronics Schematic - Custom Mice Rate Topic: -----

#1 ricardosms  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 73
  • View blog
  • Posts: 301
  • Joined: 02-April 10

Posted 04 April 2012 - 06:11 AM

CrossHair - Electronics Schematic - Custom Mice

Hello!

A cross-hair is a set two lines crossing(cross) each other on a perpendicular manner, usually very fine(hair) and it is an useful help for locating or aligning objects. It could be used on submarine or shooting games or mapping applications. The problem is that you don't want it to be part of the image, you want it to be floating on top it, but without leaving any permanent mark.

You want the crosshair to move, usually with the cursor, but it is a good feature to hide the cursor(mouse), so it is not in the way. And, how do you do that? Well, more complicated that it sounds. The net framework has these two methods: "cursor.hide" and "cursor.show".

They allow you to hide and to show a mouse pointer when it is on the form or on a control, but they are not too reliable, at least not on windows 7, you hide the mouse and it works ok, and suddenly when you go do something else and come back, the mouse pointer is there and refuses to go away. I tried several ways and couldn't trust that it wouldn't come back. The events "Hover", "Enter", "MouseMove", "MouseLeave" didn't work as I wanted. Some times was invisible in the wrong place and I couldn't click on the controls, because I didn't see it.

One note here. You need the default cursor showing on other parts of the program, so you need to go back and forth and something goes screwy there. Maybe you could experiment a little bit and find a solution that I didn't. My solution was to load a blank cursor from the program resources. A blank cursor is a cursor that has no image. I found one in the NET and I am including it with this project, in case
that you find another use for it. I loaded it from resources instead of loading it from a file because it gives less flickering and it goes with the program if you move it to another place.

Other problem is that you want the cross-hair or whatever image, to show on top of your image, not under it, but it can't become part of the image. If you are using a drawing program, you don't want a few lines polluting your picture, but at the same time you want other changes made to your image to stay.

Still more, you want the crosshair to move without leaving a mark. So you need to delete the old one when creating a new one on another location. How do you delete it? Depends. If you made it on a bitmap you have to replace the bitmap. If you made it on a graphics object you can clear or refresh your form. But, because it goes from pixel to pixel, you get a lot of flickering. What will happen with your other drawings? Will go to the drain too. So you can't have them on the same graphics. Besides, to clear a graphics object, you need a color, maybe the backcolor or white, but even a transparent color is not transparent; you won't see what is behind. So what to do? I decided to use two graphics objects for the same image object (bitmap,picturebox)

I was working on this electronics schematics drawing program and needed to align the components when stamping them there so, instead of a small croshair I needed a couple of full lines going across the image and from top to bottom and with a hole where they crossed each other. That way I could visually know where my new image should be located. Also the connecting wires needed to be aligned. It looks neater if the lines are parallel, horizontal and vertical with square corners instead of on an angle.

But from the thinking to the fact there is a long road. I found several problems and side effects. What you want to do is one thing, but it is a completely different thing how you get there. Having the same graphics object handing the bitmap and the crosshair didn't work. Two graphics on the same bitmap and handler also didn't work because when moving the mouse one graphics would cover the drawings on the other, and without clearing you had too many crosshairs drawn. Making it transparent would show just a black screen, So what I did I created few branches for the same mouse move event and manipulated each graphics object on a different one.

This is what I have now:

2 Mouse cursors: Blank, Eraser and default.
Graphics: Two for the same object, one for drawing and erasing, the other for the crosshair.


Here is my program. It is an electronics Schematics drawing program called "Circuits", but it could be for any other use, like computer programming flow diagram, doors or windows on a house floor plan or "Dress The Dolls" game. Here I load electronic components for disk and display them on a grid where you can select any of them and apply it at different places on the base image. I have 4 lines, two on salmon and two on cyan that extend from side to side of the screen, enclosing a rectangle where the component will be located. There is a small circle on the left-top corner of this rectangle, where you can see through to the image to find a precise point. The distance between the lines change according to the size of the thumbnail we will draw, and will change size with the size of the image selected and with the settings of a track bar that has given the size of the image.


Attached Image


The program has 4 radiobuttons for 4 different operations.:
. "Components" to stamp the image thumbnails on the page.
. "Titles" to write text notes at selected points.
. "Connections" to draw terminals and wires.
. "Erase" to delete areas of the image.

I didn't include an "Undo" or a "Cut-Paste" option, so you have to use the eraser to correct if you made a mistake. This is just a basic version to show you the cross-hair programming and function, and maybe you could add a crop and save routine to make an utility for circuits or other drawings and save a section only, instead of the full image.

I called the 4 radio buttons Rb1, Rb2, Rb3 and Rb4.

The procedure is like this: At form load the name of the files in the folder "Components" are added to the List "ImgArray" and the images are displayed on the datagridview cells. Also a thumbnail is created from the first image. After the program has finished loading you would look at the images and click on the one you want to use. This sets the index(position) on the list that contains the names of the images, depending on the cell clicked. The image is resized according to the trackbar value. and creates a new thumbnail image and stores it on MyThumb using the name stored on the list and reading a file from disk. After selecting the component you are ready to paste it on the diagram (PictureBox).

The First operation is to paste the thumbnail and it is done with the default radiobutton selected(Rb1); when you enter the picturebox area, the mouse disappears and the guide lines (crosshair) appear. You select a position and click. There you get the image pasted. If you click on another point, you will paste a clone of the first image and so on. Let's say, you selected a resistor and pasted it there; now you align the cross hair at the same level and besides it and click again, you have 2 resistors aligned side by side. Now you select another component, lets say a battery, you find a proper spot and click. Now a transistor or a capacitor... and so on.

When you have some components there, you would select Rb3, this sets the option for drawing the connections You align the small circle at the end of a terminal and click. This will draw a small ring there and will set a point on an array of points that will be converted to a graphics path to draw some lines. Now you find another terminal that should be connected to this one and click. You get another ring and another point is added to the array of points. When you have enough points (without going back and forth), you will press the "Draw" button. It will convert the points into a graphics path and will draw the lines from point to point. Next, you will align again with another terminal, and the next and the next and click draw and so on. For all the connections.

If you made a mistake, you click on "Erase" and go to the picturebox. An eraser cursor appears and if you click and drag you will enclose an area surrounded by a magenta trace. When you release the cursor, the enclosed area is cleared. If you type something on the textbox, select "Titles" and click the mouse (normal mouse) on the picturebox, and then press "Write" it will write the title at that point.


Attached Image


I tried making the sample component images with little margins. That will help to align them side by side, close together. They can also be resized with the trackbar, so you will have a new size aligning box on the cross hair.

From the menu, you can save your image, you can flip and rotate the whole image and you can clear it. There are also 4 buttons to rotate and flip the thumbnail, this way you can have less components on disk and still be able to adjust size and orientation. Also there is an image of the component to use.

Let's see the code:
Libraries, Global Variables and Initial Values for the variables and conditions:

#Region "Imports"
Imports System.Drawing.Imaging
Imports System.Drawing.Drawing2D
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Collections
Imports System.Collections.Specialized
Imports System.Windows
#End Region

Public Class Form1
#Region "Global"
    Dim ImgArray As New List(Of String)
    Dim Position As Integer = 0
    Private imgSize As Integer = 25
    Dim Pictures As Integer = 0
    Dim MyPath As New GraphicsPath
    Dim BendPoints As New List(Of Point)

    Dim MyThumb As Image
    Dim Reload As Image
    Dim bmx As Bitmap
    Dim resized As Bitmap
    Dim grCross As Graphics
    'arrays of points
    Private ThePts1(2) As Point
    Private TracedPath As New GraphicsPath(Drawing2D.FillMode.Winding)
    Private ThePoints() As Point
    Private NumPoints As Integer
    Dim Zero As Point
    'create a pen object to draw the lines
    Dim ErasePen As New Pen(Color.Magenta, 2)


#End Region
#Region "Initial Values"

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Reload = Work.Image.Clone


        ToolTip1.SetToolTip(Rb1, "Will Apply Selected Image From Chart" & vbNewLine & "At Last Mouse Up Position")
        ToolTip1.SetToolTip(Rb4, "Will Erase Area Around Mouse Path")
        DataVImages.DefaultCellStyle.BackColor = Color.White 'RosyBrown
        scan()
        If ImgArray.Count > 0 Then
            LoadImages()
        End If

' The default operation is to draw components, so we set the cursor to blank for the picturebox
' And generate an initial thumbnail to avoid error if clicked the picturebox without selecting a component
       
         'Load a mouse from resources

        If Rb1.Checked = True Then
            Dim ms As New System.IO.MemoryStream(My.Resources.Blank)
            Work.Cursor = New Cursor(ms)

        End If

       'Create a thumbnail image from the component

        MyThumb = generateThumbnail(Image.FromFile(Environment.CurrentDirectory & "\Components\" & ImgArray(0)), tbSize.Value)
        pbThumb.Image = MyThumb.Clone


        ' PictureBox1.Cursor = New Cursor(Application.StartupPath & "\Experiment.cur")

    End Sub

#End Region




When we start an operation it is good to clear the arrays, we don't want values there that don't exist or that are duplicated. Here is not necessary but on some instances you would like to reload or rescan a directory. I have set a subdirectory called "Components" under the application directory. This way if we save any file with ".jpg" extension, it won't be loaded as a component. Our components are jpeg files cropped from charts and images. If you need, I can modify an utility I have; that would help you to crop smaller sections of a picture and put it here to use. Let me know, and I will make it and post it here as an attachment.

The following block of code scans the directory, fills the list with component names, creates images from the files, re-sizes them and displays them on the datagridview cells.

Here is a tip that you may find useful. When you don't want the last row of cells on a datagridview with a red "X" on them you could set the datagridview property "AllowUserToAddRows" to false.

#Region "Find and load Components"
    Private Sub scan()
        ImgArray.Clear()
        Try
            'Scan directory for images
            Dim di As New IO.DirectoryInfo(Environment.CurrentDirectory & "\Components")
            Dim aryFi As IO.FileInfo() = di.GetFiles("*.jpg")
            
            ' How Many?
 
            Pictures = aryFi.Count
            Dim fi As IO.FileInfo
            Dim aryFi2 As IO.FileInfo() = di.GetFiles("*.jpg")

            'Add them to the work array
            For Each fi In aryFi2
                ImgArray.Add(fi.Name) 'Original jpg Files 
            Next
            If ImgArray.Count = 0 Then MessageBox.Show("Didn't Find Any Jpg Fles In Directory, Sorry")

        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try

    End Sub




After finding the files and filling the arrays and list we calculate the columns and rows of the datagridview and re-size the display images to fit there. In this program the operation is not repeated, but still is good idea to clear the datagridview of any former data. Erase rows and columns and create them again. The balance between rows x columns minus elements is the number of empty cells at the last row. We "BLANK" them so they don't show an "X".

    Private Sub LoadImages()
        Try
            If ImgArray Is Nothing Then
                Return
            End If

            If Me.WindowState = FormWindowState.Minimized Then
                Return
            End If
            ' If everything is fine then create columns and rows is the datagridview, set the sizes of the images and load them.
            ' In case it takes long we use the wait cursor
           
            Me.Cursor = Cursors.WaitCursor
            DataVImages.Rows.Clear()
            DataVImages.Columns.Clear()
            
            ' And start again

            Dim ColumnsThatFit As Integer = (DataVImages.Width - 5) \ (imgSize + 15)
            Dim numRows As Integer = 0
            '  using the size and margins around picture.

            Dim ImagesToDisplay As Integer = ImgArray.Count
           'Then create enough cells for them

            numRows = CInt(Math.Ceiling(CDbl(ImgArray.Count) / CDbl(ColumnsThatFit)))

            Dim CellsForPictureNumber As Integer = numRows * ColumnsThatFit

            ' Dynamically create the columns
            For index As Integer = 0 To ColumnsThatFit - 1
                Dim dataGridViewColumn As New DataGridViewImageColumn()

                DataVImages.Columns.Add(dataGridViewColumn)
                DataVImages.Columns(index).Width = imgSize + 15
            Next

            ' Create the rows

            For index As Integer = 0 To numRows - 1
                DataVImages.Rows.Add()
                DataVImages.Rows(index).Height = imgSize + 8
            Next

            ' Create the indexes for the rows and columns, so we can locate our images
            Dim columnIndex As Integer = 0
            Dim rowIndex As Integer = 0

            For index As Integer = 0 To (ImagesToDisplay - 1)
                ' Load the image from the file and add to the DataGridView
                Dim img As Image = ResizeTheImage(Image.FromFile(Environment.CurrentDirectory & "\Components\" & ImgArray(index)), 25, 25, 

False)
               
                DataVImages.Rows(rowIndex).Cells(columnIndex).Value = img

                ' Have we reached the end column? if so then start on the next row
                If columnIndex = ColumnsThatFit - 1 Then
                    rowIndex += 1
                    columnIndex = 0
                Else
                    columnIndex += 1
                End If
            Next

            ' Blank the unused cells
            If CellsForPictureNumber > ImagesToDisplay Then
                For index As Integer = 0 To CellsForPictureNumber - ImagesToDisplay - 1
                    Dim dataGridViewCellStyle As New DataGridViewCellStyle()
                    dataGridViewCellStyle.NullValue = Nothing
                    dataGridViewCellStyle.Tag = "BLANK"
                    DataVImages.Rows(rowIndex).Cells(columnIndex + index).Style = dataGridViewCellStyle
                Next
            End If
        Catch ex As Exception
            MessageBox.Show(ex.ToString)
            Me.Cursor = Cursors.Default
        End Try
        Me.Cursor = Cursors.Default
    End Sub



Create the images for display. The images that we will display are generated from the images on disk. They are thumbnails of the other

images. And we re-size them using the width and height to decide which one to use.

    Private Function ResizeTheImage(ByVal Imgx As Image, ByVal width As Integer, ByVal height As Integer, ByVal onlyResizeIfWider As 

Boolean) As Image
        Using image1 As Image = Imgx
            ' Prevent using images internal thumbnail
            image1.RotateFlip(RotateFlipType.Rotate180FlipNone)
            image1.RotateFlip(RotateFlipType.Rotate180FlipNone)
            Dim W1 As Integer = Imgx.Width
            Dim H1 As Integer = Imgx.Height

            If onlyResizeIfWider = True Then
                If image1.Width <= width Then
                    width = image1.Width
                End If
            End If

            Dim newHeight As Integer = image1.Height * width \ image1.Width
            If newHeight > height Then
                ' Resize with height instead
                width = image1.Width * height \ image1.Height
                newHeight = height
            End If

            Dim NewImage As Image = image1.GetThumbnailImage(width, newHeight, Nothing, IntPtr.Zero)
            Return NewImage
        End Using
    End Function



When we finish loading, then we are ready to start drawing. We can select a component image to stamp on the large image. When we click on

it we know which image to use, because we have the column and row indexes.

    Private Sub dataVImages_CellClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles 

DataVImages.CellClick


        Dim i, j As Integer
        i = e.RowIndex
        j = e.ColumnIndex
        Position = i * DataVImages.Columns.Count + j ' + 1  'index of imagearray
        MyThumb = generateThumbnail(Image.FromFile(Environment.CurrentDirectory & "\Components\" & ImgArray(Position)), tbSize.Value)
        pbThumb.Image = MyThumb.Clone
       'We work on a copy not the original
    End Sub
#End Region



When we press the mouse button on the drawing area we save a point to an structure. This value is to keep the position where the mouse was clicked and is the point where we will apply our thumbnails. This point is also used for drawing the text and to erase a section. To erase we enclose an area with the mouse and flood it with white. This point is updated as we move (On mousemove) and a new point created, so we draw anohter point from here to the next and so on. When we have all the points accounted for, we convert them on a graphics path and clear the area inside it.


    Private Sub Work_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Work.MouseUp
        If Rb4.Checked = False Then Exit Sub
        Dim MyGraphics As Graphics
       
        'Create a white brush to paint our area on white
         Dim TransBrush As New SolidBrush(Color.White)
        ' Exit if we're not selecting an Area.
        If ThePoints Is Nothing Then Exit Sub

        ' Close the region.
        If (ThePoints(0).X <> ThePoints(NumPoints).X) Or _
           (ThePoints(0).Y <> ThePoints(NumPoints).Y) _
        Then
            ' Save next point to the array that defines our area.
            NumPoints += 1
            ReDim Preserve ThePoints(NumPoints)  'And change the dimension of the array as we add new points
            ThePoints(NumPoints).X = ThePoints(0).X
            ThePoints(NumPoints).Y = ThePoints(0).Y
        End If

        ' Set points into a Path.
        TracedPath.AddLines(ThePoints)

        Try
            Dim bm As New Bitmap(Work.Image)
            MyGraphics = Graphics.FromImage(bm)
            MyGraphics.FillPolygon(TransBrush, ThePoints) ' We flood the enclosed region on white
            Work.Image = bm
            ThePoints = Nothing
            GC.Collect()
        Catch
            MessageBox.Show("Error")
            ThePoints = Nothing

            Exit Sub

        End Try

    End Sub



Basically our program has 4 operations. We select them by checking 4 radiobuttons. We already saw the erase function that clears an area when the mouse button is released. The other operations are: Draw (stamp) "components", Trace the wiring and connection terminals ("Connections") and print notes or legends "Titles". On the next block of code we print our components on the page. The mouseDown event handler has few if conditions to decide what operation to perform and what mouse cursor to show.

When we press the left button inside the image area we record the position where it happened by setting the values for x and y for a variable called zero. This point will be a future reference point and is where our operation takes place. If we have the radiobutton one checked, then we stamp an image thumbnail on the larger image. If we had the connections checkbox checked (Rb3) we draw an small circle where the mouse when down, and add a point to an array to later create a path for tracing lines, these are connection terminals or on due case the last point tell us where to print a string.

#Region "Draw"
    Private Sub Work_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Work.MouseDown


        'Our erase (rb4 checked)routine draws a magenta line to enclose an area, so we erase it before continuing

        If Rb4.Checked = True Then Refresh()

        ' And register our points

        ThePts1(0).X = e.X
        ThePts1(0).Y = e.Y
        Zero.X = e.X
        Zero.Y = e.Y

        Try

            bmx = New Bitmap(Work.Image)

            ' If we selected components
            If Rb1.Checked = True Then
                ' Call the function DrawWatermark withwhith the parameters that tell it what to print and where
                DrawWatermark(MyThumb, Work.Image, ThePts1(0).X, ThePts1(0).Y)
                Refresh()
            End If

            'If we selected connections, we draw a small circle where we clicked

            If Rb3.Checked = True Then
                Dim gf As Graphics = Graphics.FromImage(Work.Image)
                gf.DrawArc(Pens.Black, e.X - 2, e.Y - 2, 4, 4, 0, 360)
                BendPoints.Add(New Point(e.X, e.Y))
            End If

             ' If we selected erase, set the path for erasing
            If Rb4.Checked = True Then
                TracedPath.Reset()
                ' Erase any previous drawing and Save the starting point.
                NumPoints = 0
                ReDim ThePoints(NumPoints)
                ThePoints(NumPoints).X = e.X
                ThePoints(NumPoints).Y = e.Y

            End If

        Catch ex As Exception
            MessageBox.Show(ex.ToString)
        End Try
    End Sub



The datagridview shows all the available icons to stamp. I have here electronic symbols, but I could have windows, doors, toilets, sofas, or I could have hasts, scarves, glasses, wigs, shirts, or I could have shapes like rectangles, ellipses, rhombus, arrows for programming flux sketches. I can select from the available ones by clicking on it. If I press ok besides the component radiobutton, the program uses the last mouseup position to print the thumbnail. If instead I press on the large image, then the new point will be used when I press OK.


Attached Image


#Region "Stamp WaterMark"
    Function generateThumbnail(ByVal bmp As Bitmap, ByVal newWidth As Integer) As Image
        Dim newHeight As Integer
        Dim W1 As Integer = bmp.Width
        Dim H1 As Integer = bmp.Height
        If W1 >= H1 Then
            newHeight = (bmp.Height * newWidth) / bmp.Width
            resized = New Bitmap(newWidth, newHeight)
        Else
            newHeight = newWidth
            newWidth = (bmp.Width * newHeight) / bmp.Height
        End If
        resized = New Bitmap(newWidth, newHeight)
        Dim gx As Graphics = Graphics.FromImage(resized)
        gx.SmoothingMode = SmoothingMode.HighQuality
        gx.CompositingQuality = CompositingQuality.HighQuality
        gx.InterpolationMode = InterpolationMode.High
        gx.DrawImage(bmp, New Rectangle(0, 0, resized.Width, resized.Height), 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel)
        gx.Dispose()
        Return resized
        '        bmp.Dispose()
    End Function

    ' Copy the watermark image over the result image.
    Private Sub DrawWatermark(ByVal watermark_bm As Bitmap, _
        ByVal result_bm As Bitmap, ByVal x As Integer, ByVal y As Integer)

        Dim ALPHA As Byte = 255
        ' Set the watermark's pixels' Alpha components.
        Dim clr As Color
        For py As Integer = 0 To watermark_bm.Height - 1
            For px As Integer = 0 To watermark_bm.Width - 1
                clr = watermark_bm.GetPixel(px, py)
                watermark_bm.SetPixel(px, py, Color.FromArgb(ALPHA, clr.R, clr.G, clr.B)/>)
            Next px
        Next py
        ' watermark_bm.MakeTransparent(watermark_bm.GetPixel(2, 2))

        ' Copy onto the result image.
        Dim gr As Graphics = Graphics.FromImage(result_bm)
        gr.DrawImage(watermark_bm, x, y)
    End Sub

#End Region



I have supplied few functions to modify our thumbnail, this way we don't need to have extra images for horizontal and vertical or for mirror image, or upside down. Also we have a resize function for the thumbnail. We can also modify the whole page:

#Region "Rotate"
    'Rotate or Flip the Whole page
    Private Sub FlipRotate(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles FlipHorizontal.Click, FlipVertical.Click, 

Rotate90R.Click, Rotate90L.Click

        ' resulting bitmap.
        Dim bmrotate As New Bitmap(Work.Image)

        If sender Is FlipHorizontal Then bmrotate.RotateFlip(RotateFlipType.RotateNoneFlipX)
        If sender Is FlipVertical Then bmrotate.RotateFlip(RotateFlipType.RotateNoneFlipY)
        If sender Is Rotate90R Then bmrotate.RotateFlip(RotateFlipType.Rotate90FlipNone)
        If sender Is Rotate90L Then bmrotate.RotateFlip(RotateFlipType.Rotate270FlipNone)
        Work.Image = bmrotate

    End Sub

    'Rotate Component
    Private Sub btnL_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnL.Click, btnR.Click, btnV.Click, 

btnH.Click
        Dim bmrotate As New Bitmap(MyThumb)

        If sender Is btnH Then bmrotate.RotateFlip(RotateFlipType.RotateNoneFlipX)
        If sender Is btnV Then bmrotate.RotateFlip(RotateFlipType.RotateNoneFlipY)
        If sender Is btnR Then bmrotate.RotateFlip(RotateFlipType.Rotate90FlipNone)
        If sender Is btnL Then bmrotate.RotateFlip(RotateFlipType.Rotate270FlipNone)
        pbThumb.Image = bmrotate
        MyThumb = bmrotate
    End Sub

#End Region





    Private Sub ThumbSize(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles tbSize.Scroll
        Label11.Text = "Size:" & tbSize.Value.ToString & " PX"
    End Sub

    Private Sub tbSize_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles tbSize.MouseUp
        MyThumb = generateThumbnail(Image.FromFile(Environment.CurrentDirectory & "\Components\" & ImgArray(Position)), tbSize.Value)
        pbThumb.Image = MyThumb.Clone
    End Sub





OK, now lets say that we already have some resistors, coils, batteries and transistors stamped at several places. We need now to connect them to complete the circuit. So we will do this by sections, wherever we can without branching. WE select the radiobutton "Connections" and we click on the free end of the terminal of the elements. After clicking on some places and creating an array of points we can press the button OK besides "Connections". The program will draw lines from point to point. You should be careful here because if you click on a point across and on top of a former component drawn, you will have an undesired line and you will have to erase it and the component underneath in order to fix it. The parallel lines look nicer and in good order. So do it properly. The "Cross hair" is there for this purpose, to align and locate the components and the wiring. You have have two of them, so, NO EXCUSE!

    Private Sub btnConnections_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnections.Click

        If Rb3.Checked = False Then Exit Sub

        If BendPoints.Count = 0 Then 'If we cleared or forgot to click on the terminals, we get a message
            MessageBox.Show("Please Select Some Points")
            Exit Sub
        End If
        Try
            ' Otherwise we draw the lines
            Dim gfr As Graphics = Graphics.FromImage(Work.Image)
            For m As Integer = 0 To BendPoints.Count - 2
                gfr.DrawLine(Pens.Black, BendPoints(m), BendPoints(m + 1))
                Refresh()
            Next
        Catch ex As Exception

        End Try
        BendPoints.Clear()
    End Sub




Now we click on some more points and draw new lines, and repeat until done. We can also add more components or add some titles, names or legends for the circuit: There is a multiline textbox that accept our notes and we can print them on the image. We do this by clicking before or after typing, but before clicking OK. The text will come on blue letters wherever was our last mouseup point.

For draw the titles we use the function DrawString. We have the point already saved and if we have typed something on the textbox, that string will appear on blue at the selected point.

   Private Sub btnText_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnText.Click

        If Rb2.Checked = False Then Exit Sub

        Dim Afont As New Font("Comic Sans MS", 16, FontStyle.Bold, GraphicsUnit.Pixel)
        Dim gfr As Graphics = Graphics.FromImage(Work.Image)
        Dim pt As New PointF(ThePts1(0).X - 3, ThePts1(0).Y - 6)
        gfr.DrawString(TextBox1.Text, Afont, Brushes.MediumBlue, pt)
        Refresh()
    End Sub




When we switch from one operation we need to set some conditions, those are mostly on the checked changed events. I also included a couple of "bells and whistles". I have two extra cursors on my resources. One of them is a invisible cursor, it is set when we have the crosshairs moving, so it doesn't interfere. The other is an eraser, to use when clearing an area:

#Region "Operation"


    'To start fresh.
    Private Sub ClearToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Clear.Click
        Work.Image = Reload.Clone
    End Sub


    'Set invisible mouse from resources
    Private Sub Rb1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rb1.CheckedChanged
        If Rb1.Checked = True Then
            Dim ms As New System.IO.MemoryStream(My.Resources.Blank)
            Work.Cursor = New Cursor(ms)

        End If
    End Sub

    ' Return to normal cursor.
    Private Sub Rb2_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rb2.CheckedChanged
        If Rb2.Checked = True Then Work.Cursor = Cursors.Default
    End Sub

    Private Sub Rb3_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rb3.CheckedChanged
        If Rb3.Checked = True Then
            Dim ms As New System.IO.MemoryStream(My.Resources.Blank)
            Work.Cursor = New Cursor(ms)
            BendPoints.Clear()
        End If
    End Sub

    ' Or the eraser cursor.
    Private Sub Rb4_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rb4.CheckedChanged
        If Rb4.Checked = True Then ' Work.Cursor = Cursors.Default
            Dim ms As New System.IO.MemoryStream(My.Resources.EraserLite)
            Work.Cursor = New Cursor(ms)
        End If
    End Sub

#End Region





Let's go by sections:
First we set the mouse pointers depending on the operation. The one from resources is the blank one that we use for drawing. The other is the eraser that we use when erasing and the default. Then we set the crosshair lines.

#Region "While Moving"
#Region "Operation"
' To clear image and set appropriate cursors. The default and the Blank one.

    Private Sub ClearToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Clear.Click
        Work.Image = Reload.Clone
    End Sub

    Private Sub Rb2_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rb2.CheckedChanged
        If Rb2.Checked = True Then Work.Cursor = Cursors.Default
    End Sub

    Private Sub Rb1_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rb1.CheckedChanged
        If Rb1.Checked = True Then
            Dim ms As New System.IO.MemoryStream(My.Resources.Blank)
            Work.Cursor = New Cursor(ms)

        End If
    End Sub

    Private Sub Rb3_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rb3.CheckedChanged
        If Rb3.Checked = True Then
            Dim ms As New System.IO.MemoryStream(My.Resources.Blank)
            Work.Cursor = New Cursor(ms)
            BendPoints.Clear()
        End If
    End Sub

#End Region




The two croshairs are produced on the mousemove handler. But the mousemove subroutine has several functions assigned. The mouse could just be hovering or could be dragging. Besides, we have different cursors for different operations set. So, what we do is to branch out for each operation. A group of "If" according to the radiobuttons settings. most of them are empty, but we need them to weed out situations. The changes of cursors are already taken care of on the checked changed events.

    Private Sub Work_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Work.MouseMove

        If (e.Button = MouseButtons.Left) Then

            'Set our arrays of points
            ThePts1(1).X = e.X
            ThePts1(1).Y = e.Y
            NumPoints += 1
            ReDim Preserve ThePoints(NumPoints)
            ThePoints(NumPoints).X = e.X
            ThePoints(NumPoints).Y = e.Y

            If Rb1.Checked = True Then
            ' Empty
            ElseIf Rb2.Checked = True Then
            ' Empty
           ElseIf Rb3.Checked = True Then
            ' Empty
           ElseIf Rb4.Checked = True Then

                ' Create graphics for displaying eraser enclosed area
                grCross = Work.CreateGraphics
                grCross.DrawLine(ErasePen, ThePts1(0).X, ThePts1(0).Y, ThePts1(1).X, ThePts1(1).Y)

            End If
            'Make 2nd points first points

            ThePts1(0) = ThePts1(1)

        Else
            Work.Refresh()

            ' And show our two cursors at a distance from each other according to the size of our thumbnail
            If Rb2.Checked = False AndAlso Rb4.Checked = False Then
                Try
                    grCross = Work.CreateGraphics
                    grCross.DrawLine(Pens.Salmon, e.X, 5, e.X, e.Y - 5)
                    grCross.DrawLine(Pens.Salmon, e.X, e.Y + 5, e.X, Work.Height - 5)
                    grCross.DrawLine(Pens.Cyan, 5, e.Y + MyThumb.Height, Work.Width - 5, e.Y + MyThumb.Height)
                    grCross.DrawLine(Pens.Cyan, e.X + MyThumb.Width, 5, e.X + MyThumb.Width, Work.Height - 5)
                    grCross.DrawLine(Pens.Salmon, e.X + 5, e.Y, Work.Width - 5, e.Y)
                    grCross.DrawLine(Pens.Salmon, 5, e.Y, e.X - 5, e.Y)
                    grCross.DrawArc(Pens.Salmon, e.X - 4, e.Y - 4, 8, 8, 0, 360)
                    grCross.FillEllipse(Brushes.Transparent, e.X - 3, e.Y - 3, 6, 6)
                    ' grCross.DrawImage(MyThumb, e.X, e.Y)
                Catch
                End Try
            End If
        End If

    End Sub





I cropped my images from other images. At the beginning I made them square because it was easier to re-size to a proper dimension, but depending on the shape of the element, some of them had too much white area around, so you couldn't set images too close because they would cover the former ones. Then I cut them closer and resized them differently using the longest side and comparing it with the trackbar value. Here we also generate a watermark but we don't want it transparent, so we use the alpha value set to 255 and them apply it.

I have included some accessory routines and functions:

#Region "Save And Exit"

    ' To save the schematics
    Private Sub ToolStripMenuItem1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripMenuItem1.Click
        Dim i As Integer
        Dim str As String = "Dummy"
        For i = 1 To 1000
            If i < 10 Then str = "Schem_00" & i.ToString & ".jpg"
            If i > 9 And i < 100 Then str = "Schem_0" & i.ToString & ".jpg"
            If i > 99 Then str = "Schem_" & i.ToString & ".jpg"
            If i > 900 Then MsgBox("You Have Over 900 Pictures, Please Check And Delete Unnecessary Ones")
            If Not System.IO.File.Exists(str) Then
                Try
                    Work.Image.Save(str, System.Drawing.Imaging.ImageFormat.Jpeg) 'Whole
                Catch Ex As Exception
                    MsgBox("Could Not Write To Location")
                End Try
                Exit For
            End If
        Next

    End Sub


    ' Exit Program
    Private Sub ExitToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click
        Application.Exit()
    End Sub


    'View output folder
    Private Sub ToolStripMenuItem3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripMenuItem3.Click
        Shell("Explorer " & AppDomain.CurrentDomain.BaseDirectory, AppWinStyle.NormalFocus)
    End Sub

#End Region




Also, in order to allow for other uses, I have set 3 routines. One for drawing a set of Cartesian axis on the center of the panel. One for drawing rulers on inches at top and left of image, and one for drawing millimeter rulers on top and left. We are assuming 96 dpi for the ruler on inches, and 50 millimeters every two inches. The millimeters rulers are a little bit inaccurate, but it avoids using floats for calculating positions. It will miss 16 mm on a meter. The inches is more accurate, but assumes printer resolution. Nevertheless they are useful:


Attached Image


#Region "Axis"

    Private Sub DrawAxisToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 

DrawAxisToolStripMenuItem.Click
        Dim zero As New Point
        zero.X = Panel1.Width / 2
        zero.Y = Panel1.Height / 2

        Dim rx As Integer = Panel1.Width / 2 ' Need Code here for clicking
        Dim ry As Integer = Panel1.Height / 2 ' Need Code here for clicking

        Dim gfr As Graphics = Graphics.FromImage(Work.Image)
        gfr.DrawLine(Pens.Black, 0, ry, Work.Width, ry)
        gfr.DrawLine(Pens.Black, rx, 0, rx, Work.Height)
        '  Refresh()

        For m As Integer = rx Mod 25 To Work.Width Step 25
            If m Mod 50 = 0 Then
                gfr.DrawLine(Pens.Black, m, ry - 4, m, ry + 4)
            Else
                gfr.DrawLine(Pens.Black, m, ry - 2, m, ry + 2)

            End If
        Next
        For m As Integer = ry Mod 25 To Work.Height Step 25
            If m Mod 50 = 0 Then
                gfr.DrawLine(Pens.Black, rx - 4, m, rx + 4, m)

            Else
                gfr.DrawLine(Pens.Black, rx - 2, m, rx + 2, m)

            End If
        Next
        Refresh()
    End Sub


#End Region



#Region "Rulers"

    Private Function Centimetres(ByVal img As Image) As Image
        Dim Afont As New Font("Arial", 8, FontStyle.Regular, GraphicsUnit.Pixel)
        Dim gfr As Graphics = Graphics.FromImage(img)
        ' Dim Cm As Single = 5.08
        For m As Integer = 0 To Work.Image.Height - 1 Step 5
            gfr.DrawLine(Pens.Black, 0, m, 2, m)

            Select Case m Mod 40

                Case 20
                    gfr.DrawLine(Pens.Black, 0, m, 5, m)

                Case 0
                    gfr.DrawLine(Pens.Black, 0, m, 9, m)
                    If m > 6 Then gfr.DrawString(((m) \ 40).ToString, Afont, Brushes.Black, 11, (m) - 6)

            End Select
        Next
        For m As Integer = 0 To Work.Image.Width - 1 Step 5

            gfr.DrawLine(Pens.Black, m, 0, m, 2)

            Select Case m Mod 40

                Case 20
                    gfr.DrawLine(Pens.Black, m, 0, m, 5)

                Case 0
                    gfr.DrawLine(Pens.Black, m, 0, m, 9)
                    If m > 3 Then gfr.DrawString(((m + 1) \ 40).ToString, Afont, Brushes.Black, m - 3, 11)

            End Select
        Next
        gfr.DrawString("0", Afont, Brushes.Black, 2, 1)
        Return img
    End Function
    Private Function Inches(ByVal img As Image) As Image
        Dim Afont As New Font("Arial", 8, FontStyle.Regular, GraphicsUnit.Pixel)
        Dim gfr As Graphics = Graphics.FromImage(img)
        For m As Integer = 0 To Work.Image.Height - 1 Step 6
            Select Case (m \ 6) Mod 16

                Case 1, 3, 5, 7, 9, 11, 13, 15
                    gfr.DrawLine(Pens.Black, 0, m, 2, m)
                Case 0, 2, 6, 10, 14
                    gfr.DrawLine(Pens.Black, 0, m, 4, m)
                Case 4, 12
                    gfr.DrawLine(Pens.Black, 0, m, 6, m)

                Case 8
                    gfr.DrawLine(Pens.Black, 0, m, 8, m)
            End Select
            If m > 0 Then
                If m Mod 96 = 0 Then
                    gfr.DrawString(((m + 1) \ 96 Mod 96).ToString, Afont, Brushes.Black, 11, (m + 1) - 6)
                    gfr.DrawLine(Pens.Black, 0, m, 9, m)

                End If
            End If
        Next
        For m As Integer = 0 To Work.Image.Width - 1 Step 6
            Select Case (m \ 6) Mod 16

                Case 1, 3, 5, 7, 9, 11, 13, 15
                    gfr.DrawLine(Pens.Black, m, 0, m, 2)

                Case 0, 2, 6, 10, 14
                    gfr.DrawLine(Pens.Black, m, 0, m, 4)
                Case 4, 12
                    gfr.DrawLine(Pens.Black, m, 0, m, 6)
                Case 8
                    gfr.DrawLine(Pens.Black, m, 0, m, 8)

            End Select
            If m > 0 Then
                If m Mod 96 = 0 Then
                    gfr.DrawString(((m + 1) \ 96 Mod 96).ToString, Afont, Brushes.Black, m - 3, 11)
                    gfr.DrawLine(Pens.Black, m, 0, m, 9)
                Else

                End If
            End If
        Next
        gfr.DrawString("0", Afont, Brushes.Black, 3, 3)
        Return img
    End Function


    Private Sub DrawRulersToolStripMenuItem_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles 

DrawRulersToolStripMenuItem.Click
        Work.Image = Inches(Work.Image)
    End Sub

    Private Sub ToolStripMenuItem2_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripMenuItem2.Click
        Work.Image = Centimetres(Work.Image)
    End Sub
#End Region



I hope this will be interesting for some of you and maybe useful.

Please see the attachment.

Thank you,

ricardosms.

Attached File(s)



Is This A Good Question/Topic? 2
  • +

Replies To: CrossHair - Electronics Schematic - Custom Mice

#2 ricardosms  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 73
  • View blog
  • Posts: 301
  • Joined: 02-April 10

Posted 11 April 2012 - 05:14 AM

Hello

I just remembered to mention you than on line 45 of the mouse move handler routine above I have commented out
' grCross.DrawImage(MyThumb, e.X, e.Y)  



because it doesn't look smooth at moving, but you can actually see the image before pasting. It may also be useful for some of you.
Was This Post Helpful? 1
  • +
  • -

#3 _HAWK_  Icon User is offline

  • Master(Of Foo)
  • member icon

Reputation: 1053
  • View blog
  • Posts: 4,081
  • Joined: 02-July 08

Posted 11 April 2012 - 07:37 PM

Bad ass! Was thinking of making one.
Was This Post Helpful? 0
  • +
  • -

#4 ricardosms  Icon User is offline

  • D.I.C Regular
  • member icon

Reputation: 73
  • View blog
  • Posts: 301
  • Joined: 02-April 10

Posted 12 April 2012 - 03:01 AM

Go Ahead, I may choose yours.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1