我们在上一次讲了如何画最基本的柱状图。今天我们来看看这段Processing程序是不是有需要改进的地方。因为我们的目的是要能自动的根据所给的数据生成柱状图,所以让我们来试试另一组数据,替换下面这行,其他不变。

int[] numbers = 
    {2, 5, 3, 1, 6, 5, 9, 4, 7, 3, 2, 5, 
     1, 4, 2, 5, 2, 5, 3, 1, 6, 5, 31, 4,
     7, 3, 2, 5, 1, 4, 2, 5 };

我们可以看到当改变数据的时候,生成的柱状图有几个问题。

1. 整个图表的宽度和高度都没有用满。这是因为在计算每个长方形的宽度和单位高度的时候我们用了整型,int。所以,值的小数部分被强制去掉了,比如3.5就变成了3, 改用float就好了。

float w = float(chart_width)/numbers.length;
float h = float(chart_height)/max_number;

改了这个之后,如果你看到长方形的边框有些错位,你需要在开始画图前加上smooth();

2. x轴的文字标识重叠在了一起。这是因为当数据变多的时候,每个长方形的宽度就变小了。而文字标识的字体大小是不变的。这个问题怎么解决呢?我们可以不用显示所有的标识,而根据Sketch的宽度和字体大小来显示部分标识。同时,我们想要有规律的显示标识,也就是说每隔多少个数据,显示一个。首先我们要计算一下总共可以显示多少个。

float num_x_ticks = chart_width/textWidth(Integer.toString(numbers.length));

在Processing里,textWidth()会返回一段文字显示在屏幕上的宽度。然后,我们计算每隔几个长方形要显示文字标识。这里我们用到了ceil()函数。这个函数会返回大于或者等于参数的最小整数。比如,ceil(3.5)=4。因为,我们要保证标识之间没有重叠,所以要取到上限。

int x_tick_interval = ceil(numbers.length/num_x_ticks);

最后我们需要做的就是在显示标识的命令前加上if条件,i%x_tick_interval返回的是i除以x_tick_interval得到的余数。这个条件保证了每x_tick_interval个长方形就会显示标识。

for(int i=0;i<numbers.length;i++) {
    rect(chart_x+i*w, chart_y, w, -h*numbers[i]);
    if(i%x_tick_interval==0) {
        text(i, chart_x+i*w+w/2, chart_y+tick_padding);
    }
}

下面就是我们得到的可视化。

对于数据可视化来说,互动是非常重要的。对于一个柱状图来说,当用户鼠标移动某个长方形上时,能显示对应的数值很有用。有很多显示的效果,我们这里用的是在图表的最上方,对应鼠标长方形的位置显示数值,然后用垂直线把数值和对应的长方形连起来,就像下面这样。

首先,我们要改动整个程序的结构。因为,每次鼠标移动,标识的位置都不同,所以可视化必须不断的刷新。之前的程序只是在setup()函数里面画一次是不行的。我们要把画图的部分放到draw()函数里面。在processing里面,draw()函数会被不停的调用。为了让程序更快,我们把一些只用做一次的计算放到setup()里面。把变量都定义在最外面。

现在,我们来看看怎么画标识。在processing中,mouseX和mouseY存了鼠标的位置。我们先要确定鼠标在图表范围内。然后我们要找到鼠标所指向的长方形。别忘了,长方形是从chart_x开始。所以int selectedBarIndex = int((mouseX-chart_x)/w)。w是每个长方形的宽度。 链接标识和长方形的线从离长方形顶部5个像素的地方开始到标识下面5个像素。

if(mouseX>chart_x && mouseX<chart_x+chart_width) {  
    stroke(100,100,100);
    int selectedBarIndex = int((mouseX-chart_x)/w);
    textAlign(CENTER,BOTTOM);
    line(mouseX, top_margin-5, mouseX, chart_y-h*numbers[selectedBarIndex]-5);
    text(numbers[selectedBarIndex], mouseX, top_margin-10);
}

可以在这里下载完整的程序。

关于柱状图就到这里了,我们来总结一下。用processing这样的程序语言来绘图或者做可视化最重要的是要把脑子里的图转换成计算机能懂的数字。准确的告诉计算机,从哪里到哪里画一条直线,或者在哪里显示文字。这个可能是初学者最难的地方。我刚学OpenGL的时候,常做的一件事是拿一张纸,把要做的可视化先画出来。然后假设纸的大小就是屏幕大小,左上角是原点,用尺量一量,给图上的每个物体标上坐标。这样再开始写程序就容易多了。这个对我帮助很大,大家可以试试。

最后,希望得到大家的反馈,是简单了还是难了,是快了还是慢了。希望能帮助更多的人学会processing,开始做可视化!

© 2011, 视物 | 致知. All rights reserved.

Related Posts:

  1. 简单,速度中等,不错,个人建议上一天的课介绍一天的新图表,这样有利于大家消化吸收,其实这个速度都算是满的了,按理来说天天上都没有问题,但是估计不是人人都有这个时间呢,每个人的情况不一样嘛,以上仅代表个人立场,如有雷同,纯属巧合。
    谢谢楼主的无私奉献
    获益良多
    谢谢

  2. 感觉速度中等吧,可以接受。自己稍微有点基础,这学期还在上数据结构的课程,给博主提供反馈吧
    博主写的很不错啊,继续支持

  3. 有没有实时显示动态曲线的例子???最好是可以将采集到的三轴加速度传感器的数值能用三条曲线动态表示出来。