Shapes.vb
'' 
'' このコードは、DioDocs for PDF のサンプルの一部として提供されています。
'' Copyright (c) GrapeCity inc. All rights reserved.
'' 
Imports System.IO
Imports System.Drawing
Imports System.Numerics
Imports System.Linq
Imports GrapeCity.Documents.Pdf
Imports GrapeCity.Documents.Text
Imports GrapeCity.Documents.Drawing

'' GcPdf でさまざまな図形をどのように描くことができるかを示します。
'' シンプルなシェイプを組み合わせてより複雑な形状を生成する方法を示します。
'' 単純なグラフィックス変換を使用していくつかの図形を描画します。
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 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 {.FontName = "Yu Gothic", .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("図形",
                New TextFormat() With {.FontName = "Yu Gothic", .FontSize = 14, .Underline = True},
                New RectangleF(PointF.Empty, New SizeF(page.Size.Width, 44)),
                TextAlignment.Center, ParagraphAlignment.Far)
        '' 図形を描画するために使用するペンです。
        Dim pen = New Pen(Color.Orange, 1)
        pen.LineJoin = PenLineJoin.Round
        Dim fill = 100 '' 表面を塗りつぶすアルファ値

        '' レイアウト用グリッドを設定します。
        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, "円")
        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, "楕円")
        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, "円柱")
        ip.X = startIp.X
        ip.Y += grid.StepY
        pen.Color = Color.Indigo

        '' 正方形。
        DrawPolygon(g, ip, grid.Radius, 4, -Math.PI / 4, pen, "正方形")
        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, "長方形")
        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, "立方体")
        ip.X = startIp.X
        ip.Y += grid.StepY
        pen.Color = Color.DarkGreen

        '' 五角形。
        DrawPolygon(g, ip, grid.Radius, 5, -Math.PI / 2, pen, "五角形")
        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, "六角形")
        ip.X += grid.StepX

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

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

        '' 塗りつぶし五面体。
        Dim pts = DrawPolygon(g, ip, grid.Radius, 5, -Math.PI / 2, pen, "塗りつぶし五面体")
        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, "ピラミッド")
        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, "三角錐")
        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

        '' 平行四辺形(四角形を変形して利用します)。
        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, "平行四辺形")
        ip.X += grid.StepX

        '' 台形(DrawPolygon を使って四角形の点を取得します)。
        Dim dx = 10
        pts = DrawPolygon(g, ip, grid.Radius, 4, -Math.PI / 4, Nothing, "台形")
        pts(0).X -= dx
        pts(1).X += dx
        pts(2).X -= dx
        pts(3).X += dx
        g.DrawPolygon(pts, pen)
        ''
        '' PDF ドキュメントを保存します。
        doc.Save(stream)
        Return doc.Pages.Count
    End Function
End Class