从给定点绘制线段

从给定点绘制线段

我有一个格式为多个点 (>= 10000) 的列表

x y

例如

1 2
1.0001 2.003
...

有没有Linux程序可以绘制第1和第2、第2和第3、第n和第(n+1)段之间的段,最好是矢量图形?

答案1

正如 Joseph R. 提到的,有古老的经典gnuplot,加上许多更现代的产品,可以创建各种格式的各种图形,包括基于矢量的和位图的。但此类程序往往用途广泛,需要相当长的时间才能学习如何正确使用它们,当您只想快速绘制简单的折线图时,这可能有点令人畏惧。

我最近一直在自学 SVG 格式,手动创建小型 SVG 文件,还编写 Python 程序来使用 SVG 绘制各种东西,所以当我看到你的问题时,我认为这是进行更多 SVG 编程练习的绝佳机会。 :)

这是一个原始的 Python 程序,它根据问题中给出的格式的数据制作一个简单的折线图。输出为 SVG 格式,打印到 stdout,因此您需要使用重定向将其保存到文件中。输入数据是从命令行上指定的文件名读取的,但如果没有给出文件名,则程序从 stdin 读取数据,因此该程序可以在管道中使用。

输入数据可以包含空行或#第一个非空白字符的注释行。每行上的 X 和 Y 值必须由至少一个空格字符分隔(制表符也可以),一行上的其他空格将被忽略,因此 X 值之前或 Y 值之后的空格将被忽略。

该程序扫描所有 XY 数据以查找最大值和最小值,这些值用于计算 SVG viewBox,以便绘图正确缩放和居中。

SVGgraph.py

#! /usr/bin/env python

''' Create a simple line graph as an SVG file

    Written by PM 2Ring 2014.11.09
'''

import sys

def bounding_box(points):
    xtup, ytup = zip(*points)

    xlo = min(xtup)
    xhi = max(xtup)

    ylo = min(ytup)
    yhi = max(ytup)
    return xlo, ylo, xhi - xlo, yhi - ylo


def points_to_SVG(points, width, height):
    #Get graph bounds & adjust to allow for a margin
    xlo, ylo, xsize, ysize = bounding_box(points)
    margin = 0.02
    xmargin = xsize * margin
    ymargin = ysize * margin
    xlo -= xmargin
    xsize += 2 * xmargin
    ylo -= ymargin
    ysize += 2 * ymargin

    strokewidth = 2.0 * min(xsize, ysize) / float(max(width, height))

    head = '''<?xml version="1.0"?>\n<svg xmlns="http://www.w3.org/2000/svg"
    width="%d" height="%d" viewBox="%f %f %f %f"
    preserveAspectRatio="xMidYMid meet">\n\n''' % (width, height, xlo, ylo, xsize, ysize)

    body = '    <polyline points="\n' + '\n'.join(["%f, %f" % t for t in points]) + '\n"\n'

    tail = 'style="fill:none; stroke-width:%f; stroke:#006600;"/>\n</svg>' % strokewidth

    return head + body + tail


def main():
    iname = sys.argv[1] if len(sys.argv) > 1 else None
    f = open(iname, 'rt') if iname else sys.stdin

    data = f.read().splitlines()
    if iname is not None:
        f.close()

    points = []
    for line in data:
        #Skip blank lines
        if not line: continue

        x, y = line.split()

        #Skip comments: lines which have '#' as the first non-blank char
        if x.startswith('#'): continue

        points.append((float(x), float(y)))

    width, height = 800, 600
    print points_to_SVG(points, width, height)


if __name__ == '__main__':
    main()

这是一些示例输出:

图测试.svg

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
    width="800" height="600" viewBox="-0.240855 -3.881333 12.524483 7.762666"
    preserveAspectRatio="xMidYMid meet">

    <polyline points="
0.000000, 0.000000
0.523599, 3.732051
1.047198, 2.598076
1.570796, -0.500000
2.094395, -0.866025
2.617994, 0.267949
3.141593, 0.000000
3.665191, -0.267949
4.188790, 0.866025
4.712389, 0.500000
5.235988, -2.598076
5.759587, -3.732051
6.283185, -0.000000
6.806784, 3.732051
7.330383, 2.598076
7.853982, -0.500000
8.377580, -0.866025
8.901179, 0.267949
9.424778, 0.000000
9.948377, -0.267949
10.471976, 0.866025
10.995574, 0.500000
11.519173, -2.598076
12.042772, -3.732051
"
style="fill:none; stroke-width:0.019407; stroke:#006600;"/>
</svg>

