How to draw 2D diagram in linux

前端 未结 2 1615
别那么骄傲
别那么骄傲 2021-01-28 23:03

I have a .txt file contatning points which is look like following:

##x1 y1 x2 y2
123 567 798 900
788 900 87  89
....

I want to draw 2D diagram

相关标签:
2条回答
  • 2021-01-28 23:24

    You can also do this using rsvg-convert (from librsvg) or svg2png. Both of those programs expect an inout file in SVG format and render it as a PNG file. So you would need to convert your

    123 567 798 900
    788 900 87  89
    

    into this type of thing

    <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
      <line x1="123" y1="567" x2="798" y2="900" stroke="blue" stroke-width="1"/>
      <line x1="788" y1="900" x2="87" y2="89" stroke="blue" stroke-width="1"/>
    </svg>
    

    That can be done easily enough with a little awk script like this:

    awk '
       BEGIN{ printf("<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">\n") }
    
      {
         x1=$1; y1=$2; x2=$3; y2=$4;
         printf("<line x1=\"" x1 "\" y1=\"" y1 "\" x2=\"" x2 "\" y2=\"" y2 "\" stroke=\"blue\" stroke-width=\"1\"/>\n")
      }
    
      END{ printf("</svg>\n") }' points.txt
    

    You can then pump the output of that into either of the two programs I mentioned above:

    awk ... | rsvg-convert -b \#ffff00 > result.png
    

    0 讨论(0)
  • 2021-01-28 23:29

    Updated

    In the light of your reply about the number of lines and the maximum x,y dimensions, my original, ImageMagick-based approach at the bottom of this answer is clearly NOT the right one for your specific problem. However, I will leave it for others to see as it would be perfectly fine for up to a few dozen lines. I am now providing a more appropriate gnuplot version.

    Gnuplot Version

    If you want to do it with gnuplot, it would look something like this:

    set terminal png size 1000,1000
    set output 'result.png'
    unset xtics
    unset ytics
    unset border
    plot 'lines.txt' using 1:2:($3-$1):($4-$2) with vectors nohead notitle
    

    If you save that in a file calle plot.cmd, you can then run it with

    gnuplot < plot.cmd
    

    If you want arrowheads, use a variant like this:

    set terminal png size 1000,1000
    set output 'result.png'
    set style arrow 1 heads filled size screen 0.03,15,45 ls 1
    unset xtics
    unset ytics
    unset border
    plot 'lines.txt' using 1:2:($3-$1):($4-$2) with vectors arrowstyle 1  notitle 
    

    Magick++ and C++ Answer

    I decided to work out a Magick++ and C++ answer, just for fun. The code looks like this - and the command to compile is shown in the comments at the top.

    ////////////////////////////////////////////////////////////////////////////////
    // sample.cpp
    // Mark Setchell
    //
    // ImageMagick Magick++ sample code
    //
    // Compile with:
    // g++ sample.cpp -o sample $(Magick++-config --cppflags --cxxflags --ldflags --libs)
    ////////////////////////////////////////////////////////////////////////////////
    #include <Magick++.h> 
    #include <iostream> 
    #include <fstream>
    #include <sstream>
    #include <string>
    #include <vector>
    
    using namespace std; 
    using namespace Magick; 
    
    int main(int argc,char **argv) 
    { 
       InitializeMagick(*argv);
    
       // Create an image object, scaled by a factor of 100 to speed it up !
       float scale=100.0;
       Image image("650x650","white");
    
       // Construct drawing list 
       std::list<Magick::Drawable> drawList;
    
       // Initial settings, blue lines 1 pixel thick
       drawList.push_back(DrawableStrokeColor("blue"));
       drawList.push_back(DrawableStrokeWidth(1));
    
       // Read in lines from file, expected format "x1 y1 x2 y2"
       int lineno=0;
       std::ifstream infile("lines.txt");
       std::string line;
       while (std::getline(infile, line))
       {
          std::istringstream iss(line);
          int x1,y1,x2,y2;
          iss >> x1;
          iss >> y1;
          iss >> x2;
          iss >> y2;
          x1 = int(x1/scale);
          y1 = int(x2/scale);
          x2 = int(y1/scale);
          y2 = int(y2/scale);
          cout << "Line: " << ++lineno << " " << x1 << "," << y1 << " " << x2 << "," << y2 << endl;
          // Add this point to the list of lines to draw
          drawList.push_back(DrawableLine(x1,y1,x2,y2));
       }
    
       // Draw everything using completed drawing list 
       image.draw(drawList);
    
       // Write the image to a file 
       image.write( "result.png" ); 
    
       return 0; 
    }
    

    I generated 1,000 lines of random test data with Perl like this:

    perl -E 'for($i=0;$i<1000;$i++){printf("%d %d %d %d\n",int rand 65000,int rand 65000, int rand 65000, int rand 65000);}' > lines.txt
    

    The result looks like this:

    Original Answer

    You could also do it quite easily with ImageMagick which is already installed on most Linux distros anyway. There are actually only 4 lines of code in the following - the rest is all comments:

    #!/bin/bash
    
    # Create the output image, 1000x1000 pixels say
    convert -size 1000x1000 xc:pink result.png
    
    # Suppressing lines that have a hash (#) at the start, read in the file "lines.txt"
    grep -v "^#" lines.txt | while read x1 y1 x2 y2; do
    
       echo Read line $x1,$y1 $x2,$y2
    
       # Tell ImageMagick to draw the line on the image
       convert result.png -stroke blue -strokewidth 5 -draw "line $x1,$y1 $x2,$y2" result.png
    done
    

    Output

    Read line 123,567 798,900
    Read line 788,900 87,89
    
    0 讨论(0)
提交回复
热议问题