SvgGraphicsShapes.vb
''
'' This code is part of Document Solutions for Imaging demos.
'' Copyright (c) MESCIUS inc. All rights reserved.
''
Imports System
Imports System.IO
Imports System.Drawing
Imports System.Collections.Generic
Imports System.Linq
Imports System.Numerics
Imports GrapeCity.Documents.Drawing
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Imaging
Imports GrapeCity.Documents.Svg
Imports GCTEXT = GrapeCity.Documents.Text
Imports GCDRAW = GrapeCity.Documents.Drawing

'' This example shows how to use GcSvgGraphics for common graphics
'' operations. It draws some geometric shapes on GcSvgGraphics,
'' then converts that graphics to GcSvgDocument, which is saved
'' to an SVG stream that is shown by this sample.
'' Note that the code drawing shapes on GcSvgGraphics is identical
'' to the code drawing shapes on GcBitmapGraphics in the Shapes sample.
'' Compare this to the SvgShapes sample which creates SVG elements
'' representing shapes, and draws them on the resulting raster GcBitmap.
Public Class SvgGraphicsShapes
    Public ReadOnly Property DefaultMime As String
        Get
            Return Util.MimeTypes.SVG
        End Get
    End Property

    '' 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(g As GcGraphics, center As PointF, r As Single, n As Integer, startAngle As Single, pen As GCDRAW.Pen, Optional caption As String = Nothing) As PointF()
        Dim pts(n - 1) As PointF
        For i = 0 To n - 1
            pts(i) = New PointF(center.X + CSng(r * Math.Cos(startAngle + 2 * Math.PI * i / n)), center.Y + CSng(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(g As GcGraphics, center As PointF, r As Single, caption As String)
        g.DrawString(caption,
            New TextFormat() With {
                .Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "FreeSerif.ttf")),
                .FontSize = 12
            },
            New RectangleF(center.X - r, center.Y + r, r * 2, 24),
            TextAlignment.Center, ParagraphAlignment.Center, False)
    End Sub

    '' Main entry point.
    Public Function GenerateImageStream(targetMime As String, pixelSize As Size, dpi As Single, opaque As Boolean, Optional sampleParams As String() = Nothing) As Stream
        If targetMime <> Util.MimeTypes.SVG Then
            Throw New Exception("This sample only supports SVG output format.")
        End If

        Dim Inch = dpi
        Dim ms = New MemoryStream()
        Using g As New GcSvgGraphics(pixelSize.Width, pixelSize.Height)
            If opaque Then
                g.FillRectangle(New RectangleF(0, 0, g.Width, g.Height), Color.White)
            End If

            '' Document header:
            g.DrawString("Shapes",
                New TextFormat() With {
                    .Font = GCTEXT.Font.FromFile(Path.Combine("Resources", "Fonts", "FreeSerifBold.ttf")),
                    .FontBold = True,
                    .FontSize = 14,
                    .Underline = True
                },
                New RectangleF(PointF.Empty, New SizeF(g.Width, 44)),
                TextAlignment.Center, ParagraphAlignment.Far)

            '' Pen used to draw shapes:
            Dim pen = New GCDRAW.Pen(Color.Orange, 1)
            pen.LineJoin = PenLineJoin.Round
            Dim fill As Integer = 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 = (g.Width - Inch / 2) / 3,
                .StepY = (g.Height - Inch / 4) / 5.5F
            }

            Dim startIp As New PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10)
            Dim ip As PointF = startIp

            '' 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, CSng(-Math.PI / 4), pen, "Square")
            ip.X += grid.StepX

            '' Rectangle:
            Dim rectQx As Single = 1.4F
            Dim rectQy As Single = 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 As Single = 6
            Dim cubePtsFar = DrawPolygon(g, New PointF(ip.X - cubex, ip.Y - cubex), grid.Radius, 4, CSng(-Math.PI / 4), pen)
            Dim cubePtsNear = DrawPolygon(g, New PointF(ip.X + cubex, ip.Y + cubex), grid.Radius, 4, CSng(-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, CSng(-Math.PI / 2), pen, "Pentagon")
            ip.X += grid.StepX

            '' Hexagon with transform:
            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, CSng(-Math.PI / 8), pen, "Octagon")
            ip.X = startIp.X
            ip.Y += grid.StepY
            pen.Color = Color.DarkRed

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

            '' Filled pentagram:
            Dim pts = DrawPolygon(g, ip, grid.Radius, 5, CSng(-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

            '' Pyramid (oblique projection):
            Dim angle = Math.PI / 6
            Dim s = CSng(Math.Sin(angle))
            Dim c = CSng(Math.Cos(angle))
            Dim project As Func(Of Single, Single, Single, PointF) = Function(x_, y_, z_) New PointF(x_ - c * y_ * 0.5F, -(z_ - s * y_ * 0.5F))
            Dim p3d As Func(Of Vector3, PointF) = Function(v_) project(v_.X, v_.Y, v_.Z)
            Dim hedge = grid.Radius
            Dim pts3d = {
                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)
            }
            pts = pts3d.Select(Function(v_) p3d(v_)).ToArray()
            g.Transform = Matrix3x2.CreateTranslation(ip.X, ip.Y + hedge * 0.7F)
            g.DrawPolygon(New PointF() {pts(4), pts(1), pts(2), pts(3), pts(4), pts(2)}, pen)
            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, CSng(-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 skew 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(CSng(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 (adjust points of a square):
            Dim dx As Single = 10
            pts = DrawPolygon(g, ip, grid.Radius, 4, CSng(-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)

            '' Done:
            Dim svg = g.ToSvgDocument()
            svg.Save(ms)
            ms.Seek(0, SeekOrigin.Begin)
            Return ms
        End Using
    End Function
End Class