1. 程式人生 > >【續集】 C語言圖形和函式影象的繪製【其他部分 第二章】

【續集】 C語言圖形和函式影象的繪製【其他部分 第二章】

           第二章【續集】 圖形和函式影象的繪製

      圓形和拋物線的繪製

繪製函式影象的第二種方法

同一座標系下繪製多個函式影象(交叉影象)

4、圓形和拋物線的繪製

   有了前面繪製三角函式影象的基礎,圓形和拋物線的繪製應該非常簡單,我們以圓形為例說明:

a、觀察需要繪製的影象


      每行需要輸出兩個點,而且影象左右軸對稱

   b、使用的函式:(x-10)2+(y-10)2=102

   c、輸出21行,行座標間距為1

   d、左半圓的列號:10-sqrt(100-(r-10)*(r-10))

      右半圓的列號:10+sqrt(100-(r-10)*(r-10))

   e、調整輸出行寬比(因螢幕的行寬比不是一比一,因此不進行調整會導致輸出為橢圓)

   按照以上步驟編寫程式碼如下:

 C++ Code By DuanXu-yzc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
 * 繪製圓形(圓心座標(10,10),半徑為10)
 */

#include <stdio.h>
#include <math.h>

int main ( void )
{
    int rows;
    int r;
    int c;
    float adjust;

    rows = 20;
    adjust = 2
.3;   // 螢幕行寬比(輸出螢幕行間距和列間距不同,用adjust作為調整)

    for ( r=20; r>=0; r-- )
    {
        // 左半圓
        for ( c=0; c<adjust*(10-sqrt(100-(r-10)*(r-10))); c++ )
            printf ( " " );
        printf ( "*" );

        // 右半圓
        for ( ; c<adjust*(10+sqrt(100-(r-10)*(r-10))); c++ )
            printf ( " " );
        printf ( "*"
 );

        printf ( "\n" );
    }

    return 0;
}

得到的影象:

拋物線的繪製與圓形極為相似,直接上程式碼和結果:

/*
 * 繪製拋物線
 */
#include <stdio.h>
#include <math.h>

int main ( void )
{
	int rows;
	int r;
	int c;
	double adjust = 2.3;

	rows = 20;

	for ( r=rows; r>=0; r-- )
	{
		for ( c=0; c<adjust*(10-sqrt(5*r)); c++ )
			printf ( " " );
		printf ( "*" );

		for ( ; c<adjust*(10+sqrt(5*r)); c++ )
			printf ( " " );
		printf ( "*" );

		printf ( "\n" );
	}

	return 0;
}

