Shapes.vb
''
'' This code is part of GrapeCity Documents for Imaging samples.
'' Copyright (c) GrapeCity, Inc. All rights reserved.
''
Imports System.IO
Imports System.Drawing
Imports System.Numerics
Imports GrapeCity.Documents.Drawing
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Imaging

'' Demonstrates how various shapes can be drawn in GcImaging.
'' Shows how simple shapes can be combined to produce more complex shapes.
'' Simple graphics transformations are used to draw some shapes.
Public Class Shapes
    '' Helper method to draw a polygon And a caption beneath it.
    '' Can also be used to just calculate the points without actual drawing.
    '' startAngle Is for the first point, clockwise from (1,0).
    Private Function DrawPolygon(
                                ByVal g As GcGraphics,
                                ByVal center As PointF,
                                ByVal r As Single,
                                ByVal n As Integer,
                                ByVal startAngle As Single,
                                ByVal pen As Pen,
                                Optional ByVal caption As String = Nothing) As PointF()
        Dim pts(n - 1) As PointF
        For i = 0 To n - 1
            pts(i) = New PointF(center.X + (r * Math.Cos(startAngle + 2 * Math.PI * i / n)), center.Y + (r * Math.Sin(startAngle + 2 * Math.PI * i / n)))
        Next

        If pen IsNot Nothing Then
            g.DrawPolygon(pts, pen)
        End If
        If Not String.IsNullOrEmpty(caption) Then
            DrawCaption(g, center, r, caption)
        End If
        Return pts
    End Function

    '' Helper method to draw a caption beneath a shape
    Private Sub DrawCaption(ByVal g As GcGraphics, ByVal center As PointF, ByVal r As Single, ByVal caption As String)
        g.DrawString(
            caption,
            New TextFormat() With {.Font = Font.FromFile(Path.Combine("Resources", "Fonts", "times.ttf")), .FontSize = 10},
            New RectangleF(center.X - r, center.Y + r, r * 2, 24),
            TextAlignment.Center, ParagraphAlignment.Center, False)
    End Sub

    '' Main entry point.
    Function GenerateImage(
            Optional pixelWidth As Integer = 1024,
            Optional pixelHeight As Integer = 1024,
            Optional opaque As Boolean = True,
            Optional dpiX As Single = 96,
            Optional dpiY As Single = 96) As GcBitmap
        Dim Inch = dpiX
        Dim bmp = New GcBitmap(pixelWidth, pixelHeight, True, dpiX, dpiY)
        Using g = bmp.CreateGraphics(Color.White)
            '' Document header:
            g.DrawString("Shapes",
                    New TextFormat() With {.Font = Font.FromFile(Path.Combine("Resources", "Fonts", "timesbd.ttf")), .FontSize = 14, .Underline = True},
                    New RectangleF(PointF.Empty, New SizeF(bmp.Width, 44)),
                    TextAlignment.Center, ParagraphAlignment.Far)
            '' Pen used to draw shapes:
            Dim pen = New Pen(Color.Orange, 1)
            pen.LineJoin = PenLineJoin.Round
            Dim fill = 100 '' Surfaces fill alpha

            '' Set up the helper layout grid:
            Dim grid = New With {
                    .Cols = 3,
                    .Rows = 5,
                    .MarginX = Inch / 4,
                    .MarginY = Inch / 3,
                    .Radius = Inch * 0.7F,
                    .StepX = (bmp.Width - Inch / 2) / 3,
                    .StepY = (bmp.Height - Inch / 4) / 5.5F
                }

            '' Insertion point of the next figure's center:
            Dim startIp = New PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10)
            Dim ip = startIp
#If False Then
            '' Debug code to show the layout grid:
            Dim ipp = ip
            For i = 1 To grid.Cols
                ipp.Y = ip.Y
                For j = 1 To grid.Rows
                    g.DrawRectangle(New RectangleF(ipp.X - grid.Radius, ipp.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2), Color.LightGreen, 0.5F)
                    ipp.Y += grid.StepY
                Next
                ipp.X += grid.StepX
            Next
