Shapes.vb
'' 完毕:
Imports System.IO
Imports System.Drawing
Imports System.Numerics
Imports System.Linq
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Drawing
Imports GCTEXT = GrapeCity.Documents.Text
Imports GCDRAW = GrapeCity.Documents.Drawing

'' 演示如何在 GcDocs.Pdf 中绘制各种形状。
'' 展示如何组合简单的形状以产生更复杂的形状。
'' 简单的图形变换用于绘制一些形状。
Public Class Shapes

    '' 用于绘制多边形及其下方的标题的辅助方法。
    '' 也可用于仅计算点而无需实际绘图。
    '' startAngle 是第一个点,从 (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 pn As GCDRAW.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 pn IsNot Nothing Then
            g.DrawPolygon(pts, pn)
        End If
        If Not String.IsNullOrEmpty(caption) Then
            DrawCaption(g, center, r, caption)
        End If
        Return pts
    End Function

    '' 在形状下方绘制标题的辅助方法:
    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 = StandardFonts.Times, .FontSize = 10},
            New RectangleF(center.X - r, center.Y + r, r * 2, 24),
            TextAlignment.Center, ParagraphAlignment.Center, False)
    End Sub

    '' 主要入口点。
    Function CreatePDF(ByVal stream As Stream) As Integer
        Dim doc = New GcPdfDocument()
        Dim page = doc.Pages.Add()
        Dim g = page.Graphics
        '' 文档标题:
        g.DrawString("Shapes",
                New TextFormat() With {.Font = StandardFonts.TimesBold, .FontSize = 14, .Underline = True},
                New RectangleF(PointF.Empty, New SizeF(page.Size.Width, 44)),
                TextAlignment.Center, ParagraphAlignment.Far)
        '' 用于绘制形状的笔:
        Dim pen = New GCDRAW.Pen(Color.Orange, 1)
        pen.LineJoin = PenLineJoin.Round
        Dim fill = 100 '' Surfaces fill alpha

        '' 设置辅助布局网格:
        Dim grid = New With {
                .Cols = 3,
                .Rows = 5,
                .MarginX = 72,
                .MarginY = 36,
                .Radius = 36,
                .StepX = (page.Size.Width - 144) / 3,
                .StepY = (page.Size.Height - 72) / 5
            }

        '' 下一个图形中心的插入点:
        Dim startIp = New PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10)
        Dim ip = startIp
#If False Then
        '' 调试代码以显示布局网格:
        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
        '' 圆圈:
        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

        '' 椭圆:
        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

        '' 圆柱:
        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

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

        '' 矩形:
        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

        '' 立方体:
        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

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

        '' 六边形:
        '' 为了示例,我们应用变换来使六边形更宽更短:
        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

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

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

        '' 填充五角星:
        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

        '' 设置一种简单的倾斜投影来绘制金字塔:
        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
        '' 调试 - 绘制 3 轴:

        '' 形成四角锥体的 3d 点:
        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)
            }
        '' 投影点以绘制金字塔:
        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

        '' 锥体:
        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

        '' 平行四边形(在矩形上使用graphics.Transform):
        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

        '' 梯形(使用 DrawPolygon 来获取正方形的点):
        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)
        ''
        '' 完毕:
        doc.Save(stream)
        Return doc.Pages.Count
    End Function
End Class