I didn't know what it meant when I read sparkline for the first time. I never heard of it before, but it actually appeared in the redis code, at the beginning, I thought this was also a common queue. I split it into a struct bag. The analysis is far from what I thought. In sparkline, a line chart is similar to a line chart, which consists of an information point. So you may understand what sparkline. C is used for drawing. Let's take a look at the internal structure of the drawing and what elements are needed for the drawing:
/* sparkline.h -- ASCII Sparklines header file * * --------------------------------------------------------------------------- * * Copyright(C) 2011-2014 Salvatore Sanfilippo <[email protected]> * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */#ifndef __SPARKLINE_H#define __SPARKLINE_H/* sparkline是一类信息体积小和数据密度高的图表。目前它被用作一些测量, *相关的变化的信息呈现的方式,如平均温度,股市交投活跃。sparkline常常以一组多条的形式出现在柱状图,折线图当中。 *可以理解为一个图线信息 *//* A sequence is represented of many "samples" *//* 可以理解为图像上的一个信息点,有文字,有值的大小 */struct sample { double value; char *label;};/* 图线信息结构体,包括n个元素点,可以据此描述出图,绘图的可不是直接按点和值直接绘制的 */struct sequence {//当前元素点个数 int length; //总共的文字个数,有些点没有label描述,为NULL int labels; //元素点列表 struct sample *samples; //元素中的最大值,最小值 double min, max;};/* 定义了一些渲染图时候一些属性操作设置 */#define SPARKLINE_NO_FLAGS 0#define SPARKLINE_FILL 1 /* Fill the area under the curve. */#define SPARKLINE_LOG_SCALE 2 /* Use logarithmic scale. */struct sequence *createSparklineSequence(void); //创建图线序列结构体void sparklineSequenceAddSample(struct sequence *seq, double value, char *label); //在图线序列中添加一个信息点void freeSparklineSequence(struct sequence *seq); //释放图线序列sds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags); //渲染图线序列为一个图,其实就是得到一个字符串组成的图sds sparklineRender(sds output, struct sequence *seq, int columns, int rows, int flags); //方法同上,只是少可一个偏移量#endif /* __SPARKLINE_H */
We can see that the above sample structure actually refers to the meaning of the "Information Point" element, which is very simple. A text label and a value are concise and clear, and then sequence is naturally a line chart, it defines the element point list, which also contains the length, maximum value, minimum value, and comprehensive line. The subsequent drawing operations are performed based on the sequence structure of the graphic line. Struct is not complex at all. But the implementation of drawing is not simple at all. How can we draw a line similar to a line based on the given point information? Don't forget, this is the line of drawing in the command line window, therefore, it is not as convenient as the GUI operations in advanced languages. Let's look at how redis code is written.
/* sparkline.c -- ASCII Sparklines * This code is modified from http://github.com/antirez/aspark and adapted * in order to return SDS strings instead of outputting directly to * the terminal. * * ---------------------------------------------------------------------------
In sparkline. C, this code is modified from:
aspark is a C program to display ASCII Sparklines.It is completely useless in 2011.
Yes, that is to say, aspark is used to display the graph effect on the c program. Later, I saw that the code was almost the same. I added my own things to the redis code and slightly modified it. There were several forms of aspark line display. The first one was, the simplest presentation:
$ ./aspark 1,2,3,4,10,7,6,5 `-_ __-` `
The second one expands the number of rows to more rows to display more data. The preceding two rows are displayed. When there is more data, adjust the number of rows. By default, two rows are displayed.
$ ./aspark 1,2,3,4,5,6,7,8,9,10,10,8,5,3,1 --rows 4 _-``_ _` -` ` _-` `_
Of course, it can be more visualized, And it looks more comfortable to fill characters in the blank space:
$ ./aspark 1,2,3,4,5,6,7,8,9,10,10,8,5,3,1 --rows 4 --fill _o##_ _#||||| o#|||||||# _o#||||||||||#_
With the "|" symbol, it is very imaginative. The most important thing is how to achieve this effect. The core code is as follows:
/* Render part of a sequence, so that render_sequence() call call this function * with differnent parts in order to create the full output without overflowing * the current terminal columns. *//* 渲染出这个图线信息 */sds sparklineRenderRange(sds output, struct sequence *seq, int rows, int offset, int len, int flags) { int j; double relmax = seq->max - seq->min; int steps = charset_len*rows; int row = 0; char *chars = zmalloc(len); int loop = 1; int opt_fill = flags & SPARKLINE_FILL; int opt_log = flags & SPARKLINE_LOG_SCALE; if (opt_log) { relmax = log(relmax+1); } else if (relmax == 0) { relmax = 1; } while(loop) { loop = 0; memset(chars,' ',len); for (j = 0; j < len; j++) { struct sample *s = &seq->samples[j+offset]; //value派上用处了 double relval = s->value - seq->min; int step; if (opt_log) relval = log(relval+1); //最后会算出相关的step step = (int) (relval*steps)/relmax; if (step < 0) step = 0; if (step >= steps) step = steps-1; if (row < rows) { /* Print the character needed to create the sparkline */ /* step控制输出的字符是哪一个 */ int charidx = step-((rows-row-1)*charset_len); loop = 1; if (charidx >= 0 && charidx < charset_len) { chars[j] = opt_fill ? charset_fill[charidx] : charset[charidx]; } else if(opt_fill && charidx >= charset_len) { //用"|"填充内容,更加可视化 chars[j] = '|'; } } else { /* Labels spacing */ if (seq->labels && row-rows < label_margin_top) { loop = 1; break; } /* Print the label if needed. */ if (s->label) { int label_len = strlen(s->label); int label_char = row - rows - label_margin_top; if (label_len > label_char) { loop = 1; chars[j] = s->label[label_char]; } } } } if (loop) { row++; output = sdscatlen(output,chars,len); output = sdscatlen(output,"\n",1); } } zfree(chars); return output;}
Due to my limited ability, I am not quite familiar with the specific details. After reading about the details, I took a slight look at the use of the variables. The above code is very good code, it's worth learning. Today, at least I know what sparkline is, haha.
Redis source code analysis (5) --- sparkline micrograph