Desenhe os segmentos dos pontos dados

0

Eu tenho uma lista de vários pontos (> = 10000) no formato

x y

Por exemplo

1 2
1.0001 2.003
...

Existe algum programa Linux para desenhar segmentos entre o 1º, o 2º, o 2º, o 3º, o n-ésimo (n + 1) -segmento, preferencialmente como um gráfico vetorial?

    
por marmistrz 08.11.2014 / 21:36

2 respostas

3

Como Joseph R. mencionou, há o antigo clássico gnuplot , além de uma série de ofertas mais modernas que podem criar todos os tipos de gráficos em vários formatos, tanto baseados em vetores quanto bitmap. Mas esses programas tendem a ser tão versáteis que leva um bom tempo para aprender a usá-los adequadamente, o que pode ser um pouco assustador quando você quer traçar rapidamente um simples gráfico de linhas.

Eu venho me ensinando sobre o formato SVG recentemente, criando pequenos arquivos SVG manualmente e também escrevendo programas em Python para desenhar várias coisas usando SVG, então quando eu vi a sua pergunta eu pensei que era uma oportunidade perfeita para fazer um pouco mais Prática de programação SVG. :)

Aqui está um programa em bruto do Python que faz um gráfico de linha simples a partir de dados no formato dado na questão. A saída está no formato SVG, impressa em stdout, portanto, você precisará usar o redirecionamento para salvá-la em um arquivo. Os dados de entrada são lidos a partir do nome do arquivo especificado na linha de comando, mas se nenhum nome de arquivo for fornecido, o programa lê seus dados de stdin, para que o programa possa ser usado em um pipeline.

Os dados de entrada podem conter linhas em branco ou linhas de comentário com # como o primeiro caractere não em branco. Os valores X e Y em cada linha devem ser separados por pelo menos um caractere de espaço em branco (as guias são ok), outro espaço em branco em uma linha é ignorado, portanto, espaços antes do valor X ou após o valor Y são ignorados. >

O programa varre todos os dados XY para encontrar os valores máximo e mínimo, que são usados para calcular a viewBox do SVG para que a plotagem seja dimensionada e centralizada corretamente.

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()

Veja um exemplo de saída:

graphtest.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, aqui está o programa que usei para gerar os dados de teste para esse 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()

Uso

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

ou

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

Dê ao SVGgraph-points.py um argumento de 200 ou mais para criar um gráfico suave.

Como eu disse acima, este é apenas um roteiro grosseiro; Eu não senti vontade de adicionar manuseio sofisticado de linha de comando. :)

Você pode desejar modificar os parâmetros width e height , no script Python ou no SVG, mas eles não são essenciais, pois os programas de exibição SVG geralmente permitem controlar a escala enquanto visualiza imagens. E mesmo que você edite esses valores no arquivo SVG, a imagem será sempre centralizada e dimensionada adequadamente para que nenhuma peça seja cortada.

Você também pode experimentar o fator de escala margin , atualmente definido como 0,02, que determina a margem mínima em torno do gráfico. Você pode controlar a espessura (nominal) da linha plotada ajustando o strokewidth multiplicador, que atualmente é definido como 2.0.

Divirta-se!

Editar

Aqui está uma nova versão do script gráfico que usa um sistema de coordenadas convencional, em vez do sistema invertido que o SVG (e muitos outros sistemas de desenho de computador) usa. Então agora seus gráficos não estarão de cabeça para baixo. :)

#! /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()
    
por 09.11.2014 / 08:02
2

Você pode escrever um pequeno script. Algo assim:

#!/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')

Isso abre uma nova tela, desenha as linhas e grava o resultado em um arquivo.

    
por 08.11.2014 / 22:35