#End If
            '' Circle:
            g.DrawEllipse(New RectangleF(ip.X - grid.Radius, ip.Y - grid.Radius, grid.Radius * 2, grid.Radius * 2), pen)
            DrawCaption(g, ip, grid.Radius, "Circle")
            ip.X += grid.StepX

            '' Ellipse:
            g.DrawEllipse(New RectangleF(ip.X - grid.Radius * 1.4F, ip.Y - grid.Radius / 2, grid.Radius * 2 * 1.4F, grid.Radius), pen)
            DrawCaption(g, ip, grid.Radius, "Ellipse")
            ip.X += grid.StepX

            '' Cylinder:
            Dim radX = grid.Radius / 1.4F
            Dim radY = grid.Radius / 6
            Dim height = grid.Radius * 1.8F
            g.DrawEllipse(New RectangleF(ip.X - radX, ip.Y - height / 2, radX * 2, radY * 2), pen)
            g.FillEllipse(New RectangleF(ip.X - radX, ip.Y + height / 2 - radY * 2, radX * 2, radY * 2), Color.FromArgb(fill, pen.Color))
            g.DrawEllipse(New RectangleF(ip.X - radX, ip.Y + height / 2 - radY * 2, radX * 2, radY * 2), pen)
            g.DrawLine(New PointF(ip.X - radX, ip.Y - height / 2 + radY), New PointF(ip.X - radX, ip.Y + height / 2 - radY), pen)
            g.DrawLine(New PointF(ip.X + radX, ip.Y - height / 2 + radY), New PointF(ip.X + radX, ip.Y + height / 2 - radY), pen)
            DrawCaption(g, ip, grid.Radius, "Cylinder")
            ip.X = startIp.X
            ip.Y += grid.StepY
            pen.Color = Color.Indigo

            '' Square:
            DrawPolygon(g, ip, grid.Radius, 4, -Math.PI / 4, pen, "Square")
            ip.X += grid.StepX

            '' Rectangle:
            Dim rectQx = 1.4F
            Dim rectQy = 0.6F
            Dim rect = New RectangleF(ip.X - grid.Radius * rectQx, ip.Y - grid.Radius * rectQy, grid.Radius * 2 * rectQx, grid.Radius * 2 * rectQy)
            g.DrawRectangle(rect, pen)
            DrawCaption(g, ip, grid.Radius, "Rectangle")
            ip.X += grid.StepX

            '' Cube:
            Dim cubex = 6
            Dim cubePtsFar = DrawPolygon(g, New PointF(ip.X - cubex, ip.Y - cubex), grid.Radius, 4, -Math.PI / 4, pen)
            Dim cubePtsNear = DrawPolygon(g, New PointF(ip.X + cubex, ip.Y + cubex), grid.Radius, 4, -Math.PI / 4, pen)
            g.DrawLine(cubePtsFar(0), cubePtsNear(0), pen)
            g.DrawLine(cubePtsFar(1), cubePtsNear(1), pen)
            g.DrawLine(cubePtsFar(2), cubePtsNear(2), pen)
            g.DrawLine(cubePtsFar(3), cubePtsNear(3), pen)
            g.FillPolygon(New PointF() {cubePtsFar(1), cubePtsFar(2), cubePtsNear(2), cubePtsNear(1)}, Color.FromArgb(fill, pen.Color))
            DrawCaption(g, ip, grid.Radius, "Cube")
            ip.X = startIp.X
            ip.Y += grid.StepY
            pen.Color = Color.DarkGreen

            '' Pentagon:
            DrawPolygon(g, ip, grid.Radius, 5, -Math.PI / 2, pen, "Pentagon")
            ip.X += grid.StepX

            '' Hexagon:
            '' For sample sake, we apply a transform to make the hexagon wider and shorter:
            g.Transform = Matrix3x2.CreateScale(1.4F, 0.8F) * Matrix3x2.CreateTranslation(ip.X, ip.Y)
            DrawPolygon(g, PointF.Empty, grid.Radius, 6, 0, pen, Nothing)
            g.Transform = Matrix3x2.Identity
            DrawCaption(g, ip, grid.Radius, "Hexagon")
            ip.X += grid.StepX

            '' Octagon:
            DrawPolygon(g, ip, grid.Radius, 8, -Math.PI / 8, pen, "Octagon")
            ip.X = startIp.X
            ip.Y += grid.StepY
            pen.Color = Color.DarkRed

            '' Triangle:
            DrawPolygon(g, ip, grid.Radius, 3, -Math.PI / 2, pen, "Triangle")
            ip.X += grid.StepX

            '' Filled pentagram:
            Dim pts = DrawPolygon(g, ip, grid.Radius, 5, -Math.PI / 2, pen, "Pentagram")
            pts = New PointF() {pts(0), pts(2), pts(4), pts(1), pts(3)}
            g.FillPolygon(pts, Color.FromArgb(fill, pen.Color))
            g.DrawPolygon(pts, pen)
            ip.X += grid.StepX

            '' Set up a simple kind of oblique projection to draw a pyramid:
            Dim angle = Math.PI / 6
            Dim s = Math.Sin(angle)
            Dim c = Math.Cos(angle)

            Dim project As Func(Of Single, Single, Single, PointF) =
                Function(ByVal x_, ByVal y_, ByVal z_)
                    Return New PointF(x_ - c * y_ * 0.5F, -(z_ - s * y_ * 0.5F))
                End Function

            Dim p3d As Func(Of Vector3, PointF) =
                Function(ByVal v_)
                    Return project(v_.X, v_.Y, v_.Z)
                End Function

            Dim hedge = grid.Radius '' 1/2 edge
            '' Debug - draw the 3 axis:
            '' g.DrawLine(project(0, 0, 0), project(100, 0, 0), Color.Red)
            '' g.DrawLine(project(0, 0, 0), project(0, 100, 0), Color.Green)
            '' g.DrawLine(project(0, 0, 0), project(0, 0, 100), Color.Blue)
            '' 3d points forming a square pyramid:
            Dim pts3d As Vector3() =
                {
                    New Vector3(-hedge, -hedge, 0),
                    New Vector3(hedge, -hedge, 0),
                    New Vector3(hedge, hedge, 0),
                    New Vector3(-hedge, hedge, 0),
                    New Vector3(0, 0, hedge * 2)
                }
            '' project the points to draw the pyramid:
            pts = pts3d.Select(Function(v_) p3d(v_)).ToArray()
            g.Transform = Matrix3x2.CreateTranslation(ip.X, ip.Y + hedge * 0.7F)
            '' Visible edges:
            g.DrawPolygon(New PointF() {pts(4), pts(1), pts(2), pts(3), pts(4), pts(2)}, pen)
            '' Invisible edges:
            pen.Width /= 2
            pen.Color = Color.FromArgb(fill, pen.Color)
            g.DrawLine(pts(0), pts(4), pen)
            g.DrawLine(pts(0), pts(1), pen)
            g.DrawLine(pts(0), pts(3), pen)
            g.FillPolygon(pts.Take(4).ToArray(), pen.Color)
            ''
            g.Transform = Matrix3x2.Identity
            DrawCaption(g, ip, grid.Radius, "Pyramid")
            ip.X = startIp.X
            ip.Y += grid.StepY
            pen.Width *= 2
            pen.Color = Color.Green

            '' Cone:
            Dim baseh = grid.Radius * 0.3F
            pts = DrawPolygon(g, ip, grid.Radius, 3, -Math.PI / 2, Nothing, "Cone")
            g.DrawLines(New PointF() {pts(2), pts(0), pts(1)}, pen)
            rect = New RectangleF(pts(2).X, pts(2).Y - baseh / 2, pts(1).X - pts(2).X, baseh)
            g.FillEllipse(rect, Color.FromArgb(fill, pen.Color))
            g.DrawEllipse(rect, pen)
            ip.X += grid.StepX

            '' Parallelogram (use graphics.Transform on a rectangle):
            rect = New RectangleF(-grid.Radius * rectQx, -grid.Radius * rectQy, grid.Radius * 2 * rectQx, grid.Radius * 2 * rectQy)
            g.Transform = Matrix3x2.CreateSkew(Math.PI / 6, 0) * Matrix3x2.CreateTranslation(ip.X, ip.Y)
            g.DrawRectangle(rect, pen)
            g.Transform = Matrix3x2.Identity
            DrawCaption(g, ip, grid.Radius, "Parallelogram")
            ip.X += grid.StepX

            '' Trapezoid (use DrawPolygon to just get the points of the square):
            Dim dx = 10
            pts = DrawPolygon(g, ip, grid.Radius, 4, -Math.PI / 4, Nothing, "Trapezoid")
            pts(0).X -= dx
            pts(1).X += dx
            pts(2).X -= dx
            pts(3).X += dx
            g.DrawPolygon(pts, pen)

            '' Draw border around the whole image
            g.DrawRectangle(New RectangleF(0, 0, bmp.Width, bmp.Height), Color.DarkSlateBlue, 4)
        End Using
        '' Done
        Return bmp
    End Function
End Class