我上个月发表的一篇文章《用长方形填充正方形》中的那些被填充的正方形是使用 C# 程序画出来的。下面就是 FillSquare.cs:

 1: using System;
 2: using System.IO;
 3: using System.Drawing;
 4: using System.Linq;
 5: 
 6: namespace Skyiv.Utils
 7: {
 8:   sealed partial class FillSquare
 9:   {
10:     enum Direction { Horizontal, Vertical };
11: 
12:     static readonly Direction H = Direction.Horizontal;
13:     static readonly Direction V = Direction.Vertical;
14:     static readonly int Color1 = 0, Color2 = 1, Color3 = 2, Color4 = 3;
15: 
16:     int size;
17:     Graphics gc;
18:     Font font;
19:     Brush[] brushs;
20: 
21:     public FillSquare(int size)
22:     {
23:       this.size = size;
24:       font = new Font("Lucida Console", 14);
25:       brushs = new Color[]{ Color.Cyan, Color.Fuchsia, Color.Yellow, Color.Lime }
26:         .Select(x => new SolidBrush(x)).ToArray();
27:     }
28: 
29:     public void Run()
30:     {
31:       var bitmap = new Bitmap(size, size);
32:       gc = Graphics.FromImage(bitmap);
33:       gc.FillRectangle(new SolidBrush(Color.Gray), 0, 0, size, size);
34:       Draw();
35:       bitmap.Save("square.png");
36:     }
37: 
38:     void Swap<T>(ref T a, ref T b) { var tmp = a; a = b; b = tmp; }
39:     double S(params int[] nums) { return nums.Sum(x => 1.0 / x); }
40: 
41:     void Rect(int k, int color, Direction dir, double x, double y)
42:     {
43:       var x0 = (float)(x * size);
44:       var y0 = (float)(y * size);
45:       var height = (float)size / k;
46:       var width = (float)size / (k + 1);
47:       if (dir == H) Swap(ref width, ref height);
48:       gc.FillRectangle(brushs[color], x0, y0, width, height);
49:       gc.DrawString(k.ToString(), font, Brushes.Black, x0, y0);
50:     }
51: 
52:     void RectsV(int k, int pairs, int color1, int color2, double x, double y)
53:     {
54:       for (; pairs-- > 0; k += 2)
55:       {
56:         Rect(k, color1, V, x, y);
57:         Rect(k + 1, color2, H, x, y + 1.0 / k);
58:         y += 1.0 / k + 1.0 / (k + 2);
59:       }
60:     }
61: 
62:     void RectsH(int k, int pairs, int color1, int color2, double x, double y)
63:     {
64:       for (; pairs-- > 0; k += 2)
65:       {
66:         Rect(k, color1, H, x, y);
67:         Rect(k + 1, color2, V, x + 1.0 / k, y);
68:         x += 1.0 / k + 1.0 / (k + 2);
69:       }
70:     }
71:     
72:     static void Main(string[] args)
73:     {
74:       new FillSquare((args.Length > 0) ? int.Parse(args[0]) : 800).Run();
75:     }
76:   }
77: }

简要说明如下:

  • 整个程序只包含一个FillSquare类,分为两个 .cs 源文件,这是第一个源文件。
  • 第 8 行的partial表明这是个部分类。
  • 第 21~27 行的构造函数作一些必要的初始化工作。
  • 第 29~36 行的Run方法是程序的主体,它生成图像文件,调用在另一个源文件中定义的Draw方法填充正方形,然后保存图像到 png 文件中。
  • 第 38 行的Swap<T>泛型方法用于交换两个参数的值。
  • 第 39 行的S方法用于对它的可变个数的参数的倒数求和。
  • 第 41~50 行的Rect方法将指定的长方形填充到正方形内部。
  • 第 52~60 行的RctsV方法将一系列的长方形在垂直方向上填充到正方形内部。
  • 第 62~70 行的RctsH方法将一系列的长方形在水平方向上填充到正方形内部。
  • 第 72~75 行的Main方法获取命令行参数,然后填充指定大小的正方形。

使用 make 工具编译所需的 Makefile 文件:

EXE1 = FillSquare.exe
SRC1 = FillSquare.cs FillSquare.Draw.cs

$(EXE1): $(SRC1)
    $(CSC) -out:$@ -r:System.Drawing.dll $(SRC1)

第一幅图

画第一幅图的第二个源文件 FillSquare.Draw.cs:

namespace Skyiv.Utils
{
  sealed partial class FillSquare
  {
    void Draw()
    {
      Rect(1, Color1, V, 0.25, 0);
    }
  }
}

这个最简单了,就是在单位正方形内部填充一个 1 x 1/2 的长方形。Rect方法的参数说明如下:

  • 第 1 个参数 k 指定长方形的大小为 1/k x 1/(k+1)。
  • 第 2 个参数 color 指定长方形的颜色。
  • 第 3 个参数 dir 指定长方形的方向,H:水平,V:垂直。
  • 第 4~5 个参数指定长方形左上角的坐标。

编译和运行:

