简体   繁体   中英

Draw Line using c++ without graphics

I wish to write program that draw line using c++ lang without using graphics library I tried a lot but I don't reach to the result which I want the line equation is :

y=mx+b

I want to draw this line (for example)

y=3x+2

this is my code

#include <iostream>
#include <math.h>

using namespace std;

int pth (int x)  {
    return 2*x-3;
}

int main()  {
    int i,j,y;
    for (int x=-10;x <= 10;x++)  {
        for ( i=-10;i <= x;i++)  {
            if (i==x) {
                y=pth(x);
                cout<<"x=";
                cout<<x;
                cout<<"y=";
                cout<<y;

                for(j=-10;j<=y;j++) {
                    if(j==y)
                        cout << "*";
                    else
                        cout << " ";
                }
            }
        }
        cout << "\n";
    }
    cin.get();
    return 0;
 }

the output is like this

x=0y=3   *
 x=1y=5     *
  x=2y=7       *
   x=3y=9         *
    x=4y=11           *
     x=5y=13             *
      x=6y=15               *
       x=7y=17                 *
        x=8y=19                   *
         x=9y=21                     *
          x=10y=23                       *

but for the given equation (y=2x-3) the result must be

                                      *
                                  *
                              *
                           *
                        *
                    *
                 *

the code is right as I think but the problem is how to determine the direction of the line so it will draw correctly

Because I like a challenge, I made a tiny Canvas class, to be used like:

int main() {
    using Canvas = BasicCanvas<160, 80>;
    Canvas canvas;
    canvas.origin = {canvas.cols()/3, canvas.rows()/3};

    canvas.axes();

    canvas.plot([](double x) { return  x; });
    canvas.plot([](double  ) { return -8; });
    canvas.plot([](double x) { return 3*log(x); });
    canvas.plot([](double x) { return 4*sin(x/2); });
    canvas.plot([](double x) { return 24*cos(x/12); });

    std::cout << canvas;
}

Which prints 在此处输入图片说明

Or commenting out the origin assignment: 在此处输入图片说明

Implementation

The implementation basically iterates through the positions on the x axis and plots approximate line drawing characters depending on the angle (first derivative) of the function at that point:

template <size_t Columns = 100, size_t Rows = 50>
struct BasicCanvas {
    using Line   = std::array<char, Columns>;
    using Screen = std::array<Line, Rows>;
    struct Coord { size_t x, y; };

    static constexpr size_t rows() { return Rows; }
    static constexpr size_t cols() { return Columns; }

    Screen screen;
    Coord origin;

    BasicCanvas(Coord origin = {Columns/2, Rows/2}) : origin(origin) {
        Line empty;
        std::fill(empty.begin(), empty.end(), '.');
        std::fill(screen.begin(), screen.end(), empty);
    }

    friend std::ostream& operator<<(std::ostream& os, BasicCanvas const& c) {
        for (auto& line : c.screen) {
            os.write(line.data(), line.size()) << "\n";
        }
        return os;
    }

    Line&       operator[](size_t y)          { return screen.at(screen.size()-(y+1)); }
    Line const& operator[](size_t y) const    { return screen.at(screen.size()-(y+1)); }
    char&       operator[](Coord coord)       { return operator[](coord.y).at(coord.x); }
    char const& operator[](Coord coord) const { return operator[](coord.y).at(coord.x); }

    void axes() {
        for (auto& line : screen)
            line.at(origin.x) = '|';

        auto& y_axis = operator[](origin.y);

        for (auto& cell : y_axis)
            cell = '-';

        y_axis.at(origin.x) = '+';
    }

    template <typename F>
    void plot(F f, double scaleX = 1.0, double scaleY = 1.0) {
        for (size_t x_tick = 0; x_tick < Columns; ++x_tick) {
            auto x = (x_tick * scaleX) - origin.x;
            auto y = f(x);
            auto y_ = derivative(f, x, scaleX/2);

            size_t y_tick = (y / scaleY) + origin.y;
            if (y_tick < Rows)
                operator[]({x_tick, y_tick}) = glyph(y_);
        }
    }

  private:
    template <typename F>
    auto derivative(F const& f, double x, double dx = 0.01) {
        return (f(x+dx)-f(x-dx))/(2*dx);
    }

    char glyph(double tangent) {
        auto angle = atan(tangent);

        while (angle < 0) 
            angle += 2*M_PI;

        int angle_index = 2.0 * angle / atan(1);

        return R"(--/||\--)"[angle_index % 8];
    }

};

Full Listing

Live On Coliru

(simplified function selection):

#include <iostream>
#include <array>
#include <cmath>

template <size_t Columns = 100, size_t Rows = 50>
struct BasicCanvas {
    using Line   = std::array<char, Columns>;
    using Screen = std::array<Line, Rows>;
    struct Coord { size_t x, y; };

    static constexpr size_t rows() { return Rows; }
    static constexpr size_t cols() { return Columns; }

