Link to home
Start Free TrialLog in
Avatar of Leo DiNicola
Leo DiNicolaFlag for United States of America

asked on

GDI+ example to drawing, moving and resizing line circles and rectangles.

Greeting experts!  Can someone point to a good resourcefor GDI+ samples.  We need to be able to create a sketch program that will resize itself if the user drwas a line off the frame and things like that.  There don't seem to be any good resources on GDI or even a sketch program by itself.  We need access to the code so I doubt a sketch program will work.

Help,
Jim
Avatar of S-Twilley
S-Twilley

I haven't use alot of the stuff on this site, but it's helped me in a few areas:

http://www.bobpowell.net/faqmain.htm   (it's from one of the other experts :P)
Avatar of Leo DiNicola

ASKER

IT's not that bad.  We saw that on a previously asked question.  It's still a little piecemeal.
Avatar of Mike Tomlinson
Could you gives us a little more detail on what kind of shapes you need the user to be able to create.  Can they be changed by the user after they have been created? etc...

Also please elaborate on this statement:
    "We need to be able to create a sketch program that will resize itself if the user drwas a line off the frame and things like that."

~IM
I think it would be like if you set the start position of a line and dragged off the screen/draw area... they want the line to be drawn all the way to that point, even though it's outside of the draw area... and then maybe be able to scroll to it?

That's my guess
Correct, if the user is drawing a line and moves/drags outside the draw area then the draw area should expand to allow the user to finish the line.  There will be a counter displaying the length of the line as they draw it, I've got that working.  As far as the shapes are concrened.  There will be line, rectangles circles, arcs and probably most often polygons.  The ulitmate goal is to create a sketch program for the user to sketch the layout of a house for assessment purposes.  It won't need windows or door markers just the basic layout of the building.

We've got a good book on the way about GDI+ but I', strugling thru it now.  
Thank You
QualityData,

Take a look at, build, and then play with the projects I posted in these PAQs:
https://www.experts-exchange.com/questions/21221234/Creating-a-seperate-class-for-graphics.html
https://www.experts-exchange.com/questions/21158325/Create-draggable-retangle-shapes-in-a-Picturebox.html

If this is the kind of thing you are looking for then I can post another sample app that shows how to do lines, curves, ellipses, rectangles, polygons, etc...

~IM
Wow Idle_Mind!  Yes those will cretainly help.  I'd appreciate the other project you were talking about.

Thanks a ton, Jim
ASKER CERTIFIED SOLUTION
Avatar of Mike Tomlinson
Mike Tomlinson
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Alrighty...here is a little project I call "Shape Painter".  It is more complex than the previous examples so there will be more code posted.

In it I have created an Interface called Shape that all of the "shapes" must implement.  This allows the code behind the GUI to be smaller and universal since it can use the common exposed elements of the interface to interact with any "shape" that correctly implements the Shape interface.

This is a great example of OOP in action.  There is one class called Line that implements the Shape interface and then all of the other shapes Inherit from Line (In fact, Class Ellipse is based on three levels of Inheritance).  Here is the Interface/Class structure:

    Interface Shape
        Class Line Implements Shape
            Class Polygon Inherits Line
                Class Rectangle Inherits Polygon
                    Class Ellipse Inherits Rectangle
                Class ClosedCurve Inherits Polygon
            Class Curve Inherits Line
            Class Segment Inherits Line
    Class Form1 (the GUI)

In this app, a Line can have two or more endpoints.  A segment only has two.  The box in the bottom left of the app allows you change the number of points, line thickness, border color and fill color of the shape before you create it.  You can move the endpoints of the shapes by clicking and dragging them.  If you hold down the Shift key, then the shape will be moved as you drag.  If you hold down the Alt key then the shape will be rotated as you drag.  Some of the shapes cannot be rotated.

Remember, this isn't the only way to achieve this kind of thing.  Also, this app hasn't been optimized for performance.  I designed it mainly as a demonstration of OOP techniques and how to create "shapes" that can be changed at runtime by the user via keyboard/mouse actions.

Anyhoo, here she is...you need one Interface, seven classes, and a form:

' -------------------------------------------------------------------------------------
' Interface Shape
' -------------------------------------------------------------------------------------
Public Interface Shape
    Enum action
        none = 0
        defineShape = 1
        dragPoint = 2
        dragShape = 3
        rotateShape = 4
    End Enum

    Property penColor() As Color
    Property penWidth() As Byte
    Property brushColor() As Color
    Property isFilled() As Boolean
    Property showHandles() As Boolean
    Property Points(ByVal index As Integer) As Point
    ReadOnly Property numberOfPoints() As Integer
    ReadOnly Property currentAction() As action
    Function isOverHandle(ByVal mousePosition As Point) As Integer

    Sub addPoint(ByVal vertex As Point)
    Sub definePoints(ByVal numberOfPoints As Integer, ByRef p As Panel)
    Sub dragPoint(ByVal pointIndex As Integer, ByRef p As Panel)
    Sub dragShape(ByVal startPoint As Point, ByRef p As Panel)
    Sub rotateShape(ByVal startpoint As Point, ByRef p As Panel)

    Sub Paint(ByRef g As Graphics)

    Function intersects(ByVal A As Point, ByVal B As Point, ByVal C As Point, ByVal D As Point, ByRef E As Point, ByRef F As Point) As Boolean

    Event definitionComplete()
    Event shapeChanged()
End Interface


' -------------------------------------------------------------------------------------
' Class Line
' -------------------------------------------------------------------------------------
Imports System.drawing

Public Class Line
    Implements Shape

    Public Enum hShape
        squareHandles = 0
        circleHandles = 1
    End Enum

    Public Enum action
        none = 0
        defineShape = 1
        dragPoint = 2
        dragShape = 3
        rotateShape = 4
    End Enum

    Protected pts() As Point
    Protected penClr As Color = Color.Black
    Protected penWdth As Byte = 1
    Protected numPoints As Integer = 0
    Protected hRadius As Byte = 3
    Protected sHandles As Boolean = True

    Protected startDragPoint As Point
    Protected shapeAngle As Single = 0
    Protected lastAngle As Single
    Protected mouseOverIndex As Integer
    Protected hndShape As hShape = hShape.squareHandles
    Protected Shared curAction As action = action.none

    Protected numPtsToDefine As Integer = 0
    Protected numClicks As Integer = 0

    Protected Event definitionComplete() Implements Shape.definitionComplete
    Protected Event shapeChanged() Implements Shape.shapeChanged

    Protected WithEvents sourcePanel As Panel = Nothing

    Public Property handleShape() As hShape
        Get
            Return hndShape
        End Get
        Set(ByVal Value As hShape)
            hndShape = Value
        End Set
    End Property

    Public Property Points(ByVal index As Integer) As Point Implements Shape.Points
        Get
            If index >= 0 And index <= pts.GetUpperBound(0) Then
                Return pts(index)
            End If
        End Get
        Set(ByVal Value As Point)
            If index >= 0 And index <= pts.GetUpperBound(0) Then
                pts(index) = Value
            End If
        End Set
    End Property

    Public Overridable Sub removePoint(ByVal index As Integer)
        Dim i As Integer
        If index >= 0 And index <= pts.GetUpperBound(0) Then
            For i = index To pts.GetUpperBound(0) - 1
                pts(i) = pts(i + 1)
            Next i
            numPoints = numPoints - 1
            If numPoints > 0 Then
                ReDim Preserve pts(numPoints - 1)
            Else
                Erase pts
            End If
        End If
    End Sub

    ReadOnly Property numberOfPoints() As Integer Implements Shape.numberOfPoints
        Get
            Return numPoints
        End Get
    End Property

    Public Property penColor() As Color Implements Shape.penColor
        Get
            Return penClr
        End Get
        Set(ByVal Value As Color)
            penClr = Value
        End Set
    End Property

    Public Property penWidth() As Byte Implements Shape.penWidth
        Get
            Return penWdth
        End Get
        Set(ByVal Value As Byte)
            If Value >= 1 Then
                penWdth = Value
            End If
        End Set
    End Property

    Public Overridable Property isFilled() As Boolean Implements Shape.isFilled
        Get

        End Get
        Set(ByVal Value As Boolean)

        End Set
    End Property

    Public Overridable Property brushColor() As Color Implements Shape.brushColor
        Get

        End Get
        Set(ByVal Value As Color)

        End Set
    End Property

    Public ReadOnly Property currentAction() As Shape.action Implements Shape.currentAction
        Get
            Return curAction
        End Get
    End Property

    Public Property handleRadius() As Byte
        Get
            Return hRadius
        End Get
        Set(ByVal Value As Byte)
            If Value >= 1 Then
                hRadius = Value
            End If
        End Set
    End Property

    Public Property showHandles() As Boolean Implements Shape.showHandles
        Get
            Return sHandles
        End Get
        Set(ByVal Value As Boolean)
            sHandles = Value
        End Set
    End Property

    Public Overridable Sub addPoint(ByVal vertex As Point) Implements Shape.addPoint
        ReDim Preserve pts(numPoints)
        pts(numPoints) = vertex
        numPoints = numPoints + 1
    End Sub

    Public Overridable Function isOverHandle(ByVal mousePosition As Point) As Integer Implements Shape.isOverHandle
        Dim h As Integer
        'dim newPts() as Point = transformpoints(
        If numPoints > 0 Then
            For h = 0 To pts.GetUpperBound(0)
                If PointToPointDist(mousePosition.X, mousePosition.Y, pts(h).X, pts(h).Y) <= hRadius Then
                    Return h
                End If
            Next h
        End If
        Return -1
    End Function

    Public Overridable Sub Paint(ByRef g As System.Drawing.Graphics) Implements Shape.Paint
        If numPoints >= 2 Then
            Dim p As New Pen(penClr, penWdth)

            transformWorld(g)
            g.DrawLines(p, pts)
            p.Dispose()
            PaintHandles(g)
        End If
    End Sub

    Protected Overridable Sub transformWorld(ByRef g As System.Drawing.Graphics)
        Dim oldPosition As Point = computeCenter()

        g.TranslateTransform(-oldPosition.X, -oldPosition.Y)
        g.RotateTransform(shapeAngle, Drawing2D.MatrixOrder.Append)
        g.TranslateTransform(oldPosition.X, oldPosition.Y)
    End Sub

    Protected Overridable Function transformPoints(ByRef g As System.Drawing.Graphics) As Point()
        Dim newPts() As Point
        Dim oldPosition As Point = computeCenter()

        ' Keep origianal points intact...
        Array.Copy(pts, newPts, pts.Length)

        g.ResetTransform()

        ' offset shape to origin based on computed center
        ' and rotate by specified number of angles
        g.TranslateTransform(-oldPosition.X, -oldPosition.Y)
        g.RotateTransform(shapeAngle, Drawing2D.MatrixOrder.Append)
        g.TransformPoints(System.Drawing.Drawing2D.CoordinateSpace.Page, _
            System.Drawing.Drawing2D.CoordinateSpace.World, newPts)


        ' offset shape back to its original location
        g.TranslateTransform(oldPosition.X, oldPosition.Y)
        g.TransformPoints(System.Drawing.Drawing2D.CoordinateSpace.Page, _
            System.Drawing.Drawing2D.CoordinateSpace.World, newPts)

        g.ResetTransform()
        Return newPts
    End Function

    Protected Overridable Sub PaintHandles(ByRef g As System.Drawing.Graphics)
        Dim cp As Integer
        If numPoints >= 1 And sHandles Then
            Dim p As New Pen(penClr)
            Dim b As New SolidBrush(penClr)
            Dim r As Rectangle
            r.Width = hRadius * 2
            r.Height = hRadius * 2
            For cp = 0 To pts.GetUpperBound(0)
                r.X = pts(cp).X - hRadius
                r.Y = pts(cp).Y - hRadius
                If hndShape = hShape.circleHandles Then
                    g.FillEllipse(b, r)
                    g.DrawEllipse(p, r)
                ElseIf hndShape = hShape.squareHandles Then
                    g.FillRectangle(b, r)
                    g.DrawRectangle(p, r)
                End If
            Next
            b.Dispose()
            p.Dispose()
        End If
    End Sub

    Public Shared Function PointToPointDist(ByVal Ax As Single, ByVal Ay As Single, _
            ByVal Bx As Single, ByVal By As Single) As Single
        ' PointToPointDist = SquareRoot((Bx - Ax)^2 + (By - Ay)^2)
        Return Math.Sqrt((Bx - Ax) * (Bx - Ax) + (By - Ay) * (By - Ay))
    End Function

    Public Shared Function PointToLineDist(ByVal Px As Single, ByVal Py As Single, _
            ByVal Ax As Single, ByVal Ay As Single, ByVal Bx As Single, ByVal By As Single) As Single
        Dim q As Single

        If (Ax = Bx) And (Ay = By) Then
            ' A and B passed in define a point, not a line.
            ' Point to Point Distance
            Return PointToPointDist(Px, Py, Ax, Ay)
        Else
            ' Distance is the length of the line needed to connect the point to the segment
            ' such that the two lines would be perpendicular.

            ' q is the parameterized value needed to get to this point of intersection
            q = ((Px - Ax) * (Bx - Ax) + (Py - Ay) * (By - Ay)) / ((Bx - Ax) * (Bx - Ax) + (By - Ay) * (By - Ay))

            ' Limit q to 0 <= q <= 1
            ' If q is outside this range then the Point is somewhere past the endpoints
            ' of our segment.  By setting q = 0 or q = 1 we are measuring the actual distacne
            ' from the point to one of the endpoints instead
            If q < 0 Then q = 0
            If q > 1 Then q = 1

            ' Distance
            Return PointToPointDist(Px, Py, (1 - q) * Ax + q * Bx, (1 - q) * Ay + q * By)
        End If
    End Function

    Protected Shared Function computeAngle(ByVal centerX As Single, ByVal centerY As Single, _
            ByVal currentX As Single, ByVal currentY As Single) As Single
        Dim dx As Single, dy As Single
        Dim angle As Single

        dy = currentY - centerY
        dx = currentX - centerX
        If dx = 0 Then
            If dy <= 0 Then
                angle = 90
            Else
                angle = 270
            End If
        Else
            angle = Math.Atan(dy / dx)
            angle = angle * (180 / Math.PI)
            If currentX > centerX And currentY <= centerY Then
                angle = Math.Abs(angle)
            ElseIf currentX < centerX And currentY <= centerY Then
                angle = 180 - angle
            ElseIf currentX < centerX And currentY > centerY Then
                angle = 180 + Math.Abs(angle)
            Else
                angle = 360 - angle
            End If
        End If
        Return angle
    End Function

    Public Overridable Sub definePoints(ByVal numberOfPoints As Integer, ByRef p As Panel) Implements Shape.definePoints
        If curAction = action.none And numberOfPoints >= 2 Then
            Erase pts
            numPoints = 0
            numPtsToDefine = numberOfPoints
            numClicks = 0
            curAction = action.defineShape
            sourcePanel = p
        End If
    End Sub

    Public Overridable Sub dragPoint(ByVal pointIndex As Integer, ByRef p As Panel) Implements Shape.dragPoint
        If curAction = action.none And numPoints > 0 Then
            If pointIndex >= 0 And pointIndex <= pts.GetUpperBound(0) Then
                mouseOverIndex = pointIndex
                curAction = action.dragPoint
                sourcePanel = p
            End If
        End If
    End Sub

    Public Sub dragShape(ByVal startPoint As System.Drawing.Point, ByRef p As System.Windows.Forms.Panel) Implements Shape.dragShape
        If curAction = action.none And numPoints > 0 Then
            startDragPoint = startPoint
            curAction = action.dragShape
            sourcePanel = p
        End If
    End Sub

    Public Overridable Sub rotateShape(ByVal startpoint As System.Drawing.Point, ByRef p As System.Windows.Forms.Panel) Implements Shape.rotateShape
        If curAction = action.none And numPoints >= 2 Then
            Dim center As Point = computeCenter()
            lastAngle = computeAngle(center.X, center.Y, startpoint.X, startpoint.Y)
            curAction = action.rotateShape
            sourcePanel = p
        End If
    End Sub

    Protected Overridable Sub rotateShapeByDegrees(ByVal angleInDegrees As Single, ByRef p As System.Windows.Forms.Panel)
        If curAction = action.rotateShape And numPoints >= 2 Then
            If Not (p Is Nothing) Then
                Dim g As Graphics = p.CreateGraphics
                Dim oldPosition As Point = computeCenter()

                ' offset shape to origin based on computed center
                ' and rotate by specified number of angles
                g.TranslateTransform(-oldPosition.X, -oldPosition.Y)
                g.RotateTransform(angleInDegrees, Drawing2D.MatrixOrder.Append)
                g.TransformPoints(System.Drawing.Drawing2D.CoordinateSpace.Page, _
                    System.Drawing.Drawing2D.CoordinateSpace.World, pts)

                ' reset the transform world
                g.ResetTransform()

                ' offset shape back to its original location
                g.TranslateTransform(oldPosition.X, oldPosition.Y)
                g.TransformPoints(System.Drawing.Drawing2D.CoordinateSpace.Page, _
                    System.Drawing.Drawing2D.CoordinateSpace.World, pts)

                ' paint rotated shape
                p.Refresh()
                g.Dispose()
            End If
        End If
    End Sub

    Protected Function computeCenter() As Point
        Dim p As New Point
        Dim i As Integer
        Dim x As Integer, y As Integer

        If numPoints > 0 Then
            For i = 0 To pts.GetUpperBound(0)
                x = x + pts(i).X
                y = y + pts(i).Y
            Next
            p.X = x / numPoints
            p.Y = y / numPoints
        End If

        Return p
    End Function

    Protected Overridable Sub sourcePanel_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles sourcePanel.MouseDown
        Select Case curAction
            Case action.defineShape
                If numClicks = 0 Then
                    Me.addPoint(New Point(e.X, e.Y))
                    Me.addPoint(New Point(e.X, e.Y))
                Else
                    Me.Points(numClicks) = New Point(e.X, e.Y)
                End If
                numClicks = numClicks + 1
                If numClicks = numPtsToDefine Then
                    sourcePanel = Nothing
                    curAction = action.none
                    RaiseEvent definitionComplete()
                    Exit Sub
                ElseIf numClicks > 1 Then
                    Me.addPoint(New Point(e.X, e.Y))
                End If
                If Not (sourcePanel Is Nothing) Then
                    sourcePanel.Refresh()
                End If
        End Select
    End Sub

    Protected Overridable Sub sourcePanel_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles sourcePanel.MouseMove
        Dim center As Point
        Dim curAngle As Single
        Dim deltaAngle As Single

        Select Case curAction
            Case action.defineShape
                If numClicks >= 1 Then
                    Me.Points(numClicks) = New Point(e.X, e.Y)
                    If Not (sourcePanel Is Nothing) Then
                        sourcePanel.Refresh()
                    End If
                End If

            Case action.dragPoint
                pts(mouseOverIndex).X = e.X
                pts(mouseOverIndex).Y = e.Y
                If Not (sourcePanel Is Nothing) Then
                    sourcePanel.Refresh()
                End If

            Case action.dragShape
                Dim i As Integer, dx As Integer, dy As Integer
                dx = e.X - startDragPoint.X
                dy = e.Y - startDragPoint.Y
                For i = 0 To pts.GetUpperBound(0)
                    pts(i).X = pts(i).X + dx
                    pts(i).Y = pts(i).Y + dy
                Next i
                startDragPoint = New Point(e.X, e.Y)
                If Not (sourcePanel Is Nothing) Then
                    sourcePanel.Refresh()
                End If

            Case action.rotateShape
                center = computeCenter()
                curAngle = computeAngle(center.X, center.Y, e.X, e.Y)
                If Math.Abs(curAngle - lastAngle) >= 1.0 Then
                    If lastAngle >= 270 And curAngle < 90 Then
                        deltaAngle = (360 - lastAngle) + curAngle
                    ElseIf lastAngle <= 90 And curAngle > 270 Then
                        deltaAngle = -(lastAngle + (360 - lastAngle))
                    Else
                        deltaAngle = curAngle - lastAngle
                    End If

                    'shapeAngle = shapeAngle - deltaAngle
                    'While shapeAngle > 360
                    'shapeAngle = shapeAngle - 360
                    'End While
                    'While shapeAngle < -360
                    'shapeAngle = shapeAngle + 360
                    'End While
                    'sourcePanel.Refresh()
                    rotateShapeByDegrees(-deltaAngle, sourcePanel)

                    lastAngle = curAngle
                End If

        End Select
    End Sub

    Protected Overridable Sub sourcePanel_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles sourcePanel.MouseUp
        Dim center As Point
        Dim curAngle As Single
        Dim deltaAngle As Single

        Select Case curAction
            Case action.dragPoint
                curAction = action.none
                pts(mouseOverIndex).X = e.X
                pts(mouseOverIndex).Y = e.Y
                If Not (sourcePanel Is Nothing) Then
                    sourcePanel.Refresh()
                End If
                sourcePanel = Nothing

            Case action.dragShape
                Dim i As Integer, dx As Integer, dy As Integer
                dx = e.X - startDragPoint.X
                dy = e.Y - startDragPoint.Y
                For i = 0 To pts.GetUpperBound(0)
                    pts(i).X = pts(i).X + dx
                    pts(i).Y = pts(i).Y + dy
                Next i
                startDragPoint = New Point(e.X, e.Y)
                curAction = action.none
                If Not (sourcePanel Is Nothing) Then
                    sourcePanel.Refresh()
                End If
                sourcePanel = Nothing

            Case action.rotateShape
                center = computeCenter()
                curAngle = computeAngle(center.X, center.Y, e.X, e.Y)
                If curAngle <> lastAngle Then
                    If lastAngle >= 270 And curAngle < 90 Then
                        deltaAngle = (360 - lastAngle) + curAngle
                    ElseIf lastAngle <= 90 And curAngle > 270 Then
                        deltaAngle = -(lastAngle + (360 - lastAngle))
                    Else
                        deltaAngle = curAngle - lastAngle
                    End If
                    rotateShapeByDegrees(-deltaAngle, sourcePanel)
                End If
                curAction = action.none
                sourcePanel = Nothing

        End Select
    End Sub

    Public Function intersects(ByVal A As Point, ByVal B As Point, ByVal C As Point, ByVal D As Point, ByRef E As Point, ByRef F As Point) As Boolean Implements Shape.intersects
        Dim Det1 As Double = (A.Y - C.Y) * (D.X - C.X) - (A.X - C.X) * (D.Y - C.Y)
        Dim Det2 As Double = (B.X - A.X) * (D.Y - C.Y) - (B.Y - A.Y) * (D.X - C.X)
        If Det2 <> 0 Then ' The segments are not Parallel...
            Dim Det3 As Double = (A.Y - C.Y) * (B.X - A.X) - (A.X - C.X) * (B.Y - A.Y)
            Dim Det4 As Double = (B.X - A.X) * (D.Y - C.Y) - (B.Y - A.Y) * (D.X - C.X)
            Dim r As Double = Det1 / Det2
            Dim s As Double = Det3 / Det4
            If (r >= 0 And r <= 1) And (s >= 0 And s <= 1) Then
                ' ...and they Physically intersect at...
                E.X = A.X + r * (B.X - A.X)
                E.Y = A.Y + r * (B.Y - A.Y)
                F = E
                Return True
            Else
                ' ..and they would intersect if one or both of them were extended
                Return False
            End If
        Else ' The segments are Parallel...
            If Det1 = 0 Then ' ...and Overlappping
                ' Return the Endpoints of the Overlapping segment

                If A.Equals(B) And C.Equals(D) Then
                    ' same point [(AB) and (CD)] was passed in
                    E = A
                    F = A
                    Return True
                ElseIf A.Equals(B) And Not C.Equals(D) Then
                    ' one point (AB) and a line (CD) was passed in
                    E = A
                    F = A
                    Return True
                ElseIf Not A.Equals(B) And C.Equals(D) Then
                    ' one point (CD) and a line (AB) was passed in
                    E = C
                    F = C
                    Return True
                ElseIf (A.Equals(C) And B.Equals(D)) Or (A.Equals(D) And B.Equals(C)) Then
                    ' segments share the same endpoints
                    E = A
                    F = B
                    Return True
                End If

                If segmentContainsPoint(A, B, C) And segmentContainsPoint(A, B, D) Then
                    ' segment CD is contained by segment AB
                    E = C
                    F = D
                    Return True
                ElseIf segmentContainsPoint(C, D, A) And segmentContainsPoint(C, D, B) Then
                    ' segment AB is contained by segement CD
                    E = A
                    F = B
                    Return True
                End If

                ' one segment does not contain the other
                If segmentContainsPoint(A, B, C) Then
                    E = C
                    If segmentContainsPoint(C, D, A) Then
                        F = A
                    ElseIf segmentContainsPoint(C, D, B) Then
                        F = B
                    End If
                    Return True
                ElseIf segmentContainsPoint(A, B, D) Then
                    E = D
                    If segmentContainsPoint(C, D, A) Then
                        F = A
                    ElseIf segmentContainsPoint(C, D, B) Then
                        F = B
                    End If
                    Return True
                End If
            Else ' ...but they do not Overlap
                Return False
            End If
        End If
    End Function

    Private Function segmentContainsPoint(ByVal A As Point, ByVal B As Point, ByVal C As Point) As Boolean
        ' Two Segments AB and CD have already been determined to have the
        ' same slope and that they overlap.
        ' AB is the segment, and C is the point in question.
        ' If AB contains C then return true, otherwise return false
        If C.Equals(A) Or C.Equals(B) Then
            Return True
        ElseIf A.X = B.X Then ' Project to the Y-Axis
            If (A.Y <= C.Y And C.Y <= B.Y) Or (B.Y <= C.Y And C.Y <= A.Y) Then
                Return True
            Else
                Return False
            End If
        Else ' Project to the X-Axis
            If (A.X <= C.X And C.X <= B.X) Or (B.X <= C.X And C.X <= A.X) Then
                Return True
            Else
                Return False
            End If
        End If
    End Function
End Class


' -------------------------------------------------------------------------------------
' Class Polygon
' -------------------------------------------------------------------------------------
Public Class Polygon
    Inherits Line

    Protected brushClr As Color = Color.White
    Protected filled As Boolean = False

    Public Overrides Property brushColor() As Color
        Get
            Return brushClr
        End Get
        Set(ByVal Value As Color)
            brushClr = Value
        End Set
    End Property

    Public Overrides Property isFilled() As Boolean
        Get
            Return filled
        End Get
        Set(ByVal Value As Boolean)
            filled = Value
        End Set
    End Property

    Public Overrides Sub Paint(ByRef g As System.Drawing.Graphics)
        If numPoints >= 2 Then
            Dim p As New Pen(penClr, penwdth)
            transformWorld(g)
            If filled Then
                Dim b As New SolidBrush(brushClr)
                g.FillPolygon(b, Pts)
                b.Dispose()
            End If
            g.DrawPolygon(p, Pts)
            p.Dispose()
            PaintHandles(g)
        End If
    End Sub

End Class


' -------------------------------------------------------------------------------------
' Class Rectangle
' -------------------------------------------------------------------------------------
Public Class myRectangle
    Inherits Polygon

    Public Overrides Sub addPoint(ByVal vertex As System.Drawing.Point)
        If numberOfPoints < 2 Then
            MyBase.addPoint(vertex)
        End If
    End Sub

    Public Overrides Sub rotateShape(ByVal startpoint As System.Drawing.Point, ByRef p As System.Windows.Forms.Panel)

    End Sub

    Public Overrides Sub Paint(ByRef g As System.Drawing.Graphics)
        If numPoints = 2 Then
            transformWorld(g)
            Dim p As New Pen(penClr, penwdth)
            Dim r As Rectangle
            Dim left As Integer, right As Integer
            Dim top As Integer, bottom As Integer

            ' Normalize the Rectange Structure
            If pts(0).X <= Pts(1).X Then
                left = Pts(0).X
                right = Pts(1).X
            Else
                left = Pts(1).X
                right = Pts(0).X
            End If
            If pts(0).Y <= Pts(1).Y Then
                top = Pts(0).Y
                bottom = Pts(1).Y
            Else
                top = pts(1).Y
                bottom = pts(0).Y
            End If
            r.X = left
            r.Y = top
            r.Width = right - left + 1
            r.Height = bottom - top + 1

            If filled Then
                Dim b As New SolidBrush(brushClr)
                g.FillRectangle(b, r)
                b.Dispose()
            End If
            g.DrawRectangle(p, r)
            p.Dispose()
            PaintHandles(g)
        End If
    End Sub

