Imports System.io
Imports System.Drawing.Imaging

Public Class Form1

  Dim VersionNumber As Single
  Dim myBitmap As New System.Drawing.Bitmap(800, 600)
  Dim myBitmapGraphics = Graphics.FromImage(myBitmap)
  Public PolygonList As New ArrayList()
  Public ListOfPoints As New PointsList
  Public Polyshap(18) As Polyshape
  Public Shape As Integer
  Public Colour As Color
  Dim ScaleFactor As Double = 1
  Dim Origin As Point
  Dim MousePos As PointF
  Dim Drag1, Drag2 As Point
  Dim DragRectangle As Boolean = False
  Dim PointsSelected As Boolean = False
  Dim filename As String = "Default"
  Dim modified As Boolean = False
  'Principal edge lengths
  Dim A As Double = 100
  Dim B As Double = 2 * A * Math.Cos(2 * Math.PI / 5)
  Dim C As Double = 2 * B * Math.Cos(2 * Math.PI / 5)
  Dim D As Double = 2 * C * Math.Cos(2 * Math.PI / 5)
  Dim oldM As Integer = 0

  Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress
    Beep()
    If Asc(e.KeyChar) = 24 Then Deletelast.PerformClick()
  End Sub

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    ConstructShape(1, "Triangle", 3)
    ConstructShape(2, "Square", 4)
    ConstructShape(3, "Pentagon", 5)
    ConstructShape(4, "Hexagon", 6)
    ConstructShape(5, "Octagon", 8)
    ConstructShape(6, "Decagon", 10)
    ConstructShape(7, "Custom", 12)
    ConstructQuadrilateral(8, "45 Rhomb", 0, 0, 0, 0, 0, A, A, A, A, 45, 135, 45, 135, 0, 0, 0, 0, 0, 0, 0, 0) '45 rhomb
    ConstructQuadrilateral(9, "Convex quad", 1, 1, 2, 3, 4, 160, 60, 120, 0, 75, 75, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 'convex quad
    ConstructQuadrilateral(10, "Concave quad", 1, 1, 2, 3, 4, 150, 110, 120, 0, 140, -35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 'concav quad
    ConstructQuadrilateral(11, "Kite", 1, 1, 2, 2, 1, A, B, B, A, 108, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 'kite
    ConstructQuadrilateral(12, "Dart", 1, 1, 2, 2, 1, A, B, B, A, 144, -36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) 'dart
    ConstructQuadrilateral(13, "Penrose Kite", -1, 1, -1, 1, -1, A, B, B, A, 108, 36, 0, 0, 100, C, 100, B, 0, 2, 0, 1) 'Penrose kite
    ConstructQuadrilateral(14, "Penrose Dart", -1, -1, 1, -1, 1, A, B, B, A, 144, -36, 0, 0, 0, D + 100, 0, C + 100, 0, 2, 0, 1) 'Penrose dart
    ConstructQuadrilateral(15, "36 Rhomb", 0, 0, 0, 0, 0, A, A, A, A, 36, 144, 36, 144, 0, 0, 0, 0, 0, 0, 0, 0) '36 rhomb
    ConstructQuadrilateral(16, "72 Rhomb", 0, 0, 0, 0, 0, A, A, A, A, 72, 108, 72, 108, 0, 0, 0, 0, 0, 0, 0, 0) '72 rhomb
    ConstructQuadrilateral(17, "36 Penrose Rhomb", -1, 1, -1, 2, -2, A, A, A, A, 36, 144, 36, 144, 30, 0, 100, 0, 2, 0, 1, 0) '36 Penrose rhomb
    ConstructQuadrilateral(18, "72 Penrose Rhomb", -1, 1, 2, -2, -1, A, A, A, A, 72, 108, 72, 108, 0, 100, 0, 70, 0, 1, 0, 2) '72 Penrose rhomb
    RestoreDefaultColours()
    RadioButton1.Checked = True
    Shape = 1
    Colour = Colours(1)
    Origin.X = 400 : Origin.Y = 300
    PictureBox1.BackgroundImage = myBitmap
    PictureBox1.AllowDrop = True
    TrackBar1.Value = 20 * ScaleFactor
    Label1.Text = "Scale factor =" & Str(100 * ScaleFactor) & "%"
  End Sub

  Private Sub RestoreDefaultColours()
    Colours(1) = Color.Red
    Colours(2) = Color.RoyalBlue
    Colours(3) = Color.LimeGreen
    Colours(4) = Color.Yellow
    Colours(5) = Color.Orange
    Colours(6) = Color.MediumOrchid
    Colours(7) = Color.Turquoise
    Colours(8) = Color.SpringGreen
    Colours(9) = Color.LightSteelBlue
    Colours(10) = Color.LightGreen
    Colours(11) = Color.HotPink
    Colours(12) = Color.SlateBlue
    Colours(13) = Color.Pink
    Colours(14) = Color.LightSkyBlue
    Colours(15) = Color.DarkOrange
    Colours(16) = Color.SeaGreen
    Colours(17) = Color.Tomato
    Colours(18) = Color.LightSalmon
    LineColours(0) = Color.Black
    LineColours(1) = Color.Blue
    LineColours(2) = Color.White
    Dim k As Integer
    For Each b As Control In Panel2.Controls
      k = Val(Mid(b.Tag, 2))
      If Microsoft.VisualBasic.Left(b.Tag, 1) = "B" Then
        If k < 50 Then
          b.BackColor = Colours(k)
        Else
          b.BackColor = LineColours(k - 50)
        End If
      End If
    Next
  End Sub

  Private Sub modify(ByVal b As Boolean)
    If b Then Me.Text = "Easy Tiler *" Else Me.Text = "Easy Tiler"
    modified = b
  End Sub

  Private Sub ConstructShape(ByVal s As Integer, ByVal ID As String, ByVal N As Integer)
    Polyshap(s).NumberOfNodes = N
    Dim vectorlist(N) As vector
    For I As Integer = 0 To N - 1
      vectorlist(I).Magnitude = A
      vectorlist(I).Direction = 360 / N
      vectorlist(I).ID.Parity = 0
      vectorlist(I).ID.Radius = 0
      vectorlist(I).ID.Colour = 0
    Next
    Polyshap(s).ListOfVectors = vectorlist
    Polyshap(s).Paritymode = 0
    Polyshap(s).type = ID
  End Sub

  Private Sub ConstructTriangle(ByVal s, ByVal pm, ByVal p0, ByVal m0, ByVal d0, ByVal p1, ByVal m1, ByVal p2)
    Polyshap(s).NumberOfNodes = 3
    Dim vectorlist(4) As vector
    Dim p As New PointF(0, 0)
    Dim q As New PointF(0, 0)
    Dim angle As Double = 0
    Dim angle2 As Double
    vectorlist(0).ID.Parity = p0
    vectorlist(0).Magnitude = m0
    vectorlist(0).Direction = d0
    q.X = vectorlist(0).Magnitude
    angle += vectorlist(0).Direction
    vectorlist(1).ID.Parity = p1
    vectorlist(1).Magnitude = m1
    q.X += vectorlist(1).Magnitude * Math.Cos(Rad(angle))
    q.Y += vectorlist(1).Magnitude * Math.Sin(Rad(angle))
    vectorlist(2).Magnitude = PointsList.distance(q, p)
    angle2 = 180 * PointsList.arctan(q, p) / Math.PI
    If angle2 < 0 Then angle2 += 360
    vectorlist(1).Direction = angle2 - angle
    vectorlist(2).ID.Parity = p2
    vectorlist(2).Direction = 360 - angle2
    Polyshap(s).ListOfVectors = vectorlist
    Polyshap(s).Paritymode = pm
  End Sub

  Private Sub ConstructQuadrilateral(ByVal s, ByVal ID, ByVal pm, ByVal p0, ByVal p1, ByVal p2, ByVal p3, ByVal m0, ByVal m1, ByVal m2, ByVal m3, ByVal d0, ByVal d1, ByVal d2, ByVal d3, ByVal r0, ByVal r1, ByVal r2, ByVal r3, ByVal c0, ByVal c1, ByVal c2, ByVal c3)
    Polyshap(s).NumberOfNodes = 4
    Dim vectorlist(4) As vector
    Dim p As New PointF(0, 0)
    Dim q As New PointF(0, 0)
    Dim angle As Double = 0
    Dim angle2 As Double
    vectorlist(0).ID.Parity = p0
    vectorlist(0).Magnitude = m0
    vectorlist(0).Direction = d0
    vectorlist(0).ID.Radius = r0
    vectorlist(0).ID.Colour = c0
    q.X = vectorlist(0).Magnitude
    angle += vectorlist(0).Direction
    vectorlist(1).ID.Parity = p1
    vectorlist(1).Magnitude = m1
    vectorlist(1).Direction = d1
    vectorlist(1).ID.Radius = r1
    vectorlist(1).ID.Colour = c1
    q.X += vectorlist(1).Magnitude * Math.Cos(Rad(angle))
    q.Y += vectorlist(1).Magnitude * Math.Sin(Rad(angle))
    vectorlist(2).ID.Parity = p2
    angle += vectorlist(1).Direction
    vectorlist(2).Magnitude = m2
    q.X += vectorlist(2).Magnitude * Math.Cos(Rad(angle))
    q.Y += vectorlist(2).Magnitude * Math.Sin(Rad(angle))
    vectorlist(3).Magnitude = PointsList.distance(q, p)
    angle2 = 180 * PointsList.arctan(q, p) / Math.PI
    If angle2 < 0 Then angle2 += 360
    vectorlist(2).Direction = angle2 - angle
    vectorlist(2).ID.Radius = r2
    vectorlist(2).ID.Colour = c2
    vectorlist(3).ID.Parity = p3
    vectorlist(3).Direction = 360 - angle2
    vectorlist(3).ID.Radius = r3
    vectorlist(3).ID.Colour = c3
    Polyshap(s).ListOfVectors = vectorlist
    Polyshap(s).Paritymode = pm
    Polyshap(s).Type = ID
  End Sub

  Private Function Rad(ByVal a)
    Return Math.PI * a / 180
  End Function

  Private Sub PictureBox1_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles PictureBox1.DragDrop
    Dim filenames As String()
    filenames = DirectCast(e.Data.GetData(DataFormats.FileDrop), String())
    filename = filenames(0)
    Load_File()
  End Sub

  Private Sub PictureBox1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles PictureBox1.DragEnter
    If e.Data.GetDataPresent(DataFormats.FileDrop) Then
      e.Effect = DragDropEffects.Copy
    Else
      e.Effect = DragDropEffects.None
    End If
  End Sub

  Private Sub PictureBox1_DragOver(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles PictureBox1.DragOver
  End Sub

  Private Sub PictureBox1_MouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDoubleClick
    MousePos = New Point((e.X - Origin.X) / ScaleFactor, (e.Y - Origin.Y) / ScaleFactor)
    Dim pp As New Polygon
    If findPolygon(MousePos, pp) Then
      PolygonList.Remove(pp)
      resetflags()
      RedrawBitmap()
      modify(True)
    End If
  End Sub

  Private Sub PictureBox1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) _
  Handles PictureBox1.MouseDown
    Drag1 = e.Location
    MousePos = New Point((e.X - Origin.X) / ScaleFactor, (e.Y - Origin.Y) / ScaleFactor)
    If Shape = 0 Then
      If e.Button = Windows.Forms.MouseButtons.Left And My.Computer.Keyboard.ShiftKeyDown Then
        DragRectangle = True
      End If
    Else
      Dim pp As New Polygon
      If e.Button = Windows.Forms.MouseButtons.Right Then
        If findPolygon(MousePos, pp) Then
          pp.FILLCOLOUR = Colour
        End If
      Else
        AddNewPolygon(MousePos)
      End If
      resetflags()
      modify(True)
    End If
  End Sub

  Private Sub PictureBox1_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseMove
    If Shape > 0 Then Exit Sub
    If DragRectangle Then
      Drag2 = e.Location
      PictureBox1.Invalidate()
      Exit Sub
    End If
    If e.Button <> 0 Then
      For p As Integer = 0 To ListOfPoints.maxpoint - 1
        If e.Button = Windows.Forms.MouseButtons.Left Or ListOfPoints.pointsselectedflag(p) Then
          ListOfPoints.pointslist(p).X += (e.X - Drag1.X) / ScaleFactor
          ListOfPoints.pointslist(p).Y += (e.Y - Drag1.Y) / ScaleFactor
        End If
      Next
      Drag1 = e.Location
      RedrawBitmap()
    End If
  End Sub

  Private Sub PictureBox1_MouseUp(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
    If DragRectangle Then
      Dim pp, p1, p2 As New PointF
      p1.X = (Drag1.X - Origin.X) / ScaleFactor
      p1.Y = (Drag1.Y - Origin.Y) / ScaleFactor
      p2.X = (Drag2.X - Origin.X) / ScaleFactor
      p2.Y = (Drag2.Y - Origin.Y) / ScaleFactor
      For p As Integer = 0 To ListOfPoints.maxpoint - 1
        If ListOfPoints.pointslistflag(p) Then
          pp = ListOfPoints.GetPoint(p)
          ListOfPoints.pointsselectedflag(p) = pp.X > p1.X And pp.Y > p1.Y And pp.X < p2.X And pp.Y < p2.Y
        End If
      Next
    End If
    DragRectangle = False
    RedrawBitmap()
  End Sub

  Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint
    If DragRectangle Then
      Dim dashed_pen As New Pen(Color.Gray, 1)
      dashed_pen.DashStyle = Drawing2D.DashStyle.Dash
      e.Graphics.DrawRectangle(dashed_pen, Drag1.X, Drag1.Y, Drag2.X - Drag1.X, Drag2.Y - Drag1.Y)
    End If
  End Sub

  Private Sub AddNewPolygon(ByVal p0)
    Dim p00 As PointF
    Dim p1, p2 As Integer
    Dim parity As Integer
    Dim d As Double
    Dim angle As Double
    Dim X As Double
    Dim Y As Double
    Dim p As New Polygon
    p.FILLCOLOUR = Colour
    p.NPOINTS = Polyshap(Shape).NumberOfNodes
    ReDim p.POINTS(p.NPOINTS - 1)
    If ListOfPoints.maxpoint = 0 Or My.Computer.Keyboard.CtrlKeyDown Then
      p00.X = p0.X - 10
      p00.Y = p0.Y
      ListOfPoints.add(p00, "Don't search")
      p00.X = p0.X - 10
      p00.Y = p0.Y - Polyshap(Shape).ListOfVectors(0).Magnitude
      ListOfPoints.add(p00, "Don't search")
    End If
    ListOfPoints.nearest(p0, p1, p2, d, angle)
    If Flipped Then angle = -angle
    parity = FindEdge(p1, p2)
    If parity = 99 And PolygonList.Count > 0 And My.Computer.Keyboard.CtrlKeyDown = False Then
      'MsgBox("I cannot find a suitable polygon near here") : Exit Sub
      Beep() : Exit Sub
    End If
    If parity = 99 Then parity = 0 'set parity to zero if new 
    p0 = ListOfPoints.pointslist(p1)
    p.POINTS(0).Point = ListOfPoints.add(p0, "Search")
    X = p0.X : Y = p0.Y
    Dim mag As Double
    Dim dir As Double
    Dim M, MM As Integer
    'we need to start with an edge of the same length and compatible parity
    M = 0
    While M < Polyshap(Shape).NumberOfNodes
      MM = (M + oldM) Mod Polyshap(Shape).NumberOfNodes
      If Math.Abs(Polyshap(Shape).ListOfVectors(MM).Magnitude - d) < 1 Then
        If Polyshap(Shape).Paritymode = 0 Or parity = 0 Then Exit While
        If Polyshap(Shape).ListOfVectors(MM).ID.Parity = Polyshap(Shape).Paritymode * parity Then Exit While
      End If
      M += 1
    End While
    If M = Polyshap(Shape).NumberOfNodes Then MsgBox("This polygon will not fit here") : Exit Sub
    oldM = MM + 1
    p.TYPE = Polyshap(Shape).Type
    p.APEX = p.NPOINTS - 1 - MM
    For N As Integer = 0 To p.NPOINTS - 1
      mag = Polyshap(Shape).ListOfVectors(MM).Magnitude
      dir = Polyshap(Shape).ListOfVectors(MM).Direction
      X += mag * Math.Cos(angle)
      Y += mag * Math.Sin(angle)
      p.POINTS(N).Point = ListOfPoints.add(New PointF(X, Y), "Search")
      p.POINTS(N).Parity = Polyshap(Shape).ListOfVectors(MM).ID.Parity
      p.POINTS(N).Radius = Polyshap(Shape).ListOfVectors(MM).ID.Radius
      p.POINTS(N).Colour = Polyshap(Shape).ListOfVectors(MM).ID.Colour
      angle += Math.PI * dir / 180
      MM = (MM + 1) Mod p.NPOINTS
    Next
    p.PARITYMODE = Polyshap(Shape).Paritymode
    NormaliseList(p)
    PolygonList.Add(p)
  End Sub

  Sub RedrawBitmap()
    'Dim font As New Font("Ariel", 10, FontStyle.Regular)
    'Dim brush As New SolidBrush(Color.Black)
    myBitmapGraphics.resettransform()
    myBitmapGraphics.fillrectangle(Brushes.White, 0, 0, 800, 600)
    myBitmapGraphics.TranslateTransform(Origin.X, Origin.Y)
    myBitmapGraphics.ScaleTransform(ScaleFactor, ScaleFactor)
    For Each p As Polygon In PolygonList
      p.DRAW(myBitmapGraphics, ListOfPoints)
    Next
    Dim pp As New PointF
    For p As Integer = 0 To ListOfPoints.maxpoint - 1
      'pp = ListOfPoints.GetPoint(p)
      'myBitmapGraphics.drawstring(Str(p), font, brush, pp.X, pp.Y)
      If ListOfPoints.pointsselectedflag(p) Then
        pp = ListOfPoints.GetPoint(p)
        myBitmapGraphics.fillellipse(New SolidBrush(Color.Yellow), pp.X - 6, pp.Y - 6, 12, 12)
        myBitmapGraphics.drawellipse(Pens.Black, pp.X - 6, pp.Y - 6, 12, 12)
      End If
    Next
    PictureBox1.Invalidate()
  End Sub

  Sub NormaliseList(ByRef p As Polygon)
    Dim smallest As Integer = 100000
    Dim start As Integer
    Dim POINTS(p.NPOINTS) As Element
    'Copy the indexes to the vertices into the array POINTS()
    'At the same time, find the smallest index - start
    For i As Integer = 0 To p.NPOINTS - 1
      If p.POINTS(i).Point < smallest Then smallest = p.POINTS(i).Point : start = i
      POINTS(i) = p.POINTS(i)
    Next
    If start = 0 Then Exit Sub
    'Copy the array of indices back into p.POINTS so that they are in the same
    'cyclic order but the lowest one is first
    'This simply ensures that every polygon has a unique list of vertices
    For i As Integer = 0 To p.NPOINTS - 1
      p.POINTS(i) = POINTS((start + i) Mod p.NPOINTS)
    Next
    p.APEX = (p.APEX + p.NPOINTS - start) Mod p.NPOINTS
  End Sub

  Function identify(ByRef q As Polygon) As Boolean
    For Each p As Polygon In PolygonList
      If p.equalls(q) Then q = p : Return True
    Next
    Return False
  End Function

  Private Sub resetflags()
    ListOfPoints.ResetPointslistflags()
    For Each p As Polygon In PolygonList
      ListOfPoints.SetPointslistflags(p)
    Next
    ListOfPoints.ResetMaxpoint()
  End Sub

  Private Sub TrackBar1_Scroll(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TrackBar1.Scroll
    ScaleFactor = TrackBar1.Value / 20
    Label1.Text = "Scale factor =" & Str(100 * ScaleFactor) & "%"
    RedrawBitmap()
  End Sub

  Private Function findPolygon(ByVal pt As PointF, ByRef p As Polygon)
    For Each p In PolygonList
      If Inside(pt, p) Then Return True
    Next
    Return False
  End Function

  Private Function FindEdge(ByVal p1, ByVal p2) As Integer
    Dim j As Integer
    For Each p As Polygon In PolygonList
      For i As Integer = 0 To p.NPOINTS - 1
        j = (i + p.NPOINTS - 1) Mod p.NPOINTS
        If p.POINTS(i).Point = p1 And p.POINTS(j).Point = p2 Then
          Return p.POINTS(i).Parity
        End If
      Next
    Next
    Return 99
  End Function

  Private Function Inside(ByVal pt As PointF, ByVal p As Polygon)
    Dim pt1, pt2 As PointF
    Dim m As Integer
    For n As Integer = 0 To p.NPOINTS - 1
      m = (n + 1) Mod p.NPOINTS
      pt1 = ListOfPoints.GetPoint(p.POINTS(n).Point)
      pt2 = ListOfPoints.GetPoint(p.POINTS(m).Point)
      If ListOfPoints.threepointalgorithm(pt, pt1, pt2) <> "Anticlockwise" Then Return False
    Next
    Return True
  End Function

  Private Sub ColourArrow_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _
  Arrow1.Click, Arrow2.Click, Arrow3.Click, Arrow4.Click, Arrow5.Click, Arrow6.Click, Arrow7.Click, Arrow8.Click, Arrow9.Click, Arrow10.Click, _
  Arrow11.Click, Arrow12.Click, Arrow13.Click, Arrow14.Click, Arrow15.Click, Arrow16.Click, Arrow17.Click, Arrow18.Click, Arrow50.Click, Arrow51.Click, Arrow52.Click
    If ColorDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
      Dim id As String = Mid(sender.tag, 2)
      If Val(id) < 50 Then
        Colours(Val(id)) = ColorDialog1.Color
        Colour = ColorDialog1.Color
        ColourSquare.BackColor = Colour
        For Each b As Control In Me.Panel2.Controls
          If b.Tag = "B" & id Then b.BackColor = Colours(Val(id))
        Next
      Else
        LineColours(Val(id) - 50) = ColorDialog1.Color
        For Each b As Control In Me.Panel2.Controls
          If b.Tag = "B" & id Then b.BackColor = LineColours(Val(id) - 50)
        Next
        RedrawBitmap()
      End If
    End If
  End Sub

  Private Sub ColourArrow_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ColourArrow.Click
    If ColorDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
      Colour = ColorDialog1.Color
      ColourSquare.BackColor = Colour
    End If
  End Sub

  Private Sub RadioButton_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _
RadioButton0.CheckedChanged, _
RadioButton1.CheckedChanged, RadioButton2.CheckedChanged, RadioButton3.CheckedChanged, RadioButton4.CheckedChanged, _
RadioButton5.CheckedChanged, RadioButton6.CheckedChanged, RadioButton7.CheckedChanged, RadioButton8.CheckedChanged, _
RadioButton9.CheckedChanged, RadioButton10.CheckedChanged, RadioButton11.CheckedChanged, RadioButton12.CheckedChanged, _
RadioButton13.CheckedChanged, RadioButton14.CheckedChanged, RadioButton15.CheckedChanged, RadioButton16.CheckedChanged, _
RadioButton17.CheckedChanged, RadioButton18.CheckedChanged
    Shape = Val(Mid(sender.tag, 2))
    Colour = Colours(Shape)
    If Shape = 0 Then
      ColourSquare.BackColor = Color.White
      PictureBox1.Cursor = Cursors.Hand
      Label2.Text = "SHIFT-drag to select points. LEFT-drag to move all points. RIGHT-drag to move selected points."
    Else
      ColourSquare.BackColor = Colour
      PictureBox1.Cursor = Cursors.Default
      Label2.Text = "Cick (or CTRL-click) to enter tile. RIGHT-click inside tile to change colour. RIGHT-double-click tile to delete."
      For p As Integer = 0 To ListOfPoints.maxpoint - 1
        ListOfPoints.pointsselectedflag(p) = False
      Next
      RedrawBitmap()
    End If
  End Sub

  Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles _
  Button1.Click, Button2.Click, Button3.Click, Button4.Click, Button5.Click, Button6.Click, Button8.Click, Button9.Click, Button10.Click, _
  Button11.Click, Button12.Click, Button13.Click, Button14.Click, Button15.Click, Button16.Click, Button17.Click, Button18.Click, _
  Button0.Click
    Dim id As String = Mid(sender.tag, 2)
    If Val(id) < 50 Then
      For Each b As RadioButton In Me.Panel1.Controls
        If b.Tag = "A" & id Then b.PerformClick()
      Next
    Else
      For Each b As CheckBox In Me.Panel3.Controls
        If b.Tag = "A" & id Then b.Checked = Not b.Checked
      Next
    End If
  End Sub

  Private Sub CheckBox_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CheckBox50.CheckedChanged, CheckBox51.CheckedChanged, CheckBox52.CheckedChanged
    RedrawBitmap()
  End Sub

  Private Sub Load_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LoadToolStripMenuItem1.Click
    If modified Then
      If MsgBox("This tesselation has been modified. Do you want to overwrite it?", MsgBoxStyle.OkCancel, "Warning") = MsgBoxResult.Cancel Then Exit Sub
    End If
    OpenFileDialog1.FileName = filename
    OpenFileDialog1.Filter = ("Text files (*.txt)|*.txt")
    If OpenFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
      filename = OpenFileDialog1.FileName
      Load_File()
    Else
      Exit Sub
    End If
  End Sub

  Private Sub Load_File()
    Dim filestream As New FileStream(filename, FileMode.Open, FileAccess.Read)
    Dim streamreader As New StreamReader(filestream)
    Dim text, field As String
    Try
      text = streamreader.ReadLine
      If Microsoft.VisualBasic.Left(text, 20) <> "Easy Tiler data file" Then MsgBox("This is not an Easy Tiler file.") : Exit Sub
      VersionNumber = Val(Mid(text, 29))
      While Not streamreader.EndOfStream
        text = streamreader.ReadLine
        field = GetField(text)
        Select Case field
          Case "Scale Factor:"
            ScaleFactor = Val(StripField(text))
          Case "X Origin:"
            Origin.X = Val(StripField(text))
          Case "Y Origin:"
            Origin.Y = Val(StripField(text))
          Case "Number of lines of text:"
            RichTextBox1.Text = ""
            Dim n As Integer
            n = Val(StripField(text))
            For i As Integer = 1 To n
              RichTextBox1.Text += streamreader.ReadLine
              If i < n Then RichTextBox1.Text += Chr(13)
            Next
          Case "Spot colour:"
            LineColours(0) = GetColour(StripField(text))
          Case "Line colour 1:"
            LineColours(1) = GetColour(StripField(text))
          Case "Line colour 2:"
            LineColours(2) = GetColour(StripField(text))
          Case "Number of points:"
            Dim pt As PointF
            ListOfPoints.maxpoint = 0
            Dim n As Integer
            n = Val(StripField(text))
            For i As Integer = 1 To n
              text = StripField(streamreader.ReadLine)
              pt.X = Val(text)
              text = Mid(text, InStr(text, ",") + 1)
              pt.Y = Val(text)
              ListOfPoints.add(pt, "Don't search")
            Next
          Case "Number of polygons:"
            PolygonList.Clear()
            Dim n As Integer
            n = Val(StripField(text))
            For i As Integer = 1 To n
              'We need to dim a new polygon otherwise we add the same one every time!
              Dim p As New Polygon
              text = StripField(streamreader.ReadLine)
              p.FILLCOLOUR = GetColour(text)
              text = StripField(text)
              p.NPOINTS = Val(text)
              ReDim p.POINTS(p.NPOINTS - 1)
              text = StripField(text)
              For j As Integer = 0 To p.NPOINTS - 1
                p.POINTS(j).Point = Val(text)
                text = Mid(text, InStr(text, ",") + 1)
                p.POINTS(j).Parity = Val(text)
                text = Mid(text, InStr(text, ",") + 1)
                p.POINTS(j).Radius = Val(text)
                text = Mid(text, InStr(text, ",") + 1)
                p.POINTS(j).Colour = Val(text)
                text = Mid(text, InStr(text, ",") + 1)
              Next
              PolygonList.Add(p)
            Next
          Case Else
            MsgBox("Currupt data file") : Exit Sub
        End Select
      End While
      resetflags()
      streamreader.Close()
      For Each b As Button In Me.Panel2.Controls
        If b.Tag = "B51" Then b.BackColor = LineColours(1)
        If b.Tag = "B52" Then b.BackColor = LineColours(2)
      Next
      RedrawBitmap()
      modify(False)
      TrackBar1.Value = 20 * ScaleFactor
    Catch ex As IOException
      MsgBox(ex.Message)
    End Try
  End Sub

  Private Function GetColour(ByVal text) As Color
    Dim r, g, b As Integer
    r = Val(Mid(text, InStr(text, "R") + 1))
    g = Val(Mid(text, InStr(text, "G") + 1))
    b = Val(Mid(text, InStr(text, "B") + 1))
    Return Color.FromArgb(r, g, b)
  End Function

  Private Function GetField(ByVal text)
    Return Microsoft.VisualBasic.Left(text, InStr(text, ":"))
  End Function

  Private Function StripField(ByVal text)
    If InStr(text, ":") > 0 Then Return Mid(text, InStr(text, ":") + 1)
    If InStr(text, ";") > 0 Then Return Mid(text, InStr(text, ";") + 1)
    Return text
  End Function

  Private Sub SaveData_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveToolStripMenuItem1.Click
    SaveFileDialog1.AddExtension = True
    SaveFileDialog1.FileName = filename
    SaveFileDialog1.Filter = ("Text files (*.txt)|*.txt")
    SaveFileDialog1.DefaultExt = ".txt"
    If SaveFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
      filename = IO.Path.GetFileNameWithoutExtension(SaveFileDialog1.FileName)
    Else
      Exit Sub
    End If
    Dim filestream As New FileStream(SaveFileDialog1.FileName, FileMode.Create, FileAccess.Write)
    Dim streamwriter As New StreamWriter(filestream)
    Try
      streamwriter.WriteLine("Easy Tiler data file: Version 1.00")
      streamwriter.WriteLine("Scale Factor:" & Str(ScaleFactor))
      streamwriter.WriteLine("X Origin:" & Str(Origin.X))
      streamwriter.WriteLine("Y Origin:" & Str(Origin.Y))
      streamwriter.WriteLine("Number of lines of text:" & Str(RichTextBox1.Lines.Length))
      For i As Integer = 0 To RichTextBox1.Lines.Length - 1
        streamwriter.WriteLine(RichTextBox1.Lines(i))
      Next
      streamwriter.WriteLine("Spot colour: R" & Str(LineColours(0).R) & " G" & Str(LineColours(0).G) & " B" & Str(LineColours(0).B))
      streamwriter.WriteLine("Line colour 1: R" & Str(LineColours(1).R) & " G" & Str(LineColours(1).G) & " B" & Str(LineColours(1).B))
      streamwriter.WriteLine("Line colour 2: R" & Str(LineColours(2).R) & " G" & Str(LineColours(2).G) & " B" & Str(LineColours(2).B))
      streamwriter.WriteLine("Number of points:" & Str(ListOfPoints.maxpoint))
      For i As Integer = 0 To ListOfPoints.maxpoint - 1
        streamwriter.Write(Str(i + 1) & ":")
        streamwriter.Write(Str(ListOfPoints.pointslist(i).X) & ",")
        streamwriter.WriteLine(Str(ListOfPoints.pointslist(i).Y))
      Next
      streamwriter.WriteLine("Number of polygons:" & Str(PolygonList.Count))
      Dim k As Integer = 1
      For Each p As Polygon In PolygonList
        streamwriter.Write(Str(k) & ":") : k += 1
        streamwriter.Write(" R" & Str(p.FILLCOLOUR.R) & " G" & Str(p.FILLCOLOUR.G) & " B" & Str(p.FILLCOLOUR.B) & ";")
        streamwriter.Write(Str(p.NPOINTS) & ";")
        For i As Integer = 0 To p.NPOINTS - 1
          streamwriter.Write(Str(p.POINTS(i).Point) & ",")
          streamwriter.Write(Str(p.POINTS(i).Parity) & ",")
          streamwriter.Write(Str(p.POINTS(i).Radius) & ",")
          If i = p.NPOINTS - 1 Then
            streamwriter.WriteLine(Str(p.POINTS(i).Colour))
          Else
            streamwriter.Write(Str(p.POINTS(i).Colour) & ",")
          End If
        Next
      Next
      streamwriter.Close()
      modify(False)
    Catch ex As IOException
      MsgBox(ex.Message)
    End Try
  End Sub

  Private Sub SaveImage_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripMenuItem2.Click
    SaveFileDialog1.AddExtension = True
    SaveFileDialog1.FileName = filename
    SaveFileDialog1.Filter = ("Image files (*.png)|*.png")
    SaveFileDialog1.DefaultExt = ".png"
    If SaveFileDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
      filename = IO.Path.GetFileNameWithoutExtension(SaveFileDialog1.FileName)
    Else
      Exit Sub
    End If
    Try
      Dim tl, br As PointF
      tl = ListOfPoints.GetTopLeft
      br = ListOfPoints.GetBotRight
      Dim w As Integer = (br.X - tl.X + 20) * ScaleFactor
      Dim h As Integer = (br.Y - tl.Y + 20) * ScaleFactor
      Dim rs As New System.Drawing.Rectangle((tl.X - 10) * ScaleFactor + Origin.X, (tl.Y - 10) * ScaleFactor + Origin.Y, w, h)
      Dim rd As New System.Drawing.Rectangle(0, 0, w, h)
      Dim myBitmapCopy As New System.Drawing.Bitmap(w, h)
      Dim grBitmap As Graphics = Graphics.FromImage(myBitmapCopy)
      grBitmap.DrawImage(myBitmap, rd, rs, GraphicsUnit.Pixel)
      myBitmapCopy.Save(SaveFileDialog1.FileName, ImageFormat.Png)
    Catch ex As Exception
      MsgBox(ex.Message)
    End Try
  End Sub

  Private Sub Exit_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ExitToolStripMenuItem.Click
    End
  End Sub

  Private Sub Button12_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Delete1.Click
    If PolygonList.Count > 0 Then
      PolygonList.RemoveAt(PolygonList.Count - 1)
      resetflags()
      RedrawBitmap()
    End If
    If PolygonList.Count = 0 Then modify(False)
  End Sub

  Private Sub Button13_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Delete2.Click
    If MsgBox("Are you sure you want to delete this tiling?", MsgBoxStyle.OkCancel, "Warning") = MsgBoxResult.Ok Then
      ListOfPoints.maxpoint = 0
      PolygonList.Clear()
      RedrawBitmap()
      modify(False)
    End If
  End Sub

  Private Sub HelpToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles HelpToolStripMenuItem.Click
    Help.ShowHelp(Me, System.Environment.CurrentDirectory.ToString & "/Easy Tiler Help.chm")
  End Sub

  Private Sub Rotate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rotate.Click
    If PolygonList.Count > 0 Then
      PolygonList.RemoveAt(PolygonList.Count - 1)
      resetflags()
      AddNewPolygon(MousePos)
      RedrawBitmap()
    End If
  End Sub

  Private Sub SaveDataTo_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ToolStripMenuItem1.Click
    Dialog1.Show()
  End Sub

  Private Sub Rotate30ToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rotate30ToolStripMenuItem.Click
    ListOfPoints.rotate(30)
    RedrawBitmap()
  End Sub

  Private Sub Rotate45ToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rotate45ToolStripMenuItem.Click
    ListOfPoints.rotate(36)
    RedrawBitmap()
  End Sub

  Private Sub Rotate45ToolStripMenuItem1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rotate45ToolStripMenuItem1.Click
    ListOfPoints.rotate(45)
    RedrawBitmap()
  End Sub

  Private Sub Rotate180ToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Rotate180ToolStripMenuItem.Click
    ListOfPoints.rotate(180)
    RedrawBitmap()
  End Sub

  Private Sub RestoreDefaultToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles RestoreDefaultToolStripMenuItem.Click
    RestoreDefaultColours()
  End Sub

  Private Sub InflateToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles InflateToolStripMenuItem.Click
    Dim p As Polygon
    For i As Integer = PolygonList.Count - 1 To 0 Step -1
      p = PolygonList(i)
      If Microsoft.VisualBasic.Left(p.TYPE, 12) = "Penrose Kite" Then InflateKite(p)
      If Microsoft.VisualBasic.Left(p.TYPE, 12) = "Penrose Dart" Then InflateDart(p)
    Next
    ListOfPoints.Scale(1 / GR)
    RedrawBitmap()
  End Sub

  Private Sub InflateKite(ByVal p As Polygon)
    Dim pa, pb, pc, pd, pe, pf, pg, ph As Integer
    Dim a, b, c, d, e, f, g, h As PointF
    Dim cos As Double = Math.Cos(Rad(-72))
    Dim sin As Double = Math.Sin(Rad(-72))
    pa = p.POINTS(p.APEX).Point
    pb = p.POINTS((p.APEX + 1) Mod 4).Point
    pc = p.POINTS((p.APEX + 2) Mod 4).Point
    pd = p.POINTS((p.APEX + 3) Mod 4).Point
    a = ListOfPoints.GetPoint(pa)
    b = ListOfPoints.GetPoint(pb)
    c = ListOfPoints.GetPoint(pc)
    d = ListOfPoints.GetPoint(pd)
    e.X = a.X + GR * (c.X - a.X)
    e.Y = a.Y + GR * (c.Y - a.Y)
    pe = ListOfPoints.add(e, "Search")
    f.X = b.X + GR * (a.X - b.X)
    f.Y = b.Y + GR * (a.Y - b.Y)
    pf = ListOfPoints.add(f, "Search")
    g.X = d.X + GR * (a.X - d.X)
    g.Y = d.Y + GR * (a.Y - d.Y)
    pg = ListOfPoints.add(g, "Search")
    h.X = a.X + cos * (e.X - a.X) - sin * (e.Y - a.Y)
    h.Y = a.Y + sin * (e.X - a.X) + cos * (e.Y - a.Y)
    ph = ListOfPoints.add(h, "Search")
    AddPoly(13, pd, pg, pe, pc)
    AddPoly(13, pb, pc, pe, pf)
    AddPoly(14, pa, ph, pf, pe)
    PolygonList.Remove(p)
  End Sub

  Private Sub InflateDart(ByVal p As Polygon)
    Dim pa, pb, pc, pd, pe, pf, pg As Integer
    Dim a, b, c, d, e, f, g As PointF
    Dim cos As Double = Math.Cos(Rad(-72))
    Dim sin As Double = Math.Sin(Rad(-72))
    pa = p.POINTS(p.APEX).Point
    pb = p.POINTS((p.APEX + 1) Mod 4).Point
    pc = p.POINTS((p.APEX + 2) Mod 4).Point
    pd = p.POINTS((p.APEX + 3) Mod 4).Point
    a = ListOfPoints.GetPoint(pa)
    b = ListOfPoints.GetPoint(pb)
    c = ListOfPoints.GetPoint(pc)
    d = ListOfPoints.GetPoint(pd)
    e.X = d.X + (c.X - d.X) / GR
    e.Y = d.Y + (c.Y - d.Y) / GR
    pe = ListOfPoints.add(e, "Search")
    f.X = b.X + (c.X - b.X) / GR
    f.Y = b.Y + (c.Y - b.Y) / GR
    pf = ListOfPoints.add(f, "Search")
    g.X = d.X + cos * (c.X - d.X) - sin * (c.Y - d.Y)
    g.Y = d.Y + sin * (c.X - d.X) + cos * (c.Y - d.Y)
    pg = ListOfPoints.add(g, "Search")
    AddPoly(13, pa, pe, pc, pf)
    AddPoly(14, pd, pg, pf, pc)
    PolygonList.Remove(p)
  End Sub

  Private Sub AddPoly(ByVal Shape, ByVal a, ByVal b, ByVal c, ByVal d)
    Dim p As New Polygon
    ReDim p.POINTS(3)
    Dim e As New Element
    p.TYPE = Polyshap(Shape).Type
    p.APEX = 0
    p.NPOINTS = 4
    p.FILLCOLOUR = Colours(Shape)
    p.POINTS(0).Point = a
    p.POINTS(1).Point = b
    p.POINTS(2).Point = c
    p.POINTS(3).Point = d
    For i As Integer = 0 To 3
      p.POINTS(i).Parity = Polyshap(Shape).ListOfVectors(i).ID.Parity
      p.POINTS(i).Radius = Polyshap(Shape).ListOfVectors(i).ID.Radius
      p.POINTS(i).Colour = Polyshap(Shape).ListOfVectors(i).ID.Colour
    Next
    p.PARITYMODE = Polyshap(Shape).Paritymode
    NormaliseList(p)
    PolygonList.Add(p)
  End Sub

  Private Sub NumericUpDown1_ValueChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles NumericUpDown1.ValueChanged
    ConstructShape(7, "Custom", NumericUpDown1.Value)
  End Sub
End Class
