Leo DiNicola
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
Help,
Jim
ASKER
IT's not that bad. We saw that on a previously asked question. It's still a little piecemeal.
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
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
That's my guess
ASKER
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
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
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
ASKER
Wow Idle_Mind! Yes those will cretainly help. I'd appreciate the other project you were talking about.
Thanks a ton, Jim
Thanks a ton, Jim
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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(mousePosi tion.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(-oldP osition.X, -oldPosition.Y)
g.RotateTransform(shapeAng le, Drawing2D.MatrixOrder.Appe nd)
g.TranslateTransform(oldPo sition.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(-oldP osition.X, -oldPosition.Y)
g.RotateTransform(shapeAng le, Drawing2D.MatrixOrder.Appe nd)
g.TransformPoints(System.D rawing.Dra wing2D.Coo rdinateSpa ce.Page, _
System.Drawing.Drawing2D.C oordinateS pace.World , newPts)
' offset shape back to its original location
g.TranslateTransform(oldPo sition.X, oldPosition.Y)
g.TransformPoints(System.D rawing.Dra wing2D.Coo rdinateSpa ce.Page, _
System.Drawing.Drawing2D.C oordinateS pace.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(-oldP osition.X, -oldPosition.Y)
g.RotateTransform(angleInD egrees, Drawing2D.MatrixOrder.Appe nd)
g.TransformPoints(System.D rawing.Dra wing2D.Coo rdinateSpa ce.Page, _
System.Drawing.Drawing2D.C oordinateS pace.World , pts)
' reset the transform world
g.ResetTransform()
' offset shape back to its original location
g.TranslateTransform(oldPo sition.X, oldPosition.Y)
g.TransformPoints(System.D rawing.Dra wing2D.Coo rdinateSpa ce.Page, _
System.Drawing.Drawing2D.C oordinateS pace.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(ByVa l sender As Object, ByVal e As System.Windows.Forms.Mouse EventArgs) 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(ByVa l sender As Object, ByVal e As System.Windows.Forms.Mouse EventArgs) 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(-delt aAngle, sourcePanel)
lastAngle = curAngle
End If
End Select
End Sub
Protected Overridable Sub sourcePanel_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.Mouse EventArgs) 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(-delt aAngle, 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.ICon tainer
'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.Color Dialog
Friend WithEvents penColor As System.Windows.Forms.Panel
Friend WithEvents brushColor As System.Windows.Forms.Panel
Friend WithEvents brushColorButton As System.Windows.Forms.Butto n
Friend WithEvents lineButton As System.Windows.Forms.Butto n
Friend WithEvents polygonButton As System.Windows.Forms.Butto n
Friend WithEvents curveButton As System.Windows.Forms.Butto n
Friend WithEvents ellipseButton As System.Windows.Forms.Butto n
Friend WithEvents penColorButton As System.Windows.Forms.Butto n
Friend WithEvents shapesPanel As System.Windows.Forms.Panel
Friend WithEvents showHandles As System.Windows.Forms.Check Box
Friend WithEvents borderWidth As System.Windows.Forms.Numer icUpDown
Friend WithEvents Label2 As System.Windows.Forms.Label
Friend WithEvents numberPoints As System.Windows.Forms.Numer icUpDown
Friend WithEvents Label1 As System.Windows.Forms.Label
Friend WithEvents closedCurveButton As System.Windows.Forms.Butto n
Friend WithEvents rectangleButton As System.Windows.Forms.Butto n
Friend WithEvents segmentButton As System.Windows.Forms.Butto n
<System.Diagnostics.Debugg erStepThro ugh()> Private Sub InitializeComponent()
Me.lineButton = New System.Windows.Forms.Butto n
Me.polygonButton = New System.Windows.Forms.Butto n
Me.curveButton = New System.Windows.Forms.Butto n
Me.Panel1 = New System.Windows.Forms.Panel
Me.numberPoints = New System.Windows.Forms.Numer icUpDown
Me.Label1 = New System.Windows.Forms.Label
Me.Label2 = New System.Windows.Forms.Label
Me.borderWidth = New System.Windows.Forms.Numer icUpDown
Me.brushColorButton = New System.Windows.Forms.Butto n
Me.penColorButton = New System.Windows.Forms.Butto n
Me.brushColor = New System.Windows.Forms.Panel
Me.penColor = New System.Windows.Forms.Panel
Me.ColorDialog1 = New System.Windows.Forms.Color Dialog
Me.shapesPanel = New System.Windows.Forms.Panel
Me.ellipseButton = New System.Windows.Forms.Butto n
Me.showHandles = New System.Windows.Forms.Check Box
Me.closedCurveButton = New System.Windows.Forms.Butto n
Me.rectangleButton = New System.Windows.Forms.Butto n
Me.segmentButton = New System.Windows.Forms.Butto n
Me.Panel1.SuspendLayout()
CType(Me.numberPoints, System.ComponentModel.ISup portInitia lize).Begi nInit()
CType(Me.borderWidth, System.ComponentModel.ISup portInitia lize).Begi nInit()
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.Borde rStyle.Fix edSingle
Me.Panel1.Controls.Add(Me. numberPoin ts)
Me.Panel1.Controls.Add(Me. Label1)
Me.Panel1.Controls.Add(Me. Label2)
Me.Panel1.Controls.Add(Me. borderWidt h)
Me.Panel1.Controls.Add(Me. brushColor Button)
Me.Panel1.Controls.Add(Me. penColorBu tton)
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.ContentAlig nment.Midd leRight
'
'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.ContentAlig nment.Midd leCenter
'
'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.Locati on = New System.Drawing.Point(48, 104)
Me.brushColorButton.Name = "brushColorButton"
Me.brushColorButton.Size = New System.Drawing.Size(48, 24)
Me.brushColorButton.TabInd ex = 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.Borde rStyle.Fix ed3D
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.Borde rStyle.Fix ed3D
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.Fo rms.Anchor Styles.Top Or System.Windows.Forms.Ancho rStyles.Bo ttom) _
Or System.Windows.Forms.Ancho rStyles.Le ft) _
Or System.Windows.Forms.Ancho rStyles.Ri ght), System.Windows.Forms.Ancho rStyles)
Me.shapesPanel.BorderStyle = System.Windows.Forms.Borde rStyle.Fix edSingle
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.Check State.Chec ked
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.Locat ion = New System.Drawing.Point(8, 200)
Me.closedCurveButton.Name = "closedCurveButton"
Me.closedCurveButton.Size = New System.Drawing.Size(96, 24)
Me.closedCurveButton.TabIn dex = 9
Me.closedCurveButton.Text = "Closed Curve"
'
'rectangleButton
'
Me.rectangleButton.Locatio n = New System.Drawing.Point(8, 104)
Me.rectangleButton.Name = "rectangleButton"
Me.rectangleButton.Size = New System.Drawing.Size(96, 24)
Me.rectangleButton.TabInde x = 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.segment Button)
Me.Controls.Add(Me.rectang leButton)
Me.Controls.Add(Me.closedC urveButton )
Me.Controls.Add(Me.showHan dles)
Me.Controls.Add(Me.ellipse Button)
Me.Controls.Add(Me.shapesP anel)
Me.Controls.Add(Me.Panel1)
Me.Controls.Add(Me.curveBu tton)
Me.Controls.Add(Me.polygon Button)
Me.Controls.Add(Me.lineBut ton)
Me.KeyPreview = True
Me.Name = "Form1"
Me.Text = "Shape Painter"
Me.Panel1.ResumeLayout(Fal se)
CType(Me.numberPoints, System.ComponentModel.ISup portInitia lize).EndI nit()
CType(Me.borderWidth, System.ComponentModel.ISup portInitia lize).EndI nit()
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(numP oints, shapesPanel)
End Sub
Private Sub rectangleButton_Click(ByVa l 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(numP oints, 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(numP oints, shapesPanel)
End Sub
Private Sub closedCurveButton_Click(By Val 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(numP oints, 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(numP oints, 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(numP oints, shapesPanel)
End Sub
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv entArgs) 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.KeyEv entArgs) 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.Paint EventArgs) 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(ByVa l sender As Object, ByVal e As System.Windows.Forms.Mouse EventArgs) 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_definitionComplet e() Handles newShape.definitionComplet e
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(numP oints, shapesPanel)
End Sub
End Class
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(mousePosi
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(-oldP
g.RotateTransform(shapeAng
g.TranslateTransform(oldPo
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(-oldP
g.RotateTransform(shapeAng
g.TransformPoints(System.D
System.Drawing.Drawing2D.C
' offset shape back to its original location
g.TranslateTransform(oldPo
g.TransformPoints(System.D
System.Drawing.Drawing2D.C
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
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
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
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(-oldP
g.RotateTransform(angleInD
g.TransformPoints(System.D
System.Drawing.Drawing2D.C
' reset the transform world
g.ResetTransform()
' offset shape back to its original location
g.TranslateTransform(oldPo
g.TransformPoints(System.D
System.Drawing.Drawing2D.C
' 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(ByVa
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(ByVa
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(-delt
lastAngle = curAngle
End If
End Select
End Sub
Protected Overridable Sub sourcePanel_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.Mouse
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(-delt
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
' 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.ICon
'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.Color
Friend WithEvents penColor As System.Windows.Forms.Panel
Friend WithEvents brushColor As System.Windows.Forms.Panel
Friend WithEvents brushColorButton As System.Windows.Forms.Butto
Friend WithEvents lineButton As System.Windows.Forms.Butto
Friend WithEvents polygonButton As System.Windows.Forms.Butto
Friend WithEvents curveButton As System.Windows.Forms.Butto
Friend WithEvents ellipseButton As System.Windows.Forms.Butto
Friend WithEvents penColorButton As System.Windows.Forms.Butto
Friend WithEvents shapesPanel As System.Windows.Forms.Panel
Friend WithEvents showHandles As System.Windows.Forms.Check
Friend WithEvents borderWidth As System.Windows.Forms.Numer
Friend WithEvents Label2 As System.Windows.Forms.Label
Friend WithEvents numberPoints As System.Windows.Forms.Numer
Friend WithEvents Label1 As System.Windows.Forms.Label
Friend WithEvents closedCurveButton As System.Windows.Forms.Butto
Friend WithEvents rectangleButton As System.Windows.Forms.Butto
Friend WithEvents segmentButton As System.Windows.Forms.Butto
<System.Diagnostics.Debugg
Me.lineButton = New System.Windows.Forms.Butto
Me.polygonButton = New System.Windows.Forms.Butto
Me.curveButton = New System.Windows.Forms.Butto
Me.Panel1 = New System.Windows.Forms.Panel
Me.numberPoints = New System.Windows.Forms.Numer
Me.Label1 = New System.Windows.Forms.Label
Me.Label2 = New System.Windows.Forms.Label
Me.borderWidth = New System.Windows.Forms.Numer
Me.brushColorButton = New System.Windows.Forms.Butto
Me.penColorButton = New System.Windows.Forms.Butto
Me.brushColor = New System.Windows.Forms.Panel
Me.penColor = New System.Windows.Forms.Panel
Me.ColorDialog1 = New System.Windows.Forms.Color
Me.shapesPanel = New System.Windows.Forms.Panel
Me.ellipseButton = New System.Windows.Forms.Butto
Me.showHandles = New System.Windows.Forms.Check
Me.closedCurveButton = New System.Windows.Forms.Butto
Me.rectangleButton = New System.Windows.Forms.Butto
Me.segmentButton = New System.Windows.Forms.Butto
Me.Panel1.SuspendLayout()
CType(Me.numberPoints, System.ComponentModel.ISup
CType(Me.borderWidth, System.ComponentModel.ISup
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.Borde
Me.Panel1.Controls.Add(Me.
Me.Panel1.Controls.Add(Me.
Me.Panel1.Controls.Add(Me.
Me.Panel1.Controls.Add(Me.
Me.Panel1.Controls.Add(Me.
Me.Panel1.Controls.Add(Me.
Me.Panel1.Controls.Add(Me.
Me.Panel1.Controls.Add(Me.
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.ContentAlig
'
'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.ContentAlig
'
'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.Locati
Me.brushColorButton.Name = "brushColorButton"
Me.brushColorButton.Size = New System.Drawing.Size(48, 24)
Me.brushColorButton.TabInd
Me.brushColorButton.Text = "Fill"
'
'penColorButton
'
Me.penColorButton.Location
Me.penColorButton.Name = "penColorButton"
Me.penColorButton.Size = New System.Drawing.Size(48, 24)
Me.penColorButton.TabIndex
Me.penColorButton.Text = "Border"
'
'brushColor
'
Me.brushColor.BackColor = System.Drawing.Color.White
Me.brushColor.BorderStyle = System.Windows.Forms.Borde
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.Borde
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.Fo
Or System.Windows.Forms.Ancho
Or System.Windows.Forms.Ancho
Me.shapesPanel.BorderStyle
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.Check
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.Locat
Me.closedCurveButton.Name = "closedCurveButton"
Me.closedCurveButton.Size = New System.Drawing.Size(96, 24)
Me.closedCurveButton.TabIn
Me.closedCurveButton.Text = "Closed Curve"
'
'rectangleButton
'
Me.rectangleButton.Locatio
Me.rectangleButton.Name = "rectangleButton"
Me.rectangleButton.Size = New System.Drawing.Size(96, 24)
Me.rectangleButton.TabInde
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.segment
Me.Controls.Add(Me.rectang
Me.Controls.Add(Me.closedC
Me.Controls.Add(Me.showHan
Me.Controls.Add(Me.ellipse
Me.Controls.Add(Me.shapesP
Me.Controls.Add(Me.Panel1)
Me.Controls.Add(Me.curveBu
Me.Controls.Add(Me.polygon
Me.Controls.Add(Me.lineBut
Me.KeyPreview = True
Me.Name = "Form1"
Me.Text = "Shape Painter"
Me.Panel1.ResumeLayout(Fal
CType(Me.numberPoints, System.ComponentModel.ISup
CType(Me.borderWidth, System.ComponentModel.ISup
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
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(numP
End Sub
Private Sub rectangleButton_Click(ByVa
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(numP
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(numP
End Sub
Private Sub closedCurveButton_Click(By
If numberPoints.Value >= 4 Then
newShape = New ClosedCurve
preparePolygon()
newShape.brushColor = brushColor.BackColor
newShape.isFilled = True
shapes.Add(newShape)
newShape.definePoints(numP
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(numP
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(numP
End Sub
Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEv
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.KeyEv
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.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(ByVa
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_definitionComplet
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(numP
End Sub
End Class
ASKER
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
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
You closed the question before I posted the "Shape Painter" example. Just wanted to make sure you saw it...
~IM
ASKER
Thanks Again
http://www.bobpowell.net/faqmain.htm (it's from one of the other experts :P)