    Screen screen;
    Coord origin;

    BasicCanvas(Coord origin = {Columns/2, Rows/2}) : origin(origin) {
        Line empty;
        std::fill(empty.begin(), empty.end(), ' ');
        std::fill(screen.begin(), screen.end(), empty);
    }

    friend std::ostream& operator<<(std::ostream& os, BasicCanvas const& c) {
        for (auto& line : c.screen) {
            os.write(line.data(), line.size()) << "\n";
        }
        return os;
    }

    Line&       operator[](size_t y)          { return screen.at(screen.size()-(y+1)); }
    Line const& operator[](size_t y) const    { return screen.at(screen.size()-(y+1)); }
    char&       operator[](Coord coord)       { return operator[](coord.y).at(coord.x); }
    char const& operator[](Coord coord) const { return operator[](coord.y).at(coord.x); }

    void axes() {
        for (auto& line : screen)
            line.at(origin.x) = '|';

        auto& y_axis = operator[](origin.y);

        for (auto& cell : y_axis)
            cell = '-';

        y_axis.at(origin.x) = '+';
    }

    template <typename F>
    void plot(F f, double scaleX = 1.0, double scaleY = 1.0) {
        for (size_t x_tick = 0; x_tick < Columns; ++x_tick) {
            auto x = (x_tick * scaleX) - origin.x;
            auto y = f(x);
            auto y_ = derivative(f, x, scaleX/2);

            size_t y_tick = (y / scaleY) + origin.y;
            if (y_tick < Rows)
                operator[]({x_tick, y_tick}) = glyph(y_);
        }
    }

  private:
    template <typename F>
    auto derivative(F const& f, double x, double dx = 0.01) {
        return (f(x+dx)-f(x-dx))/(2*dx);
    }

    char glyph(double tangent) {
        auto angle = atan(tangent);

        while (angle < 0) 
            angle += 2*M_PI;

        int angle_index = 2.0 * angle / atan(1);

        return R"(--/||\--)"[angle_index % 8];
    }

};

int main() {
    using Canvas = BasicCanvas<60, 30>;
    Canvas canvas;
    //canvas.origin = {canvas.cols()/3, canvas.rows()/3};

    canvas.axes();

    canvas.plot([](double x) { return  x; });
    //canvas.plot([](double  ) { return -8; });
    canvas.plot([](double x) { return 3*log(x); });
    canvas.plot([](double x) { return 4*sin(x/2); });
    //canvas.plot([](double x) { return 24*cos(x/12); });

    std::cout << canvas;
}

Prints

                              |             /               
                              |            /                
                              |           /                 
                              |          /                  
                              |         /                  -
                              |        /           -------- 
                              |       /      ------         
                              |      /   ----               
                              |     / ---                   
                              |    /--                      
                              |   --                        
       ---          --\       | /--          --\         /--
      /   \        /          | /  \        /               
                  /    \      |/                \       /   
-----/-----\------------------/|----\------/----------------
                 /      \    /|                  \     /    
    /       \               //|      \    /                 
\               /        \ /  |          /        \   /     
 --/         \--          --/ |       \--          ---      
                         /    |                             
                        /     |                             
                       /      |                             
                      /       |                             
                     /        |                             
                    /         |                             
                   /          |                             
                  /           |                             
                 /            |                             
                /             |                             
               /              |                             

I looked at your 'c' question.

If you want to use a terminal and place chars on the screen, find an ansi terminal emulator for your system, and try Ansi_t:

class Ansi_t   // use ansi features of gnome-terminal
{
   // note: Ubuntu 15.10 gnome-terminal ansi term cursor locations 
   // are 1-based with origin 1,1 at top left corner

   enum ANSI : char { ESC = 27 };

public:

   static inline std::string clrscr(void)
      {
         std::stringstream ss;
         ss << static_cast<char>(ESC) << "[H"   // home
            << static_cast<char>(ESC) << "[2J"; // clrbos
         return(ss.str());
      }

   //              r/c are 0 based------v------v------0 based
   static inline std::string gotoRC(int r, int c)
      {
         std::stringstream ss;
         // Note: row/col of GameOfLife_t is 0 based (as in C++)
         ss << static_cast<char>(ESC) << "[" 
            << (r+1) << ';' << (c+1) << 'H';
         // xy of ansi terminal is 1 based -------^^--------------^^
         return(ss.str());
      }


   // tbr - add more ansi functions as needed

}; // Ansi_t

Typical usage is"

std::cout << Ansi_t::clrscr() << std::flush; // clears the screen

std::cout << Ansi_t::gotoRC(5, 25) << '*' << std::flush;
// position the cursor, and output a single 'dot':

Now use this to draw a line, etc.

Note - On ubuntu, the terminal emulations typically have a hot key to reduce font size - it is "Ctrl minus" for some terminals. Repeated application can reduce the font size to unreadable, so each character looks more and more like a dot.

I have successfully used Ansi_t for GameOfLife ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM