#include <math.h>
#include <iostream>
using namespace std;
void graph(){
int x=140;
int y=20;
double convert = 3.141592/180;
cout<<"amplitude, frequency\n";
double amp,freq;
cin>>amp>>freq;
for(int i=0; i<y; i ){
for(int j=0; j<x; j ){
if(i==nearbyint(3*amp*cos(5*freq*j*convert)) 10){
cout<<"*";
}
else if(i==y/2){
cout<<"-";
}
else if(j==x/2){
cout<<"|";
}
else{
cout<<" ";
}
}
cout<<endl;
}
}
int main(){
graph();
return 0;
}
when the code is run, the graph will do fine until you start inputting numbers above 4, where then the line will start having large spaces in between the *'s in the y axis.
What is the simplest way I can fill in the gaps between the *s
The equation is multiplied with 3 and 5 for formatting purposes.
CodePudding user response:
I can think of some ways to do this.
The easiest way is just to check against the interval (j-0.5,j 0.5)
by replacing the test
if(i==nearbyint(3*amp*cos(5*freq*j*convert)) 10){
cout<<"*";
}
with
void graph( double amp, double freq ){
int x=140;
int y=20;
double convert = 3.141592/180;
for(int i=0; i<y; i ){
for(int j=0; j<x; j ){
double jx = j;
double v1 = 3*amp*cos(5*freq*(jx-0.5)*convert) 10;
double v2 = 3*amp*cos(5*freq*(jx 0.5)*convert) 10;
if( ((i>=v1)&&(i<=v2)) || ((i<=v1)&&(i>=v2))){
cout<<"*";
}
else if(i==y/2){
cout<<"-";
}
else if(j==x/2){
cout<<"|";
}
else{
cout<<" ";
}
}
cout<<endl;
}
}
Godbolt Link: https://godbolt.org/z/G47WjqafP
This results in
Program stdout
|
|
* * * * * * * * * * | * * * * * * * * * *
* * * * * * * * * * | * * * * * * * * * *
* * * * * * * * * * | * * * * * * * * * *
* * * * * * * * * * | * * * * * * * * * *
* * * * * * * * * * | * * * * * * * * * *
* * * * * * * * * * | * * * * * * * * *
* * * * * * * * * * | * * * * * * * * *
* * * * * * * * * * | * * * * * * * * *
----*------*------*------*------*-------*------*------*------*------*-------*------*------*------*------*-------*------*------*------*------
* * * * * * * * * *| * * * * * * * * *
* * * * * * * * * *| * * * * * * * * *
* * * * * * * * * *| * * * * * * * * *
* * * * * * * * * *| * * * * * * * * *
* * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * *
* * * * * * * * * |* * * * * * * * * *
|
But the coolest I could find for a 10 minute Blitz code project is an approach where you can use something like the Bresenham line algorithm. Bresenham will guarantee that the space between two adjacent point while drawing a line is maximum one pixel, which is exactly what you are asking.
I could apply Bresenham straight to your algorithm but why not extend this to make it more generic? I'm already wasted so here we go...
First you need to define a viewport for your graph that will map between fictitious coordinates to screen coordinates.
struct Extents {
double x[2];
double y[2];
};
Second you will need a class to hold a virtual screen with a character buffer inside and some way to clear it up.
This map will contain characters that will be printed out later.
template< int WIDTH, int HEIGHT >
struct Graph {
Extents ext;
char map[WIDTH][HEIGHT];
void clear() {
memset( map, ' ', WIDTH*HEIGHT );
}
void print() {
for ( int i=0; i<HEIGHT; i ) {
for ( int j=0; j<WIDTH; j ) {
std::cout << map[j][HEIGHT-i-1];
}
std::cout << std::endl;
}
}
};
Then you need some methods to convert from viewport to screen and vice versa
double from_screen_x( int j ) {
return ext.x[0] (j*(ext.x[1]-ext.x[0]))/WIDTH;
}
int from_viewport_x( double x ) {
return (x-ext.x[0])/(ext.x[1]-ext.x[0])*WIDTH;
}
int from_viewport_y( double y ) {
return HEIGHT*((y-ext.y[0])/(ext.y[1]-ext.y[0]));
}
Then once we have a way to convert from (x,y)
to (i,j)
screen coordinates, we can easily 'plot' a single point with a given character.
void point( int x, int y, char ch ) {
if ( (x>=0) && (x<WIDTH) ) {
if ( (y>=0) && (y<HEIGHT) ) {
map[x][y] = ch;
}
}
}
Now the workhorse of the entire class - the Bresenham algorithm which you can find on Wikipedia
void line( int x0, int y0, int x1, int y1, char ch ) {
int dx = abs(x1 - x0);
int sx = x0 < x1 ? 1 : -1;
int dy = - int( abs(y1 - y0) );
int sy = y0 < y1 ? 1 : -1;
int error = dx dy;
while( true ) {
point(x0, y0, ch);
if ( (x0 == x1) && (y0 == y1) ) break;
int e2 = 2 * error;
if ( e2 >= dy ) {
if (x0 == x1) break;
error = error dy;
x0 = x0 sx;
}
if (e2 <= dx) {
if (y0 == y1) break;
error = error dx;
y0 = y0 sy;
}
}
}
Then we need the high level functions: one to draw the axis and another to plot the chart.
The axis is easy - just find the screen coordinates of the origin (0,0)
and plot it on our 'map'.
void axis() {
int i = from_viewport_y(0);
for ( int j=0; j<WIDTH; j ) {
point(j,i,'-');
}
int j = from_viewport_x(0);
for ( int i=0; i<HEIGHT; i ) {
point(j,i,'|');
}
}
For the chart algorithm I decided to go fancy and using a templated function that will allow us to externalize the actual computation.
This algorithm will sweep the x axis in screen space, map into the viewport, compute the point in virtual coordinates and bring them back to the actual (i,j)
screen coordinates. Once we have obtained the first point we can from the 2nd point call the line algorithm between the old and the new point and keep iterating until the end.
template< typename Fn >
void chart( char ch, Fn&& fn ) {
int lasti = 0;
for ( int j=0; j<WIDTH; j ) {
double x = from_screen_x( j );
int i = from_viewport_y( fn(x) );
if ( j>0 ) {
line( j-1,lasti, j, i, ch );
}
lasti = i;
}
}
Now we are left with just the business logic. Let's create a cosine functor that will give us a cosine function with a given amplitude and frequency.
struct CosFn {
double amp;
double freq;
double operator()( double x ) {
return amp*cos( freq*x );
}
};
Now we just need to put everything together and plot it all
int main() {
Extents ext{{-1,1},{-1,1}};
Graph<120,20> g{ext};
g.clear();
g.axis();
g.chart( '.', CosFn{1,2} );
g.chart( '^', CosFn{0.75,4} );
g.chart( '*', CosFn{0.5,8} );
g.print();
}
This will produce:
Program stdout
.............|.............
...... | ......
.... ^^^^^^^^^^^ ....
.... ^^^^ | ^^^^ ....
.... ^^^ * ^^^ ....
********* ... ^^^ ****|**** ^^^ ... *********
*** ** ... ^^ ** | ** ^^ ... ** ***
* .**. ^^ ** | ** ^^ .**. *
** ... ** ^^ ** | ** ^^ ** ... **
--*----------...--------*------------^^----------*----------|----------*----------^^------------*--------...----------*-
** ... ** ^^ ** | ** ^^ ** ... *
... * ^^ * | * ^^ * ...
... ** ^^ ** | ** ^^ ** ...
... ^** ** | ** **^ ...
^ ^^^ ********** | ********** ^^^
^^^ ^^^ | ^^^ ^^^
^^^^ ^^^^ | ^^^^ ^^^^
^^^^^^^^^^^ | ^^^^^^^^^^^
|
|
Godbolt link: https://godbolt.org/z/P3Wx1vE7s