FWIW,这是我用来生成该 SVG 测试数据的程序。

SVGgraph-points.py

#! /usr/bin/env python

''' Create a list of points to test SVGgraph.py with

    Written by PM 2Ring 2014.11.09
'''

import sys
from math import pi, sin

def f(x):
    return sin(x) + 2.0 * sin(x * 2.0) + 1.5 * sin(x * 3.0)

def make_points(n):
    points = n * [None]
    for i in xrange(n):
        x = 4.0 * pi * i / n
        y = f(x)
        points[i] = (x, y)
    return points


def main():
    n = int(sys.argv[1]) if len(sys.argv) > 1 else 24
    points = make_points(n)
    print '\n'.join(["%f %f" % t for t in points])


if __name__ == '__main__':
    main()

用法

python SVGgraph-points.py 24 > testdata
python SVGgraph.py testdata > graphtest.svg

或者

python SVGgraph-points.py | python SVGgraph.py > graphtest.svg

为 SVGgraph-points.py 指定 200 或更高的参数以创建平滑的图形。

正如我上面所说,这只是一个粗略的脚本;我不想添加花哨的命令行处理。 :)

您可能希望在 Python 脚本或 SVG 中修改width和参数,但它们并不重要,因为 SVG 显示程序通常允许您在查看图像时控制比例。height即使您在 SVG 文件中编辑这些值,图像也将始终居中并适当缩放,以免任何部分被切断。

您可能还想尝试margin缩放因子,当前设置为 0.02,它决定了图形周围的最小边距。您可以通过调整乘数来控制绘制线的(标称)粗细strokewidth,当前设置为 2.0。

玩得开心!


编辑

这是图形脚本的新版本,它使用传统的坐标系,而不是 SVG(和许多其他计算机绘图系统)使用的倒置系统。所以现在你的图表不会颠倒了。 :)

#! /usr/bin/env python

''' Create a simple line graph as an SVG file

    Uses a conventional coordinate system,
    not the usual inverted SVG system.

    Written by PM 2Ring 2014.11.11
'''

import sys

def bounding_box(points):
    xtup, ytup = zip(*points)

    xlo = min(xtup)
    xhi = max(xtup)

    ylo = min(ytup)
    yhi = max(ytup)
    return xlo, ylo, xhi, yhi


def points_to_SVG(points, width, height):
    #Get graph bounds & adjust to allow for a margin
    xlo, ylo, xhi, yhi = bounding_box(points)
    xsize = xhi - xlo
    ysize = yhi - ylo

    margin = 0.02
    xmargin = xsize * margin
    ymargin = ysize * margin
    xlo -= xmargin
    xsize += 2 * xmargin
    yhi += ymargin
    ysize += 2 * ymargin

    strokewidth = 2.0 * min(xsize, ysize) / float(max(width, height))

    head = '''<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
    width="%d" height="%d" viewBox="%f %f %f %f"
    preserveAspectRatio="xMidYMid meet">

    <polyline style="fill:none; stroke-width:%f; stroke:#006600;"
        transform="scale(1, -1)"
        points="\n''' % (width, height, xlo, -yhi, xsize, ysize, strokewidth)

    body = '\n'.join(["%f, %f" % t for t in points]) 

    tail = '\n"/>\n</svg>'

    return head + body + tail


def main():
    iname = sys.argv[1] if len(sys.argv) > 1 else None
    f = open(iname, 'rt') if iname else sys.stdin

    data = f.read().splitlines()
    if iname is not None:
        f.close()

    points = []
    for line in data:
        #Skip blank lines
        if not line: continue

        x, y = line.split()

        #Skip comments: lines which have '#' as the first non-blank char
        if x.startswith('#'): continue

        points.append((float(x), float(y)))

    width, height = 800, 600
    print points_to_SVG(points, width, height)


if __name__ == '__main__':
    main()

答案2

你可以写一个小脚本。像这样的东西:

#!/usr/bin/ruby
require 'rvg/rvg'
require 'scanf'
include Magick

RVG::dpi = 72

rvg = RVG.new(2.5.in, 2.5.in).viewbox(0,0,300,300) do |canvas|
    canvas.styles(:stroke=>'black', :stroke_width=>4)
    oldx=oldy=nil
    ARGF.map{|line| line.scanf("%f %f")}.each do | x,y |
       canvas.line(oldx,oldy,x,y) if oldx
       oldx,oldy=x,y
    end
end

rvg.draw.write('output.png')

这将打开一个新画布,在其上绘制线条并将结果写入文件。

相关内容