$ make && mono FillSquare.exe 100
dmcs -out:FillSquare.exe -r:System.Drawing.dll FillSquare.cs FillSquare.Draw.cs
FillSquare.cs(14,37): warning CS0414: The private field `Skyiv.Utils.FillSquare.Color2' is assigned but its value is never used
FillSquare.cs(14,49): warning CS0414: The private field `Skyiv.Utils.FillSquare.Color3' is assigned but its value is never used
FillSquare.cs(14,61): warning CS0414: The private field `Skyiv.Utils.FillSquare.Color4' is assigned but its value is never used
Compilation succeeded - 3 warning(s)

0100

第二幅图

画第二幅图的第二个源文件 FillSquare.Draw.cs:

namespace Skyiv.Utils
{
  sealed partial class FillSquare
  {
    void Draw()
    {
      Rect(1, Color1, V, 0, 0);
      Rect(2, Color2, H, S(2), S(3, 3));
      Rect(3, Color3, V, S(2, 4), S(3));
      Rect(4, Color4, H, S(2), 1 - S(3, 5));
    }
  }
}

这次在正方形内部从大到小依次填充 4 个长方形:

  • 第 1 个长方形左上角坐标 (0, 0),垂直放置。
  • 第 2 个长方形左上角坐标 (1/2, 2/3),水平放置。
  • 第 3 个长方形左上角坐标 (1/2 + 1/4, 1/3),垂直放置。
  • 第 4 个长方形左上角坐标 (1/2, 1 - 1/3 - 1/5),水平放置。

编译和运行:

$ make && mono FillSquare.exe 200
dmcs -out:FillSquare.exe -r:System.Drawing.dll FillSquare.cs FillSquare.Draw.cs

0200

第三幅图

画第三幅图的第二个源文件 FillSquare.Draw.cs:

namespace Skyiv.Utils
{
  sealed partial class FillSquare
  {
    void Draw()
    {
      Rect(1, Color1, V, 0, 0);
      Rect(2, Color2, H, S(2), S(3, 3));
      Rect(3, Color3, V, S(2, 4), S(3));
      Rect(4, Color4, H, S(2), 1 - S(3, 5));
      Rect(5, Color2, V, S(2), 1 - S(3, 5, 5));
      Rect(6, Color4, H, S(2), 1 - S(3, 5, 5, 7));
      Rect(7, Color3, H, S(2), 0);
    }
  }
}

在上一幅图的基础上,再填充 3 个长方形:

  • 第 5 个长方形左上角坐标 (1/2, 1 - 1/3 - 2/5),垂直放置。
  • 第 6 个长方形左上角坐标 (1/2, 1 - 1/3 - 2/5 - 1/7),水平放置。
  • 第 7 个长方形左上角坐标 (1/2, 0),垂直放置。

编译和运行:

$ make && mono FillSquare.exe 400
dmcs -out:FillSquare.exe -r:System.Drawing.dll FillSquare.cs FillSquare.Draw.cs

0400

第四幅图

画第四幅图的第二个源文件 FillSquare.Draw.cs:

namespace Skyiv.Utils
{
  sealed partial class FillSquare
  {
    void Draw()
    {
      Rect(1, Color1, V, S(2), 0);
      Rect(2, Color2, H, 0, S(3, 3));
      RectsV(3, 1, Color2, Color4, 0, 0);
      RectsV(5, 2, Color1, Color3, S(4), 0);
      RectsH(9, 1, Color2, Color3, 0, S(3, 5));
      RectsV(11, 2, Color2, Color4, S(4, 6), 0);
      RectsV(15, 2, Color2, Color4, S(4, 8), S(5, 7));
      RectsV(19, 1, Color1, Color3, S(4, 8, 16), S(5, 7));
      RectsV(21, 1, Color1, Color3, S(4, 8, 18), S(5, 7, 15, 17));
      RectsV(23, 1, Color1, Color2, S(9, 11), S(3, 5));
      RectsH(25, 2, Color1, Color2, S(4), S(5, 7, 7, 9));
      RectsH(29, 4, Color1, Color4, 0, S(3, 5, 10));
      RectsH(37, 5, Color3, Color4, S(4), S(5, 7, 7, 9, 26));
      RectsV(47, 4, Color2, Color4, S(4, 8, 18, 22), S(5, 7, 15, 17));
      RectsH(55, 2, Color1, Color2, S(4, 6), S(11, 13, 13, 15));
      RectsH(59, 3, Color1, Color3, S(4, 8), S(5, 7, 15, 17, 17, 19));
    }
  }
}

这幅图最复杂,共填充了 64 个长方形。这 64 个长方形分为 16 组,使用 16 条语句画出。RectsVRectsH方法的参数说明如下:

  • 第 1 个参数 k 指定头一个长方形的大小为 1/k x 1/(k+1)。
  • 第 2 个参数 pairs 指定要画的长方形的对数。
  • 第 3~4 个参数指定每对长方形的颜色。
  • 第 5~6 个参数指定头一个长方形左上角的坐标。

编译和运行:

$ make && mono FillSquare.exe 1600
dmcs -out:FillSquare.exe -r:System.Drawing.dll FillSquare.cs FillSquare.Draw.cs

1600


ConcreteMath