C++:Rectangle---一個經典練習題
1.實現項目Rectangle :main.cpp 、Rectangle.cpp 、 Rectangle.h
首先我們需要表示出Rectangle的四個角【即四個x,y坐標】
這一步我們可以使用pair<double,double>實現,也可以自己寫一個Point類
為了方便,我使用pair
2.實現功能:
(1)計算length 、 width 、 area
(2)判斷是不是square、rectangle
(3)打印圖像
(4)旋轉圖像
(5)放縮圖像
3.實現步驟分析:
(1)計算長+寬+面積: 步驟簡單,略過
(2)判斷是不是square、rectangle
那么直接進入如何判斷rectangle
條件一:一個矩形,它的對角線必然相等
條件二:一個矩形,它的對邊相等
條件三:它不能是一條線
bool MyRectangle::Rectangle::isRectangle( ) const { // 判斷對角線是否相等 and 對邊是否相等 bool ans = distance(point[0],point[3]) == distance(point[1],point[2]); ans &= distance(point[2],point[0]) == distance(point[3],point[1]); ans &= distance(point[2],point[3]) == distance(point[1],point[0]); for(int i = 0;i < 3;i++){//判斷是不是有重合點 if(point[i].fi==point[i+1].fi&&point[i].se==point[i+1].se) return false; } return ans; }
判斷正方形直接加上鄰邊相等條件即可!
(3)打印圖像
首先想到,打印循環的點都是int類型的,但是矩形是double類型的,那么必然會導致顯示不完全的情況,在此說明了。
對於一個雙重for循環打印,我們考慮對於每一個{i,j}賦予一個屬性:在/不在 矩形內部
所以我寫了一個inRectangle的判斷函數,這個判斷函數也是很好寫的。
【詳見https://blog.csdn.net/dapengbusi/article/details/50516126】
就是利用向量的叉乘判斷一個點是不是在舉行外部【特判四個角的情況】
bool MyRectangle::Rectangle::inRectangle(float x,float y) const { const double eps = 0.3; // 這個精度控制真的難受。。。。 for(PFF it:point){ if(fabs(x-it.fi) < eps && fabs(y-it.se) < 1e-4) return true; } PFF p = make_pair(x,y); for(int i = 0;i < 4;i++) { if( cross(point[i+1]-point[i],p-point[i]) > eps ) return false; } return true; }
(4)旋轉嘛,也很簡單,就是一個旋轉公式。
【詳細請見https://blog.csdn.net/qq_36797743/article/details/85680195】
void MyRectangle::Rectangle::rotateRectangle(float deg) { vector<PFF>temp; for(PFF&it:point){ //旋轉公式 float y = ( (it.se-o.se)*cos(deg) + (it.fi-o.fi)*sin(deg) ) + o.se; float x = ( (it.fi-o.fi)*cos(deg) - (it.se-o.se)*sin(deg) ) + o.fi; if(x < 0 || x > 25 || y < 0 || y >25){ cerr<<"旋轉失敗"<<endl; return ; } temp.push_back(make_pair(x,y)); } point.clear(); point = temp; }
(5)放縮,添加一個比例變量,但是很難做到定點放縮【暫且不談了吧】
4. 總結:
由於這個是黑框程序,所以只能使用字符 '.' 代替實心的矩形,如果使用其它的圖形庫: ege , esayX ,MFC的gdiplus等來編寫,當然也可以基於QT寫一個,語法也是C++的。
5.錯誤反省:
(1)實現過程中發現光標很煩人地閃來閃去。於是加了一段代碼隱藏光標,但是windows.h頭文件與Rectangle類是沖突的,那么我們怎么辦呢?
為Rectangle加一個命名空間即可,但是這樣的話,在命名空間外部定義的函數,也需要加上一個MyRectangle::作用域符號,然后創建對象時也需要加一個MyRectangle::
(2)極角排序還需要加強練習啊!
文件:

#include <iostream> #include <windows.h> #include "Rectangle.h" using namespace std; #define mp make_pair int main() { //隱藏光標 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); // 得到控制台的句柄 CONSOLE_CURSOR_INFO CursorInfo; // 建立光標對象 GetConsoleCursorInfo(handle,&CursorInfo); // 得到光標對象的信息 CursorInfo.bVisible = false; // 設置bvisible為 false SetConsoleCursorInfo(handle,&CursorInfo); // 然后設置即可! //Rectangle r({1,1},{15,1},{1,15},{14,14}); //最后一個點錯誤,不能形成矩形 const float pi = acos(-1); //隱藏光標之后,發現windows與Rectangle類起沖突 //只能使用命名空間解決問題了【代碼量加大。。】 MyRectangle::Rectangle R(mp(1,1),mp(10,1),mp(1,15),mp(10,15)); R.print(); R.rotateRectangle(); system("pause"); R.print(); for(int i = 1;i < 10;i++) { R.rotateRectangle(); R.print(); } const double deg = 45.0/180.0*pi;//轉45°,轉換成rad單位,效果最好 R.setRectangle(mp(10,10),mp(15,10),mp(10,11),mp(15,11)); R.print(); R.rotateRectangle(); system("pause"); R.print(); for(int i = 1;i < 10;i++) { R.rotateRectangle(deg); R.print(); } //斜着的 R.setRectangle(mp(1,3),mp(3,1),mp(4,2),mp(2,4)); R.print(); //角度不宜太小,否則可能顯示同一個圖形 / 或者圖像顯示不全 for(int i = 1;i < 10;i++) { R.rotateRectangle(deg); R.print(); } return 0; }

/* 作者:Pigeon 時間:2021 功能:作業 實現方法:寫 C++ */ #include <iostream> #include <vector> #include <algorithm> #include <cmath> #include <stdexcept> using namespace std; #include "Rectangle.h" typedef pair<float,float> PFF; #define fi first #define se second // the pre definition //三個輔助函數 float distance(PFF a,PFF b) { float dx = a.fi - b.fi; float dy = a.se - b.se; return sqrt(dx*dx+dy*dy); } //為了方便,寫一個pair<float,float>中的operator-運算符 PFF operator-(PFF a,PFF b) { return {a.fi-b.fi,a.se-b.se}; } float cross(PFF a,PFF b)//傳入兩個向量 { return ( b.se * a.fi ) - ( a.se * b.fi ); } MyRectangle::Rectangle::Rectangle(PFF a,PFF b,PFF c,PFF d):rectChar('.'),borderChar('#') { setRectangle(a,b,c,d); //ctor } void MyRectangle::Rectangle::setRectangle(PFF a,PFF b,PFF c,PFF d) { point.clear();// 必須要清空之前的點 point.push_back(a); point.push_back(b); point.push_back(c); point.push_back(d); sort(point.begin(),point.end()); //判斷是不是在 0~20內 const float eps = 1e-9;//精度損失 for(PFF it:point){ if(it.fi > 20 || it.fi < eps) throw invalid_argument(" invalid x coordinate , x must be 0~20 !"); if(it.se > 20 || it.se < eps) throw invalid_argument(" invalid y coordinate , y must be 0~20 !"); } if(this->isRectangle() == 0){ throw invalid_argument(" invalid x,y coordinate , can't make a rectangle!"); } //確認是一個矩形,完成后續初始化 //對角線中心 o = make_pair((point[0].fi+point[3].fi)/2,(point[0].se+point[3].se)/2); //冒泡排序進行極角排序 for(int i = 0;i < 4;i++) { for(int j = i;j+1 < 4;j++) { if(cross(point[j]-o,point[j+1]-o) > 0) swap(point[j],point[j+1]); } } point.push_back(point[0]); //初始化寬 、 長 width = min(distance(point[0],point[1]),distance(point[1],point[3])); length = max(distance(point[0],point[1]),distance(point[1],point[3])); } bool MyRectangle::Rectangle::isRectangle( ) const { // 判斷對角線是否相等 and 對邊是否相等 bool ans = distance(point[0],point[3]) == distance(point[1],point[2]); ans &= distance(point[2],point[0]) == distance(point[3],point[1]); ans &= distance(point[2],point[3]) == distance(point[1],point[0]); for(int i = 0;i < 3;i++){ if(point[i].fi==point[i+1].fi&&point[i].se==point[i+1].se) return false; } return ans; } bool MyRectangle::Rectangle::isSquare( ) const { if( isRectangle( ) && getWidth() == getLength() )//是矩形而且邊長相等 return true; return false; } float MyRectangle::Rectangle::getWidth( ) const // 短軸距離 { return width; } float MyRectangle::Rectangle::getLength( ) const // 長軸距離 { return length; } float MyRectangle::Rectangle::getArea( ) const { return getLength()*getWidth(); } void MyRectangle::Rectangle::setFillCharacter( const char&ch ) { rectChar = ch; } void MyRectangle::Rectangle::setPerimeterCharacter( const char&ch ) { borderChar = ch; } bool MyRectangle::Rectangle::inRectangle(float x,float y) const { const double eps = 0.3; // 這個精度控制真的難受。。。。 for(PFF it:point){ if(fabs(x-it.fi) < eps && fabs(y-it.se) < 1e-4) return true; } PFF p = make_pair(x,y); for(int i = 0;i < 4;i++) { if( cross(point[i+1]-point[i],p-point[i]) > eps ) return false; } return true; } bool MyRectangle::Rectangle::onBorder(float x,float y)const { const float eps = 1e-7; return( fabs(x) < eps || fabs(y) < eps || fabs(y-25) < eps || fabs(x-25) < eps ); } void MyRectangle::Rectangle::print( ) const { system("cls"); for(int i = 0;i <= 25;i++) { for(int j = 0;j <= 25;j++) { if(onBorder(i,j))cout<<borderChar<<" "; else if(inRectangle(i,j))cout<<rectChar<<" "; else cout<<" "; } cout<<endl; } } void MyRectangle::Rectangle::rotateRectangle(float deg) { vector<PFF>temp; for(PFF&it:point){ //旋轉公式 float y = ( (it.se-o.se)*cos(deg) + (it.fi-o.fi)*sin(deg) ) + o.se; float x = ( (it.fi-o.fi)*cos(deg) - (it.se-o.se)*sin(deg) ) + o.fi; if(x < 0 || x > 25 || y < 0 || y >25){ cerr<<"旋轉失敗"<<endl; return ; } temp.push_back(make_pair(x,y)); } point.clear(); point = temp; } MyRectangle::Rectangle::~Rectangle() { //dtor }

#ifndef RECTANGLE_H #define RECTANGLE_H #include <iostream> #include <vector> #include <cmath> using namespace std; namespace MyRectangle { typedef pair<float,float> PFF; class Rectangle { public: Rectangle( PFF L1 = {1,1},PFF L2 = {1,20}, PFF R1 = {20,1}, PFF R2 = {20,20} ); virtual ~Rectangle( ); //check function bool isRectangle( ) const; bool isSquare( ) const; bool inRectangle(float, float) const;//檢驗這個點在不在矩形內 bool onBorder(float, float) const; //檢驗這個點是不是邊界 //get function float getWidth( ) const; float getLength( ) const; float getArea( ) const; //set Function void setRectangle(PFF,PFF,PFF,PFF); void setFillCharacter(const char&); void setPerimeterCharacter(const char&); //print function void print( ) const; //加分函數 void rotateRectangle(float = 1.570796326794896619);//默認旋轉: pi/2 /* * 支持旋轉一定角度 * 默認 “定點” 為對角線交點 * 確定旋轉的 “定點” * 先檢驗轉換之后 矩形是否合法 再進行旋轉 * 所有點根據 “定點” 來旋轉 */ private: vector<PFF> point; PFF o;// 對角線交點 float width , length; char rectChar , borderChar; }; } #endif // RECTANGLE_H