Shapes.cs
// 完毕:
using System;
using System.IO;
using System.Drawing;
using System.Numerics;
using System.Linq;
using GrapeCity.Documents.Pdf;
using GrapeCity.Documents.Text;
using GrapeCity.Documents.Drawing;
using GCDRAW = GrapeCity.Documents.Drawing;
namespace DsPdfWeb.Demos
{
// 演示如何在 GcDocs.Pdf 中绘制各种形状。
// 展示如何组合简单的形状以产生更复杂的形状。
// 简单的图形变换用于绘制一些形状。
public class Shapes
{
// 用于绘制多边形及其下方的标题的辅助方法。
// 也可用于仅计算点而无需实际绘图。
// startAngle 是第一个点,从 (1,0) 开始顺时针旋转。
private PointF[] DrawPolygon(GcGraphics g, PointF center, float r, int n, float startAngle, GCDRAW.Pen pen, string caption = null)
{
var pts = new PointF[n];
for (int i = 0; i < n; ++i)
pts[i] = new PointF(center.X + (float)(r * Math.Cos(startAngle + 2 * Math.PI * i / n)), center.Y + (float)(r * Math.Sin(startAngle + 2 * Math.PI * i / n)));
if (pen != null)
g.DrawPolygon(pts, pen);
if (!string.IsNullOrEmpty(caption))
DrawCaption(g, center, r, caption);
return pts;
}
// 在形状下方绘制标题的辅助方法:
private static void DrawCaption(GcGraphics g, PointF center, float r, string caption)
{
g.DrawString(caption,
new TextFormat() { Font = StandardFonts.Times, FontSize = 10, },
new RectangleF(center.X - r, center.Y + r, r * 2, 24),
TextAlignment.Center, ParagraphAlignment.Center, false);
}
// 主要入口点。
public int CreatePDF(Stream stream)
{
var doc = new GcPdfDocument();
var page = doc.Pages.Add();
var g = page.Graphics;
// 文档标题:
g.DrawString("Shapes",
new TextFormat() { Font = StandardFonts.TimesBold, FontSize = 14, Underline = true, },
new RectangleF(PointF.Empty, new SizeF(page.Size.Width, 44)),
TextAlignment.Center, ParagraphAlignment.Far);
// 用于绘制形状的笔:
var pen = new GCDRAW.Pen(Color.Orange, 1);
pen.LineJoin = PenLineJoin.Round;
int fill = 100; // Surfaces fill alpha
// 设置辅助布局网格:
var grid = new
{
Cols = 3,
Rows = 5,
MarginX = 72,
MarginY = 36,
Radius = 36,
StepX = (page.Size.Width - 144) / 3,
StepY = (page.Size.Height - 72) / 5,
};
// 下一个图形中心的插入点:
var startIp = new PointF(grid.MarginX + grid.StepX / 2, grid.MarginY + grid.StepY / 2 + 10);
var ip = startIp;
// 调试代码以显示布局网格:
/*
var ipp = ip;
for (int i = 0; i < grid.Cols; ++i)
{
ipp.Y = ip.Y;
for (int j = 0; j < grid.Rows; ++j)
{
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;
}
ipp.X += grid.StepX;
}
*/
// 圆圈:
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;
// 圆柱:
float radX = grid.Radius / 1.4f;
float radY = grid.Radius / 6;
float 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, (float)-Math.PI / 4, pen, "Square");
ip.X += grid.StepX;
// 矩形:
float rectQx = 1.4f;
float rectQy = 0.6f;
var 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;
// 立方体:
float cubex = 6;
var cubePtsFar = DrawPolygon(g, new PointF(ip.X - cubex, ip.Y - cubex), grid.Radius, 4, (float)-Math.PI / 4, pen);
var cubePtsNear = DrawPolygon(g, new PointF(ip.X + cubex, ip.Y + cubex), grid.Radius, 4, (float)-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, (float)-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, null);
g.Transform = Matrix3x2.Identity;
DrawCaption(g, ip, grid.Radius, "Hexagon");
ip.X += grid.StepX;
// 八边形:
DrawPolygon(g, ip, grid.Radius, 8, (float)-Math.PI / 8, pen, "Octagon");
ip.X = startIp.X;
ip.Y += grid.StepY;
pen.Color = Color.DarkRed;
// 三角形:
DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, pen, "Triangle");
ip.X += grid.StepX;
// 填充五角星:
var pts = DrawPolygon(g, ip, grid.Radius, 5, (float)-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;
// 设置一种简单的倾斜投影来绘制金字塔:
var angle = Math.PI / 6;
float s = (float)Math.Sin(angle);
float c = (float)Math.Cos(angle);
Func<float, float, float, PointF> project = (x_, y_, z_) => new PointF(x_ - c * y_ * 0.5f, -(z_ - s * y_ * 0.5f));
Func<Vector3, PointF> p3d = v_ => project(v_.X, v_.Y, v_.Z);
float hedge = grid.Radius; // 1/2 edge
// 调试 - 绘制 3 轴:
/*
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 点:
var pts3d = new 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(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;
// 锥体:
float baseh = grid.Radius * 0.3f;
pts = DrawPolygon(g, ip, grid.Radius, 3, (float)-Math.PI / 2, null, "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((float)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 来获取正方形的点):
float dx = 10;
pts = DrawPolygon(g, ip, grid.Radius, 4, (float)-Math.PI / 4, null, "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;
}
}
}