5、繪製函式影象的第二種方法

    到目前為止,我們使用的方法都是逐行輸出,然後通過反函式找到每行需要輸出星號的位置,最後通過輸出相應個數的空格,確定星號的位置,輸出星號。現在介紹另外一種方法,其實與已知的方法很相似,區別在於不需要先輸出空格來確定星號的位置,而是逐個判斷每行中的各列需要輸出的字元是空格還是星號,以圓形為例,每行中,只有列號(調整比例後的)為adjust*(10-sqrt(5*r)和adjust*(10+sqrt(5*r)的位置需要輸出星號,其餘位置均輸出空格。按照這個方法我們重新編寫程式如下:

/*
 * 繪製圓形(圓心座標(10,10),半徑為10)的另一種方法
 */

#include <stdio.h>
#include <math.h>

int main ( void )
{
	int rows;
	int r;
	int c;
	int left;
	int right;
	float adjust = 2.3;

	rows = 20;
	for ( r=rows; r>=0; r-- )
	{
		left = adjust*(10-sqrt(100-(r-10)*(r-10)));	// 左半圓的列號(將浮點轉化為整型)
		right = adjust*(10+sqrt(100-(r-10)*(r-10)));	// 右半圓的列號(將浮點轉化為整型)

		for ( c=0; c<=adjust*20; c++ )
		{
			if ( c == left || c == right )
				printf ( "*" );
			else
				printf ( " " );
		}
		printf ( "\n" );
	}

	return 0;
}
得到影象:

(這個影象與前一種方法繪製的影象有一處明顯不同,為什麼?請讀者思考。)

   6、同一座標系下繪製多個函式影象(交叉影象)

   我們來觀察一下直線和圓在同一座標系下相交的圖形:


    同樣,這個影象也需要逐行輸出,如果按照第一種繪製方法,就需要確定每行中星號的位置,我們發現,影象上半部分的星號依次出現在:左半圓、右半圓、直線,中間部分是:左半圓、直線、右半圓,下半部分是:直線、左半圓、右半圓,這使得我們需要在程式中需要針對不同的行段採取不同的輸出順序,如果影象更復雜的話,這同情況會變得更糟糕。我們再看看第二種繪製方法,這種方法只需要知道哪些地方需要輸出星號,並不需要明確的順序,因此更適合這種交叉影象輸出的情況,下面是繪製直線和圓的程式碼和結果:

/*
 * 同一座標系下繪製圓形和直線
 */
#include <stdio.h>
#include <math.h>

int main ( void )
{
	int rows = 20;
	int r;
	int c;
	int round_l;
	int round_r;
	int line;

	float adjust = 2.3;

	for ( r=rows; r>=0; r-- )
	{
		round_l = adjust*(10-sqrt(100-(r-10)*(r-10)));
		round_r = adjust*(10+sqrt(100-(r-10)*(r-10)));
		line = adjust*r;

		for ( c=0; c<=adjust*20; c++ )
			if ( ( c == round_l  &&  c == line ) ||
			     ( c == round_r  &&  c == line ) )
				printf ( "+" );			// 交叉的點標記'+'

			else if ( c == round_l  ||  c == round_r )
				printf ( "*" );			// 圓形邊界標記'*'

			else if ( c == line )
				printf ( "+" );			// 直線標記'+'

			else
				printf ( " " );			// 其它位置標記空格

		printf ( "\n" );
	}

	return 0;
}

    使用同樣的方法繪製餘弦和正弦的交叉影象如下:

/*
 * 同一座標系中繪製餘弦和正弦影象
 */
#include <stdio.h>
#include <math.h>

#define PI 3.14

int main ( void )
{
	int rows = 20;
	float r;
	int c;
	int sin_1;
	int sin_2;
	int sin_3;
	int sin_4;
	int cos_1;
	int cos_2;

	float adjust = 10;

	for ( r=1; r>0; r-=0.1 )
	{
		sin_1 = adjust * asin(r);
		sin_2 = adjust * (PI-asin(r));
		cos_1 = adjust * acos(r);
		cos_2 = adjust * (2*PI-acos(r));

		for ( c=0; c<=adjust*2*PI; c++ )
			if ( ( c == sin_1  &&  c == cos_1 )  ||
			     ( c == sin_2  &&  c == cos_1 )  ||
			     ( c == sin_1  &&  c == cos_2 )  ||
			     ( c == sin_2  &&  c == cos_2 ) )
				printf ( "+" );

			else if ( c == sin_1  || c == sin_2 )
				printf ( "*" );

			else if ( c == cos_1  ||  c == cos_2 )
				printf ( "+" );

			else
				printf ( " " );

		printf ( "\n" );
	}

	for ( r=0; r>=-1; r-=0.1 )
	{
		sin_3 = adjust * (PI-asin(r));
		sin_4 = adjust * (2*PI+asin(r));
		cos_1 = adjust * acos(r);
		cos_2 = adjust * (2*PI-acos(r));

		for ( c=0; c<=adjust*2*PI; c++ )
			if ( ( c == sin_3  &&  c == cos_1 )  ||
			     ( c == sin_4  &&  c == cos_1 )  ||
			     ( c == sin_3  &&  c == cos_2 )  ||
			     ( c == sin_4  &&  c == cos_2 ) )
				printf ( "+" );

			else if ( c == sin_3  || c == sin_4 )
				printf ( "*" );

			else if ( c == cos_1  ||  c == cos_2 )
				printf ( "+" );

			else
				printf ( " " );

		printf ( "\n" );
	}

	return 0;
}


本章總結

        圖形輸出是C語言初學者經常遇到的問題,實際上大多數簡單的圖形並不需要使用複雜的控制結構,所見即所得的方法更加簡潔,不過對於複雜的圖形或者函式影象,使用控制結構會更好。在使用控制結構輸出圖形時,要把握一個原則:只能逐行輸出,因此,在輸出一行時必須把這一行中所有需要輸出的字元全部輸出,所以,找到每行中各個位置需要輸出的字元(或者說,各字元在每行中的位置)尤為關鍵!我們有兩種方法來確定位置,其中第二種方法更通用簡潔。在輸出圖形時,特別是函式影象,要充分利用對稱、平移關係簡化程式,另外,要注意調節螢幕的行寬比和適當的影象放大,防止影象失真。


宣告

    2、在未經博主斷絮允許的情況下,任何個人和機構都不得以任何理由出版此文章,否則必將追究法律責任!