End Class


' -------------------------------------------------------------------------------------
' Class Ellipse
' -------------------------------------------------------------------------------------
Public Class Ellipse
    Inherits myRectangle

    Public Overrides Sub rotateShape(ByVal startpoint As System.Drawing.Point, ByRef p As System.Windows.Forms.Panel)

    End Sub

    Public Overrides Sub Paint(ByRef g As System.Drawing.Graphics)
        If numPoints = 2 Then
            Dim p As New Pen(penClr, penwdth)
            Dim r As Rectangle

            transformWorld(g)
            r.X = Pts(0).X
            r.Y = Pts(0).Y
            r.Width = Pts(1).X - r.X + 1
            r.Height = Pts(1).Y - r.Y + 1
            If filled Then
                Dim b As New SolidBrush(brushClr)
                g.FillEllipse(b, r)
                b.Dispose()
            End If
            g.DrawEllipse(p, r)
            p.Dispose()
            PaintHandles(g)
            g.ResetTransform()
        End If
    End Sub

End Class


' -------------------------------------------------------------------------------------
' Class ClosedCurve
' -------------------------------------------------------------------------------------
Public Class ClosedCurve
    Inherits Polygon

    Public Overrides Sub Paint(ByRef g As System.Drawing.Graphics)
        Dim p As New Pen(penClr, penwdth)
        If numpoints >= 4 Then
            transformWorld(g)
            If filled Then
                Dim b As New SolidBrush(brushClr)
                g.FillClosedCurve(b, Pts)
                b.Dispose()
            End If
            g.DrawClosedCurve(p, Pts)
        ElseIf numpoints >= 2 Then
            g.DrawCurve(p, pts)
        End If
        p.Dispose()
        PaintHandles(g)
    End Sub

