我一直想知道sl蒸汽机小火车是怎么做的,我惊奇于它那烟囱里冒出的炫酷的烟,以及车轮的滚动:
于是我想自己试一试,我就做一个滚动的圆就不错了,也不奢望能有什么好看的效果,就类似下面的样子(我故意usleep(500000);调了慢速):
12个字母在一个圆上轮转,以展示滚动的效果,我这些是完全算出来的:
我的代码非常简单,如下所示:
#include
#include
#include
// list.h是我抽取的Linux kernel的,不在这里贴了。
#include "list.h"
#define LINES 100
#define COLS 150
struct node {
struct list_head list;
int X;
int Y;
};
struct list_head circle;
struct list_head stack;
void init_term()
{
initscr();
noecho();
keypad(stdscr, 1);
nodelay(stdscr, 1);
curs_set(0);
}
char pad[LINES][COLS];
void init(struct list_head *list, struct list_head *stack, int x, int y, int R)
{
int i, j, iter = 0;
struct list_head *tmp, *n;
list_for_each_safe(tmp, n, list) {
struct node *p = list_entry(tmp, struct node, list);
for (j = 0; j < LINES; j++) {
for (i = 0; i < COLS; i++) {
if ((i - x)*(i - x) + (j - y)*(j - y) == (R-0)*(R-0) &&
// (i - x)*(i - x) + (j - y)*(j - y) < (R+1)*(R+0) &&
pad[j][i] != 1) {
pad[j][i] = 1;
p->X = i;
p->Y = j;
// 下面是为了把逐行扫描顺讯转换为圆周顺序。stack链表模拟一个栈
if (iter % 2) {
list_del(tmp);
list_add(tmp, stack);
}
iter ++;
i += COLS;
j += LINES;
}
}
}
}
// 依次pop出来,加入circle链表,完成圆周顺序的转换。
list_for_each_safe(tmp, n, stack) {
list_del(tmp);
list_add_tail(tmp, list);
}
}
//char *fl[16] = {"#####", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A"};
int main(int argc, char **argv)
{
int i, num;
struct list_head *head;
num = atoi(argv[1]);
INIT_LIST_HEAD(&circle);
INIT_LIST_HEAD(&stack);
for (i = 0; i < num; i++) {
struct node *p = calloc(1, sizeof(struct node));
INIT_LIST_HEAD(&p->list);
list_add_tail(&p->list, &circle);
}
head = circle.next;
memset(pad, 0, sizeof(pad));
init_term();
init(&circle, &stack, 20, 20, 10);
memset(pad, 0, sizeof(pad));
i = 1;
while (i++) {
struct list_head *tmp;
char c[2] = {'A', 0};
int idx = 0;
list_for_each(tmp, &circle) {
struct node *p = list_entry(tmp, struct node, list);
mvprintw(p->Y, p->X + i, c);
c[0] ++;
}
refresh();
usleep(500000);
clear();
list_del(head);
list_add_tail(head, &circle);
head = circle.next;
}
}
…
…
在此之前,我下载了sl的代码:
https://github.com/mtoyoda/sl
我编译,然后我运行,但我一直没有看代码,我希望自己完成一个还算过得去的效果之后,再来学习这个代码。
手欠,运行了一个strings命令,然后就看到了下面的:
[root@localhost sl]# strings ./sl
/lib64/ld-linux-x86-64.so.2
libncurses.so.5
__gmon_start__
COLS
stdscr
...
------'|oOo|=[]=- O=======O=======O | ||=======_|__
/~\____|___|/~\_| || || |__|+-/~\_|
------'|oOo|==[]=- O=======O=======O | ||=======_|__
------'|oOo|===[]=- O=======O=======O | ||=======_|__
------'|oOo|===[]=- || || | ||=======_|__
/~\____|___|/~\_| O=======O=======O |__|+-/~\_|
------'|oOo|==[]=- || || | ||=======_|__
/~\____|___|/~\_| O=======O=======O |__|+-/~\_|
==== ________ ___________
_D _| |_______/ \__I_I_____===__|_________|
|(_)--- | H\________/ | | =|___ ___|
/ | | H | | | | ||_| |_||
| | | H |__--------------------| [___] |
| ________|___H__/__|_____/[][]~\_______| |
|/ | |-----------I_____I [][] [] D |=======|__
__/ =| o |=-~~\ /~~\ /~~\ /~~\ ____Y___________|__
|/-=|___|= || || || |_____/~\___/
\_/ \O=====O=====O=====O_/ \_/
|/-=|___|=O=====O=====O=====O |_____/~\___/
\_/ \__/ \__/ \__/ \__/ \_/
__/ =| o |=-O=====O=====O=====O \ ____Y___________|__
__/ =| o |=-~O=====O=====O=====O\ ____Y___________|__
|/-=|___|= O=====O=====O=====O|_____/~\___/
\_/ \_O=====O=====O=====O/ \_/
;*3$"
GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-36)
crtstuff.c
__JCR_LIST__
deregister_tm_clones
__do_global_dtors_aux
好家伙,原来这么好玩的东西不是算出来的啊, 原来这么好玩的东西竟然是直接逐帧画出来的!
虽然它是ncurses的字符帧,但原理上还是空间换了时间,就像Windows图标总是准备好几个,大的,中等的,小的,它们都是提前准备好的位图Resource文件,而不是即时计算出来的矢量图…
我怎么就没有想到,我还老老实实傻乎乎的去计算,如果我也循着这个直接画帧的思路,也许可以在周末的时候就能完成一个骷髅头☠️了,然后以此逗安德森先生高兴高兴了。
唉,还是功力不够啊!没能及时想到时间空间的自如转换,这也太经理了。
最后,我觉得无论如何对ncurses来讲,还真的没必要比较两种方式的性能,字符终端毕竟不是像素终端,即使是遍历,也消耗不了多少资源。
浙江温州皮鞋湿,下雨进水不会胖。