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