End Class


' -------------------------------------------------------------------------------------
' Class Curve
' -------------------------------------------------------------------------------------
Public Class Curve
    Inherits Line

    Public Overrides Sub Paint(ByRef g As System.Drawing.Graphics)
        If numpoints >= 2 Then
            Dim p As New Pen(penClr, penwdth)
            transformWorld(g)
            g.DrawCurve(p, Pts)
            p.Dispose()
        End If
        PaintHandles(g)
    End Sub

End Class


' -------------------------------------------------------------------------------------
' Class Segment
' -------------------------------------------------------------------------------------
Public Class Segment
    Inherits Line

    Public Overrides Sub addPoint(ByVal vertex As System.Drawing.Point)
        If numberOfPoints < 2 Then
            MyBase.addPoint(vertex)
        End If
    End Sub

    Public Overrides Sub Paint(ByRef g As System.Drawing.Graphics)
        If numPoints = 2 Then
            Dim p As New Pen(penClr, penWdth)
            If Not pts(0).Equals(pts(1)) Then
                ' endpoints are different...draw the segment
                transformWorld(g)
                g.DrawLines(p, pts)
                PaintHandles(g)
            Else
                ' endpoints are the same...draw as a point
                Dim b As New SolidBrush(penClr)
                Dim r As Rectangle
                r.Width = hRadius * 2
                r.Height = hRadius * 2
                r.X = pts(0).X - hRadius
                r.Y = pts(0).Y - hRadius
                If hndShape = hShape.circleHandles Then
                    g.FillEllipse(b, r)
                    g.DrawEllipse(p, r)
                ElseIf hndShape = hShape.squareHandles Then
                    g.FillRectangle(b, r)
                    g.DrawRectangle(p, r)
                End If
                b.Dispose()
            End If
            p.Dispose()
        End If
    End Sub

