使用程序输出/绘制图形真的非常有趣,而且只需要用到简单的数学知识。
不知道可以写些什么,今天突然看到很久以前写的一些代码,他们是用来输出或者绘制图形的,于是想给大家展示一下。
热身/矩形
Java是我最熟悉的编程语言,所以大部分内容我将使用Java来进行演示。
其实核心的内容并不是编程语言,而是算法本身。
先从最简单的开始,比如说输出一个矩形。
我知道这个实在太简单了,根本不用思考。
1 | for (int i = 0; i <= 5; i++) { |
这样将会输出一个矩形,并且可以通过自定义 i 和 j 的值来控制矩形的大小。
开始吧/圆形
跳过三角形那些不说,现在我们想要稍微增加难度,如何输出一个圆形呢?
思路很简单,需要用到圆的方程式,即 x^2 + y^2 = r^2
我们假想这个圆圈的中心在坐标轴的(0, 0),且 r = 1,于是 x^2 + y^2 = 1
并假象有一个正方形 [A(-1, 1), B(-1, -1), C(1, -1), D(1, 1)],显然圆与正方形相切。
我们遍历正方形内所有的坐标。
满足 x^2 + y^2 <= 1 则说明在圆内,输出某个(比如*)字符,否则输出空格。
于是不难写出1
2
3
4
5
6
7for (double y = 1.0; y >= -1.05; y -= 0.05) {
for (double x = -1.0; x <= 1.025; x += 0.025) {
double r = Math.sqrt((x * x) + (y * y)); /* 获取 x 和 y 的平方和并开根号 */
System.out.print(r <= 1 ? "*" : " ");
}
System.out.println();
}
这样就可以输出一个非常粗糙的圆形。
还可以通过改变 x 和 y 的值来对输出图形造成变化。
然后/爱心
接下来我们将要输出一颗爱心。
这个难度也不算大,只要知道爱心的方程式,原理和输出圆形是一样的。
不用多解释,直接贴出代码吧。1
2
3
4
5
6
7
8double a;
for (double y = 1.5; y > -1.5; y -= 0.1) {
for (double x = -1.5; x < 1.5; x += 0.05) {
a = x * x + y * y - 1;
System.out.print(a * a * a - x * x * y * y * y <= 0 ? "%" : "/");
}
System.out.println();
}
这里我输出的符号是 “%” 和 “/“,我认为这样输出的效果看起来更好。
进阶/立体图形
我们已经输出了圆和爱心,但是它们都是平面的,那么如何输出立体的图形呢,比如说球和立体爱心。
这个问题真是困扰了我很久,后来参考了一些资料,又在知乎看了@Milo Yip的相关回答,才知道如何做到。
这里直接移植了他的代码。
球1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(void)
{
double x, y;
for (y = 1; y >= -1; y -= 0.05, putchar('\n'))
{
for (x = -1; x <= 1; x += 0.025)
{
putchar(x * x + y * y > 1 ? 'M' : "@@%#*+=;:. "[(int)(((x + y + sqrt(1 - (x * x + y * y))) * 0.5773502692 + 1)* 5.0 + 0.5)]);
}
}
getchar();
return 0;
}
立体爱心1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
double f(double x, double y, double z)
{
double a = x * x + 9.0 / 4.0 * y * y + z * z - 1;
return a * a * a - x * x * z * z * z - 9.0 / 80.0 * y * y * z * z * z;
}
double h(double x, double z)
{
for (double y = 1.0; y >= 0; y -= 0.001)
if (f(x, y, z) <= 0)
return y;
return 0;
}
int main(void)
{
for (double z = 1.5; z > -1.5; z -= 0.05)
{
for (double x = -1.5; x < 1.5; x += 0.025)
{
double v = f(x, 0, z);
if (v <= 0)
{
double y0 = h(x, z);
double ny = 0.01;
double nx = h(x + ny, z) - y0;
double nz = h(x, z + ny) - y0;
double nd = 1.0 / sqrtf(nx * nx + ny * ny + nz * nz);
double d = (nx + ny - nz) * nd * 0.5 + 0.5;
putchar(".:-=+*#%@"[(int)(d * 5.0)]);
}
else
putchar(' ');
}
putchar('\n');
}
getchar();
}
最终/动态立体爱心
这是一个会动的控制台程序,但仅支持Windows。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
double f(double x, double y, double z)
{
double a = x * x + 9.0 / 4.0 * y * y + z * z - 1;
return a * a * a - x * x * z * z * z - 9.0 / 80.0 * y * y * z * z * z;
}
double h(double x, double z)
{
for (double y = 1.0; y >= 0.0; y -= 0.001)
if (f(x, y, z) <= 0)
return y;
return 0;
}
int main(void)
{
HANDLE o = GetStdHandle(STD_OUTPUT_HANDLE);
_TCHAR buffer[25][80] = { _T(' ') };
_TCHAR ramp[] = _T(".:-=+*#%@");
for (double t = 0; ;t += 0.1)
{
int sy = 0;
double s = sinf(t);
double a = s * s * s * s * 0.2;
for (double z = 1.3; z > -1.2; z -= 0.1)
{
_TCHAR* p = &buffer[sy++][0];
double tz = z * (1.2 - a);
for (double x = -1.5; x < 1.5; x += 0.05)
{
double tx = x * (1.2 + a);
double v = f(tx, 0, tz);
if (v <= 0.0f)
{
double y0 = h(tx, tz);
double ny = 0.01;
double nx = h(tx + ny, tz) - y0;
double nz = h(tx, tz + ny) - y0;
double nd = 1.0 / sqrtf(nx * nx + ny * ny + nz * nz);
double d = (nx + ny - nz) * nd * 0.5 + 0.5;
*p++ = ramp[(int)(d * 5.0)];
}
else
{
*p++ = ' ';
}
}
}
for (sy = 0; sy < 25; sy++)
{
COORD coord = {0, sy};
SetConsoleCursorPosition(o, coord);
WriteConsole(o, buffer[sy], 79, NULL, 0);
}
Sleep(33);
}
}
番外/绘画
使用Python画了一张小猪佩奇嘻嘻嘻。
1 | import turtle as t |
END…