End Class


' -------------------------------------------------------------------------------------
' Class Form1
' -------------------------------------------------------------------------------------
Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call

    End Sub

    'Form overrides dispose to clean up the component list.
    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    'Required by the Windows Form Designer
    Private components As System.ComponentModel.IContainer

    'NOTE: The following procedure is required by the Windows Form Designer
    'It can be modified using the Windows Form Designer.  
    'Do not modify it using the code editor.
    Friend WithEvents Panel1 As System.Windows.Forms.Panel
    Friend WithEvents ColorDialog1 As System.Windows.Forms.ColorDialog
    Friend WithEvents penColor As System.Windows.Forms.Panel
    Friend WithEvents brushColor As System.Windows.Forms.Panel
    Friend WithEvents brushColorButton As System.Windows.Forms.Button
    Friend WithEvents lineButton As System.Windows.Forms.Button
    Friend WithEvents polygonButton As System.Windows.Forms.Button
    Friend WithEvents curveButton As System.Windows.Forms.Button
    Friend WithEvents ellipseButton As System.Windows.Forms.Button
    Friend WithEvents penColorButton As System.Windows.Forms.Button
    Friend WithEvents shapesPanel As System.Windows.Forms.Panel
    Friend WithEvents showHandles As System.Windows.Forms.CheckBox
    Friend WithEvents borderWidth As System.Windows.Forms.NumericUpDown
    Friend WithEvents Label2 As System.Windows.Forms.Label
    Friend WithEvents numberPoints As System.Windows.Forms.NumericUpDown
    Friend WithEvents Label1 As System.Windows.Forms.Label
    Friend WithEvents closedCurveButton As System.Windows.Forms.Button
    Friend WithEvents rectangleButton As System.Windows.Forms.Button
    Friend WithEvents segmentButton As System.Windows.Forms.Button
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.lineButton = New System.Windows.Forms.Button
        Me.polygonButton = New System.Windows.Forms.Button
        Me.curveButton = New System.Windows.Forms.Button
        Me.Panel1 = New System.Windows.Forms.Panel
        Me.numberPoints = New System.Windows.Forms.NumericUpDown
        Me.Label1 = New System.Windows.Forms.Label
        Me.Label2 = New System.Windows.Forms.Label
        Me.borderWidth = New System.Windows.Forms.NumericUpDown
        Me.brushColorButton = New System.Windows.Forms.Button
        Me.penColorButton = New System.Windows.Forms.Button
        Me.brushColor = New System.Windows.Forms.Panel
        Me.penColor = New System.Windows.Forms.Panel
        Me.ColorDialog1 = New System.Windows.Forms.ColorDialog
        Me.shapesPanel = New System.Windows.Forms.Panel
        Me.ellipseButton = New System.Windows.Forms.Button
        Me.showHandles = New System.Windows.Forms.CheckBox
        Me.closedCurveButton = New System.Windows.Forms.Button
        Me.rectangleButton = New System.Windows.Forms.Button
        Me.segmentButton = New System.Windows.Forms.Button
        Me.Panel1.SuspendLayout()
        CType(Me.numberPoints, System.ComponentModel.ISupportInitialize).BeginInit()
        CType(Me.borderWidth, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.SuspendLayout()
        '
        'lineButton
        '
        Me.lineButton.Location = New System.Drawing.Point(8, 40)
        Me.lineButton.Name = "lineButton"
        Me.lineButton.Size = New System.Drawing.Size(96, 24)
        Me.lineButton.TabIndex = 0
        Me.lineButton.Text = "Line"
        '
        'polygonButton
        '
        Me.polygonButton.Location = New System.Drawing.Point(8, 168)
        Me.polygonButton.Name = "polygonButton"
        Me.polygonButton.Size = New System.Drawing.Size(96, 24)
        Me.polygonButton.TabIndex = 1
        Me.polygonButton.Text = "Polygon"
        '
        'curveButton
        '
        Me.curveButton.Location = New System.Drawing.Point(8, 72)
        Me.curveButton.Name = "curveButton"
        Me.curveButton.Size = New System.Drawing.Size(96, 24)
        Me.curveButton.TabIndex = 2
        Me.curveButton.Text = "Curve"
        '
        'Panel1
        '
        Me.Panel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
        Me.Panel1.Controls.Add(Me.numberPoints)
        Me.Panel1.Controls.Add(Me.Label1)
        Me.Panel1.Controls.Add(Me.Label2)
        Me.Panel1.Controls.Add(Me.borderWidth)
        Me.Panel1.Controls.Add(Me.brushColorButton)
        Me.Panel1.Controls.Add(Me.penColorButton)
        Me.Panel1.Controls.Add(Me.brushColor)
        Me.Panel1.Controls.Add(Me.penColor)
        Me.Panel1.Location = New System.Drawing.Point(8, 240)
        Me.Panel1.Name = "Panel1"
        Me.Panel1.Size = New System.Drawing.Size(104, 144)
        Me.Panel1.TabIndex = 3
        '
        'numberPoints
        '
        Me.numberPoints.Location = New System.Drawing.Point(54, 8)
        Me.numberPoints.Minimum = New Decimal(New Integer() {2, 0, 0, 0})
        Me.numberPoints.Name = "numberPoints"
        Me.numberPoints.Size = New System.Drawing.Size(40, 20)
        Me.numberPoints.TabIndex = 11
        Me.numberPoints.Value = New Decimal(New Integer() {2, 0, 0, 0})
        '
        'Label1
        '
        Me.Label1.Location = New System.Drawing.Point(6, 8)
        Me.Label1.Name = "Label1"
        Me.Label1.Size = New System.Drawing.Size(40, 16)
        Me.Label1.TabIndex = 10
        Me.Label1.Text = "Points:"
        Me.Label1.TextAlign = System.Drawing.ContentAlignment.MiddleRight
        '
        'Label2
        '
        Me.Label2.Location = New System.Drawing.Point(8, 72)
        Me.Label2.Name = "Label2"
        Me.Label2.Size = New System.Drawing.Size(40, 24)
        Me.Label2.TabIndex = 5
        Me.Label2.Text = "Border Width"
        Me.Label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter
        '
        'borderWidth
        '
        Me.borderWidth.Location = New System.Drawing.Point(56, 72)
        Me.borderWidth.Minimum = New Decimal(New Integer() {1, 0, 0, 0})
        Me.borderWidth.Name = "borderWidth"
        Me.borderWidth.Size = New System.Drawing.Size(40, 20)
        Me.borderWidth.TabIndex = 4
        Me.borderWidth.Value = New Decimal(New Integer() {1, 0, 0, 0})
        '
        'brushColorButton
        '
        Me.brushColorButton.Location = New System.Drawing.Point(48, 104)
        Me.brushColorButton.Name = "brushColorButton"
        Me.brushColorButton.Size = New System.Drawing.Size(48, 24)
        Me.brushColorButton.TabIndex = 3
        Me.brushColorButton.Text = "Fill"
        '
        'penColorButton
        '
        Me.penColorButton.Location = New System.Drawing.Point(48, 40)
        Me.penColorButton.Name = "penColorButton"
        Me.penColorButton.Size = New System.Drawing.Size(48, 24)
        Me.penColorButton.TabIndex = 2
        Me.penColorButton.Text = "Border"
        '
        'brushColor
        '
        Me.brushColor.BackColor = System.Drawing.Color.White
        Me.brushColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D
        Me.brushColor.Location = New System.Drawing.Point(16, 104)
        Me.brushColor.Name = "brushColor"
        Me.brushColor.Size = New System.Drawing.Size(24, 24)
        Me.brushColor.TabIndex = 1
        '
        'penColor
        '
        Me.penColor.BackColor = System.Drawing.Color.Black
        Me.penColor.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D
        Me.penColor.Location = New System.Drawing.Point(16, 40)
        Me.penColor.Name = "penColor"
        Me.penColor.Size = New System.Drawing.Size(24, 24)
        Me.penColor.TabIndex = 0
        '
        'shapesPanel
        '
        Me.shapesPanel.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
                    Or System.Windows.Forms.AnchorStyles.Left) _
                    Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.shapesPanel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
        Me.shapesPanel.Location = New System.Drawing.Point(120, 8)
        Me.shapesPanel.Name = "shapesPanel"
        Me.shapesPanel.Size = New System.Drawing.Size(496, 408)
        Me.shapesPanel.TabIndex = 6
        '
        'ellipseButton
        '
        Me.ellipseButton.Location = New System.Drawing.Point(8, 136)
        Me.ellipseButton.Name = "ellipseButton"
        Me.ellipseButton.Size = New System.Drawing.Size(96, 24)
        Me.ellipseButton.TabIndex = 7
        Me.ellipseButton.Text = "Ellipse"
        '
        'showHandles
        '
        Me.showHandles.Checked = True
        Me.showHandles.CheckState = System.Windows.Forms.CheckState.Checked
        Me.showHandles.Location = New System.Drawing.Point(16, 392)
        Me.showHandles.Name = "showHandles"
        Me.showHandles.Size = New System.Drawing.Size(96, 24)
        Me.showHandles.TabIndex = 8
        Me.showHandles.Text = "Show Handles"
        '
        'closedCurveButton
        '
        Me.closedCurveButton.Location = New System.Drawing.Point(8, 200)
        Me.closedCurveButton.Name = "closedCurveButton"
        Me.closedCurveButton.Size = New System.Drawing.Size(96, 24)
        Me.closedCurveButton.TabIndex = 9
        Me.closedCurveButton.Text = "Closed Curve"
        '
        'rectangleButton
        '
        Me.rectangleButton.Location = New System.Drawing.Point(8, 104)
        Me.rectangleButton.Name = "rectangleButton"
        Me.rectangleButton.Size = New System.Drawing.Size(96, 24)
        Me.rectangleButton.TabIndex = 10
        Me.rectangleButton.Text = "Rectangle"
        '
        'segmentButton
        '
        Me.segmentButton.Location = New System.Drawing.Point(8, 8)
        Me.segmentButton.Name = "segmentButton"
        Me.segmentButton.Size = New System.Drawing.Size(96, 24)
        Me.segmentButton.TabIndex = 11
        Me.segmentButton.Text = "Segment"
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(624, 422)
        Me.Controls.Add(Me.segmentButton)
        Me.Controls.Add(Me.rectangleButton)
        Me.Controls.Add(Me.closedCurveButton)
        Me.Controls.Add(Me.showHandles)
        Me.Controls.Add(Me.ellipseButton)
        Me.Controls.Add(Me.shapesPanel)
        Me.Controls.Add(Me.Panel1)
        Me.Controls.Add(Me.curveButton)
        Me.Controls.Add(Me.polygonButton)
        Me.Controls.Add(Me.lineButton)
        Me.KeyPreview = True
        Me.Name = "Form1"
        Me.Text = "Shape Painter"
        Me.Panel1.ResumeLayout(False)
        CType(Me.numberPoints, System.ComponentModel.ISupportInitialize).EndInit()
        CType(Me.borderWidth, System.ComponentModel.ISupportInitialize).EndInit()
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private numPoints As Integer = 0
    Private definingShape As Boolean = False
    Private shiftDown As Boolean = False
    Private altDown As Boolean = False

    Private WithEvents newShape As Shape
    Private shapes As New Collection

    Private Sub changePenColor(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles penColorButton.Click
        Dim r As DialogResult
        r = ColorDialog1.ShowDialog
        If r = DialogResult.OK Then
            penColor.BackColor = ColorDialog1.Color
        End If
    End Sub

    Private Sub changeBrushColor(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles brushColorButton.Click
        Dim r As DialogResult
        r = ColorDialog1.ShowDialog
        If r = DialogResult.OK Then
            brushColor.BackColor = ColorDialog1.Color
        End If
    End Sub

    Private Sub showHandles_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles showHandles.CheckedChanged
        shapesPanel.Refresh()
    End Sub

    Private Sub unlockSelections(ByVal unlocked As Boolean)
        lineButton.Enabled = unlocked
        curveButton.Enabled = unlocked
        polygonButton.Enabled = unlocked
        ellipseButton.Enabled = unlocked
        segmentButton.Enabled = unlocked
        rectangleButton.Enabled = unlocked
        closedCurveButton.Enabled = unlocked
        penColorButton.Enabled = unlocked
        brushColorButton.Enabled = unlocked
        numberPoints.Enabled = unlocked
        borderWidth.Enabled = unlocked
    End Sub

    Private Function preparePolygon() As Boolean
        newShape.penColor = penColor.BackColor
        newShape.penWidth = borderWidth.Value
        numPoints = numberPoints.Value
        unlockSelections(False)
    End Function

    Private Sub ellipseButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ellipseButton.Click
        newShape = New Ellipse
        preparePolygon()
        numPoints = 2 ' override the current selection for an ellipse
        newShape.brushColor = brushColor.BackColor
        newShape.isFilled = True
        shapes.Add(newShape)
        newShape.definePoints(numPoints, shapesPanel)
    End Sub

    Private Sub rectangleButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles rectangleButton.Click
        newShape = New myRectangle
        preparePolygon()
        numPoints = 2 ' override the current selection for an ellipse
        newShape.brushColor = brushColor.BackColor
        newShape.isFilled = True
        shapes.Add(newShape)
        newShape.definePoints(numPoints, shapesPanel)
    End Sub

    Private Sub curveButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles curveButton.Click
        newShape = New Curve
        preparePolygon()
        shapes.Add(newShape)
        newShape.definePoints(numPoints, shapesPanel)
    End Sub

    Private Sub closedCurveButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles closedCurveButton.Click
        If numberPoints.Value >= 4 Then
            newShape = New ClosedCurve
            preparePolygon()
            newShape.brushColor = brushColor.BackColor
            newShape.isFilled = True
            shapes.Add(newShape)
            newShape.definePoints(numPoints, shapesPanel)
        Else
            MsgBox("A closed curve must have at least four points", MsgBoxStyle.Information, "Invalid Number of Points")
        End If
    End Sub

    Private Sub lineButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lineButton.Click
        newShape = New Line
        preparePolygon()
        shapes.Add(newShape)
        newShape.definePoints(numPoints, shapesPanel)
    End Sub

    Private Sub polygonButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles polygonButton.Click
        newShape = New Polygon
        preparePolygon()
        newShape.brushColor = brushColor.BackColor
        newShape.isFilled = True
        shapes.Add(newShape)
        newShape.definePoints(numPoints, shapesPanel)
    End Sub

    Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
        If e.Alt Then
            altDown = True
        End If
        If e.Shift Then
            shiftDown = True
        End If
    End Sub

    Private Sub Form1_KeyUp(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyUp
        If Not e.Alt Then
            altDown = False
        End If
        If Not e.Shift Then
            shiftDown = False
        End If
    End Sub

    Private Sub shapesPanel_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles shapesPanel.Paint
        Dim s As Shape

        For Each s In shapes
            s.showHandles = showHandles.Checked
            s.Paint(e.Graphics)
        Next s
    End Sub

    Private Sub shapesPanel_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles shapesPanel.MouseDown
        Dim s As Shape
        Dim p As New Point(e.X, e.Y)
        Dim overHandle As Integer

        If e.Button = MouseButtons.Left Then
            For Each s In shapes
                If s.currentAction <> Shape.action.none Then
                    Exit Sub
                End If
                overHandle = s.isOverHandle(p)
                If overHandle <> -1 Then
                    If (Not shiftDown) And (Not altDown) Then
                        s.dragPoint(overHandle, shapesPanel)
                    ElseIf shiftDown And (Not altDown) Then
                        s.dragShape(p, shapesPanel)
                    ElseIf (Not shiftDown) And altDown Then
                        s.rotateShape(New Point(e.X, e.Y), shapesPanel)
                    End If
                    Exit For
                End If
            Next s
        End If
    End Sub

    Private Sub newShape_definitionComplete() Handles newShape.definitionComplete
        unlockSelections(True)
    End Sub

    Private Sub segmentButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles segmentButton.Click
        newShape = New Segment
        preparePolygon()
        numPoints = 2 ' override the current selection for a segment
        shapes.Add(newShape)
        newShape.definePoints(numPoints, shapesPanel)
    End Sub

End Class
I'm just gonna go adeah and accept this now.  We are getting a book tommorow that goes into GDI+ in detail.  This has surely given me enough to get going with.  If you would like I could send you the code when we are done with the whole sketch routine.
I think the tough thing will be resize the drawing area dynamically and also make snap to a grid of a different scale.  If you would like the program either post your email or I can post ours.

Thanks for your help and fast response time,
Jim
QualityData,

You closed the question before I posted the "Shape Painter" example.  Just wanted to make sure you saw it...

~IM
Thanks Again