結對項目做業

結對項目做業

項目 內容
這個做業屬於那個課程 班級博客
這個做業的要求在哪裏 做業要求
我在這個課程的目標是 學習軟件工程相關知識,加強本身的開發能力。
這個做業在哪一個具體方面幫我實現目標 學習結對編程的技巧和方法
sample Sample

1、在文章開頭給出教學班級和可克隆的 Github 項目地址(例子以下)。(1')

教學班級:005java

項目地址:https://github.com/harrychen23235/MultiIntersectc++

2、在開始實現程序以前,在下述 PSP 表格記錄下你估計將在程序的各個模塊的開發上耗費的時間。(0.5')

PSP2.1 Personal Software Process Stages 預估耗時(分鐘) 實際耗時(分鐘)
Planning 計劃
· Estimate · 估計這個任務須要多少時間 20 25
Development 開發
· Analysis · 需求分析 (包括學習新技術) 120 300
· Design Spec · 生成設計文檔 30 30
· Design Review · 設計複審 (和同事審覈設計文檔) 20 20
· Coding Standard · 代碼規範 (爲目前的開發制定合適的規範) 40 30
· Design · 具體設計 30 20
· Coding · 具體編碼 120 180
· Code Review · 代碼複審 30 60
· Test · 測試(自我測試,修改代碼,提交修改) 120 120
Reporting 報告
· Test Report · 測試報告 30 30
· Size Measurement · 計算工做量 20 20
· Postmortem & Process Improvement Plan · 過後總結, 並提出過程改進計劃 240 240
合計 820 1025

此次的做業主要難點是新知識的學習,包括dll庫的生成以及qt的學習,其中qt對vs生成dll的不支持也讓我DEBUG了很是長的時間。在架構上此次的程序沿用了以前的架構,所以具體設計時並無花費太多的時間,可是測試時仍然發現了很多的問題。在結對編程上,最大的難點是對雙方的代碼不熟悉同時遠程合做極爲不方便,所以將任務一分爲二,我負責UI開發以及程序實現,另外一位同窗主要負責對於程序功能進行測試。git

3、看教科書和其它資料中關於 Information Hiding,Interface Design,Loose Coupling 的章節,說明大家在結對編程中是如何利用這些方法對接口進行設計的。(5')

信息隱藏、接口設計、鬆耦合都是軟件工程以及面向對象實現的重要方法。目標是使得模塊的調用者不須要關注模塊的細節部分,只須要按照接口說明進行操做就能獲得但願的結果。同時調用者也沒法修改模塊內容,或者反推出模塊源代碼。github

信息隱藏:算法

  • 對於全部私有變量的訪問都是用類所提供的get和set函數,禁止一切對於類中成員變量的直接訪問。
  • 在給出的.h文件中並不包含.cpp定義的全部函數,而是真正須要對外提供的接口函數。

接口設計:編程

  • 一個良好的接口能增長調用者的便利性,同時讓編程者擁有一個良好的編程架構。
  • 本次設計中使用了良好的繼承結構,以shape做爲基類,circle和line做爲shape的子類,同時ray和segment又分別做爲line的子類。實現結構複用。
  • 在生成變量實例時並不直接原地生成,而是在接口中添加工廠模式函數,增長了信息隱藏的同時使得對於對象的生成更加便利。

鬆耦合:架構

  • 核心模塊提供完整的接口函數,使得UI模塊只須要調用接口中給出的函數,例如delete,add操做只須要調用接口函數即能實現,不須要重寫完整的代碼語句。
  • 核心模塊中禁止使用extern類型的變量,防止每一個.h間耦合程度太高,在BUG觸發時致使多個源代碼出現錯誤
  • 當代碼出現錯誤時將錯誤影響範圍控制在函數自己和它的調用者上,盡力減少函數之間的耦合程度。

4、計算模塊接口的設計與實現過程。設計包括代碼如何組織,好比會有幾個類,幾個函數,他們之間關係如何,關鍵函數是否須要畫出流程圖?說明你的算法的關鍵(沒必要列出源代碼),以及獨到之處。(7')

核心模塊的主要功能包括對於圖像的添加,計算交點個數功能。app

一、首先是對於具體節點的處理。創建了自定義的point類,並採用unordered_set結構進行儲存,內部採用相似java的hashset進行儲存。因爲採用的爲自定義類,同時須要對於浮點數double進行hash,所以重寫了equal和hash函數,具體以下所示:函數

unordered_set<Point*, Hash_Point, Equal_Point> g_allpoint;
struct Hash_Point {
    size_t operator()(const class Point* input1)const {
        long temp1 = floor(input1->x);
        long temp2 = floor(input1->y);
        if (abs(input1->x - (temp1+1)) <= EPS) temp1++;
        if (abs(input1->y- (temp2+1)) <= EPS) temp2++;
        return (temp1 + temp2) * 13 + (temp1 * 1000 % 1000 + temp2 * 1000 % 1000);
    }
};
struct Equal_Point {
    bool operator()(const class Point* input1, const class Point* input2)const {
        return abs(input1->x - input2->x) <= EPS && abs(input1->y - input2->y) <= EPS;
    }
};

須要特別注意的是對於double的處理,在試運行時發現無論是floor仍是強制轉換爲int操做,均可能會出現BUG。例若有機率把(double)1轉換成0或者是1,出現二義性。所以在轉換完成以後必須新增一步if判斷操做。犧牲部分效率確保正確性。工具

二、其次是對於圖形類的具體實現以及圖像的添加。對於新增的射線和線段類,讓它們繼承直線類,射線類新增方向屬性以及起始節點,以下所示:

class Seg :public Line {
public:double  mx2, my2, mx1, my1;
      Seg(double input1, double input2, int ifspecial, double input3, double input4, double input5, double input6);
};,而線段類則新增對於2個端點的記錄。以下所示:

class Ray :public Line {
public:double mx1, my1, mx2, my2;
    int direction;//1,2,3,4表示延長到的象限位置,-1表示朝x軸正方向延伸,-2,表示朝x軸負方向延伸,-3表示向y軸正方向延伸,-4表示向y軸負方向延伸
    Ray(double input1, double input2, int ifspecial, double input3, double input4, double input5, double input6);
};

圖形的產生依然使用工廠模式,在其中增長了對於線段和射線類的處理:

Shape* ShapeFactory::GetShape(string type, double temp1, double temp2, double temp3, double temp4) {
          if (type == "L") {
              if (temp1 == temp3) return new Line(temp1, 0, 1);
              else return new Line((temp4 - temp2) / (temp3 - temp1), temp2 - (temp4 - temp2) / (temp3 - temp1) * temp1, 0);
          }
          else if (type == "R") {
              if (temp1 == temp3) return new Ray(temp1, 0, 1, temp1, temp2, temp3, temp4);
              else return new Ray((temp4 - temp2) / (temp3 - temp1), temp2 - (temp4 - temp2) / (temp3 - temp1) * temp1, 0, temp1, temp2, temp3, temp4);
          }
          else if (type == "S") {
              if (temp1 == temp3) return new Seg(temp1, 0, 1, temp1, temp2, temp3, temp4);
              else return new Seg((temp4 - temp2) / (temp3 - temp1), temp2 - (temp4 - temp2) / (temp3 - temp1) * temp1, 0, temp1, temp2, temp3, temp4);
          }
          else if (type == "C") {
              return new Circle(temp1, temp2, temp3);
          }
          else {
          }
          return NULL;}

三、對於具體交點的計算,因爲新增的線段和射線計算交點的方式和直線計算交點的方式基本相同,所以在架構上徹底沿用以前對直線求交點的函數。在交點計算完成後增長一個判斷交點是否在射線或線段上的環節。須要注意的是可能會出現線段或射線首尾相連,出現1個交點的特殊狀況,所以須要對這種狀況進行特殊判斷:

void L2L(Line* input1, Line* input2, unordered_set<Point*, Hash_Point, Equal_Point>& g_allpoint) {
    if (input1->mspecial == 0 && input2->mspecial == 0) {
        if (abs(input1->ma - input2->ma) <= EPS) {
            if (abs(input1->mb - input2->mb) <= EPS)
                L2L_Special(input1, input2, 0, g_allpoint);//對於特殊狀況的判斷處理
            return;
        }
        double x = (input2->mb - input1->mb) / (input1->ma - input2->ma);
        double y = x * input1->ma + input1->mb;
        if (Line_Process(input1, x, y) && Line_Process(input2, x, y))//判斷交點是否在線上
            g_allpoint.insert(new Point(x, y));
        return;
    }
    else if (input1->mspecial == 1 && input2->mspecial == 1) {
        if (input1->ma == input2->ma)
            L2L_Special(input1, input2, 1, g_allpoint);
        return;
    }
    else {
        if (input1->mspecial == 1) {
            double x = input1->ma;
            double y = input1->ma * input2->ma + input2->mb;
            if (Line_Process(input1, x, y) && Line_Process(input2, x, y))
                g_allpoint.insert(new Point(x, y));
        }
        else {
            double x = input2->ma;
            double y = input1->ma * input2->ma + input1->mb;
            if (Line_Process(input1, x, y) && Line_Process(input2, x, y))
                g_allpoint.insert(new Point(input2->ma, input1->ma * input2->ma + input1->mb));
        }
    }
}

對於特殊狀況處理,這部分代碼邏輯較爲複雜。主要思路是判斷位於同一直線上的射線和線段,射線和射線,線段和線段之間究竟是存在一個交點,仍是存在覆蓋區域,致使無限多個交點產生。代碼以下所示:

void L2L_Special(Line* input1, Line* input2, int ifspecial, unordered_set<Point*, Hash_Point, Equal_Point>& g_allpoint) {
    if (input1->mtype == "L" || input2->mtype == "L") throw string("infinite point");
    //若是有一個爲直線一定存在無限多個交點
    else if (input1->mtype == "R" && input2->mtype == "R") {
        if (abs(input1->ma - input2->ma) <= EPS && abs(input1->mb - input2->mb) <= EPS) {
            if (((Ray*)input1)->direction == ((Ray*)input2)->direction) {
                throw string("infinite point");
            }//起始節點相同以及方向相同射線一定有無數多個節點
            else {
                g_allpoint.insert(new Point(input1->ma, input1->mb));
                //起始節點相同以及方向相反射線一定只有一個節點
                return;
            }
        }
        else {
            //起始節點不一樣節點要麼有無數多個節點,要麼沒有節點
            int direction = DirectionGet(((Ray*)input1)->mx1, ((Ray*)input1)->my1, ((Ray*)input2)->mx1, ((Ray*)input2)->my1);
            if (direction == ((Ray*)input1)->direction)throw string("infinite point");
            else return;
        }
    }
    else if (input1->mtype == "R" || input2->mtype == "R") {
        //對於射線和線段的判斷
        Ray* r1;
        Seg* s1;
        if (input1->mtype == "R") {
            r1 = (Ray*)input1;
            s1 = (Seg*)input2;
        }
        else {
            r1 = (Ray*)input2;
            s1 = (Seg*)input1;
        }
        int direction1 = DirectionGet(r1->mx1, r1->my1, s1->mx1, s1->my1);
        int direction2 = DirectionGet(r1->mx1, r1->my1, s1->mx2, s1->my2);
        if (direction1 == r1->direction || direction2 == r1->direction) throw string("infinite point");
        else if (direction1 == 0 || direction2 == 0) g_allpoint.insert(new Point(r1->mx1, r1->my1));
        else return;
    }
    else {
         //對於線段和線段的判斷
        Seg* s1 = (Seg*)input1;
        Seg* s2 = (Seg*)input2;
        double largex1 = s1->mx1 > s1->mx2 ? s1->mx1 : s1->mx2;
        double smallx1 = s1->mx2 > s1->mx1 ? s1->mx1 : s1->mx2;
        double largex2 = s2->mx1 > s2->mx2 ? s2->mx1 : s2->mx2;
        double smallx2 = s2->mx2 > s2->mx1 ? s2->mx1 : s2->mx2;
        if (abs(smallx1 - largex2) <= EPS) {
            if (abs(smallx1 - s1->mx1) <= EPS) g_allpoint.insert(new Point(s1->mx1, s1->my1));
            else g_allpoint.insert(new Point(s1->mx2, s1->my2));
        }
        else if (abs(largex1 - smallx2) <= EPS) {
            if (abs(largex1 - s1->mx1) <= EPS) g_allpoint.insert(new Point(s1->mx1, s1->my1));
            else g_allpoint.insert(new Point(s1->mx2, s1->my2));
        }
        else if (smallx1 > largex2 || smallx2 > largex1) return;
        else throw string("infinite point");
    }
}

5、閱讀有關 UML 的內容。畫出 UML 圖顯示計算模塊部分各個實體之間的關係(畫一個圖便可)。(2’)


對於求交點的函數不太好將其歸爲具體的一類之中,所以不將其做爲具體類的成員函數進行處理,而做爲類c函數提供給接口。

6、計算模塊接口部分的性能改進。記錄在改進計算模塊性能上所花費的時間,描述你改進的思路,並展現一張性能分析圖(由VS 2015/2017的性能分析工具自動生成),並展現你程序中消耗最大的函數。(3')

下表爲優化前的具體花費時間,可見當節點數大於1000後增加速度很是不樂觀:

N 時間(ms)
200 45
400 215
600 605
800 920
1000 1754
2000 8410
3000 30360
4000 83956

可見調用時間最多的是在unordered_set中的hash和equal函數的使用,所以打算着重對這兩個函數進行優化。

對於hash函數將相同變量外提,減小具體代碼量,同時改寫對hash的生成,減小hash衝突:

size_t operator()(const class Point* input1)const {
        //return (int)(((int)input1.x) * 1e6 / 10 + ((int)input1.y) * 1e6 / 10);
        double x = input1->x;
        double y = input1->y;
        long temp1 = (long)floor(x);
        long temp2 =(long) floor(y);
        if (abs(x - ((long long)temp1+1)) <= EPS) temp1++;
        if (abs(y- ((long long)temp2+1)) <= EPS) temp2++;
        std::hash<long> long_hash;
        return long_hash.operator()(temp1)+ long_hash.operator()(temp2);
        
    }

因爲equal函數已經爲最簡形態,沒法進行進一步優化。

優化後的結果爲:

可見hash衝突狀況顯著降低,同時hash和equal調用次數顯著下降。對於4000個圖形時的計算時間下降到14208毫秒,效率大幅提高。

7、看 Design by Contract,Code Contract 的內容,描述這些作法的優缺點,說明你是如何把它們融入結對做業中的。(5')

code contract與design by contract經過前置條件,不變式以及後置條件,同時運用動態檢查以及靜態檢查,使函數和接口的正確性獲得充分的保證

優勢:

  • 相較於天然語言而言表述更加準確可靠,有無二義性。

  • 明確接口的功能。幫助編程者以目標爲導向進行編程,同時幫助調用者可以更全面地瞭解接口的功能

  • 方便DEBUG調試。能夠經過外置的插件快速生成大量測試數據完成DEBUG,比人工測試更加快捷和可靠。

  • 便於程序複用。以後的編程者可以快速清楚代碼的功能,更高效的着手對改寫。

缺點:

  • 對於較爲複雜的程序而言deign by contract約束項內容很是多而且複雜,難以編寫。即便編寫成功也沒法確保它的準確性。
  • 對於輕量級或者容錯率比較高的程序而言code contract顯得比較多餘,有點殺雞用牛刀的意思。

對於JML早在面向對象的課程中就有所涉及。在此次的程序設計中我將UI所需的接口封裝爲5個函數,2個變量實體,並在底層確保以上接口的正確性。使得調用者並不須要瞭解程序運行的所有機制就可以經過調用獲得所想要的效果。

8、計算模塊部分單元測試展現。展現出項目部分單元測試代碼,並說明測試的函數,構造測試數據的思路。並將單元測試獲得的測試覆蓋率截圖,發表在博客中。要求整體覆蓋率到 90% 以上,不然單元測試部分視做無效。(6')

我對本程序的單元測試共分爲4個環節。包括對於程序計算函數的單元測試,對於exe文件的單元測試,對於UI接口的單元測試以及對於錯誤處理的單元測試。

對於計算函數測試部分主要目的是確保底層的準確性,進而確保接口準確性,如下爲部分代碼:

TEST_METHOD(TestMethod20) {
		bool b1 = RangeJudge(-100000);
		bool b2 = RangeJudge(100000);
		bool b3 = RangeJudge(-100001);
		bool b4 = RangeJudge(100001);
		bool b5 = RangeJudge(-99999);
		bool b6 = RangeJudge(99999);
		Assert::AreEqual(b1, true);
		Assert::AreEqual(b2, true);
		Assert::AreEqual(b3, true);
		Assert::AreEqual(b4, true);
		Assert::AreEqual(b5, false);
		Assert::AreEqual(b6, false);

對於exe文件部分測試代碼以下所示:

TEST_METHOD(TestMethod22)
		{
			FILE* stream1;
			FILE* stream2;
			freopen_s(&stream1, "G:\\360MoveData\\Users\\HP\\Desktop\\nt\\input.txt", "w", stdout);
			printf("%d\n", 3);
			printf("L 0 0 1 1\n");
			printf("L 0 0 1 2\n");
			printf("L 0 0 1 3\n");

​		fclose(stdout);
​		PROCESS_INFORMATION ProcessInfo;
​		STARTUPINFO StartupInfo; //入口參數
​		ZeroMemory(&StartupInfo, sizeof(StartupInfo));
​		StartupInfo.cb = sizeof StartupInfo; //分配大小
​		if (CreateProcess("G:\\360MoveData\\Users\\HP\\Desktop\\nt\\IntersectProject.exe", "G:\\360MoveData\\Users\\HP\\Desktop\\nt\\IntersectProject.exe -i G:\\360MoveData\\Users\\HP\\Desktop\\nt\\input.txt -o G:\\360MoveData\\Users\\HP\\Desktop\\nt\\output.txt",
​			NULL, NULL, FALSE, HIGH_PRIORITY_CLASS, NULL,
​			NULL, &StartupInfo, &ProcessInfo))
​		{
​			WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
​			CloseHandle(ProcessInfo.hThread);
​			CloseHandle(ProcessInfo.hProcess);
​		}

​		//WinExec("G:\\360MoveData\\Users\\HP\\Desktop\\nt\\IntersectProject.exe -i G:\\360MoveData\\Users\\HP\\Desktop\\nt\\input.txt -o G:\\360MoveData\\Users\\HP\\Desktop\\nt\\output.txt", SW_HIDE); 
​		int result = 0;
​		FILE* open = fopen("G:\\360MoveData\\Users\\HP\\Desktop\\nt\\output.txt", "r");
​		fscanf(open, "%d", &result);
​		fclose(open);
​		Assert::AreEqual(1, result);

​	};

對於錯誤處理的部分代碼以下所示:

TEST_METHOD(TestMethod46)
	{
		FILE* stream1;
		freopen_s(&stream1, "G:\\360MoveData\\Users\\HP\\Desktop\\nt\\input.txt", "wt", stdout);
		printf("%d\n", 1);
		printf("C 0 0 0\n");

		fclose(stdout);
		PROCESS_INFORMATION ProcessInfo;
		STARTUPINFO StartupInfo; //入口參數
		ZeroMemory(&StartupInfo, sizeof(StartupInfo));
		StartupInfo.cb = sizeof StartupInfo; //分配大小
		if (CreateProcess("G:\\360MoveData\\Users\\HP\\Desktop\\nt\\IntersectProject.exe", "G:\\360MoveData\\Users\\HP\\Desktop\\nt\\IntersectProject.exe -i G:\\360MoveData\\Users\\HP\\Desktop\\nt\\input.txt -o G:\\360MoveData\\Users\\HP\\Desktop\\nt\\output.txt",
			NULL, NULL, FALSE, HIGH_PRIORITY_CLASS, NULL,
			NULL, &StartupInfo, &ProcessInfo))
		{
			WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
			CloseHandle(ProcessInfo.hThread);
			CloseHandle(ProcessInfo.hProcess);
		}

		//WinExec("G:\\360MoveData\\Users\\HP\\Desktop\\nt\\IntersectProject.exe -i G:\\360MoveData\\Users\\HP\\Desktop\\nt\\input.txt -o G:\\360MoveData\\Users\\HP\\Desktop\\nt\\output.txt", SW_HIDE); 
		char result[40];
		FILE* open = fopen("G:\\360MoveData\\Users\\HP\\Desktop\\nt\\output.txt", "r");
		fgets(result, 40, open);
		fclose(open);
		Assert::AreEqual("radius must be greater than 0\n", result);

	};

對於UI接口測試的代碼以下所示:

TEST_METHOD(TestMethod53) {
		Add_Diagram("C 1 0 2 0", 1);
		Add_Diagram("C 2 2 1 0", 1);
		Add_Diagram("C 3 -2 6 0", 1);
		Add_Diagram("L -1 4 4 -1", 1);
		Calculate();
		Assert::AreEqual(6, (int)myallpoint.size());
		Clear();
		Calculate();
		Assert::AreEqual(0, (int)myallpoint.size());
		Add_Diagram("C 1 0 2 0", 1);
		Add_Diagram("C 2 2 1 0", 1);
		Add_Diagram("C 3 -2 6 0", 1);
		Add_Diagram("L -1 4 4 -1", 1);
		Calculate();
		Assert::AreEqual(6, (int)myallpoint.size());
		Sub_Diagram("C 1 0 2 0", 1);
		Sub_Diagram("C 2 2 1 0", 1);
		Sub_Diagram("C 3 -2 6 0", 1);
		Sub_Diagram("L -1 4 4 -1", 1);
		myallpoint.clear();
		Calculate();
		Assert::AreEqual(0, (int)myallpoint.size());
	}

總共進行了52組不一樣的測試,且測試覆蓋率達到了90%以上,整體上知足了課程要求。

9、計算模塊部分異常處理說明。在博客中詳細介紹每種異常的設計目標。每種異常都要選擇一個單元測試樣例發佈在博客中,並指明錯誤對應的場景。(5')

錯誤類型 輸入(其中一種) 描述 輸出(輸出在文件中)
線重合 2 L 0 0 1 1 L 0 0 -1 -1 線性圖形有重疊部分,致使無限多個交點 infinite point
圓的重合 2 C 1 1 1 C 1 1 1 兩個圓圓心和半徑相等 same circle error
輸入點重合 1 L 1 1 1 1 線性圖形2個輸入點徹底相同 same point in a line
座標超限 1 L 10000 1000001 1000002 1000003 輸入點座標超過限制 coordinate out of range
半徑小於0 1 C 0 0 -1 輸入圓的半徑小於-1 radius must be greater than 0

10、界面模塊的詳細設計過程。在博客中詳細介紹界面模塊是如何設計的,並寫一些必要的代碼說明解釋實現過程。(5')

本程序使用QT完成界面模塊的開發工做。結合以前計算模塊給出的接口,同時QT模塊中使用了Qcustomplot模組幫助進行了繪畫功能的實現。整體上界面由一個主界面和子界面組成。主界面中給出了7種功能,包括從文件中導入圖形,手動輸入圖形,手動刪除圖形,繪製圖形,給出交點個數,清零,退出功能。

爲了實現各分功能,在主界面程序中定義了所須要相應的函數,以及對應的信號槽,以下圖所示:

private slots:
    void on_input_clicked();
    void F_INPUT();
    void FILE_INPUT();
    void RESULT();
    void CLEAR();
    void SHOWDELETE();
    void myPaint();
    
    
 ui->setupUi(this);
    connect(ui->quit, SIGNAL(clicked()), this, SLOT(close()));
    connect(ui->input,SIGNAL(clicked()),this,SLOT(F_INPUT()));
    connect(ui->load,SIGNAL(clicked()),this,SLOT(FILE_INPUT()));
    connect(ui->show_result,SIGNAL(clicked()),this,SLOT(RESULT()));
    connect(ui->clear,SIGNAL(clicked()),this,SLOT(CLEAR()));
    connect(ui->delete_2,SIGNAL(clicked()),SLOT(SHOWDELETE()));
    connect(ui->draw,SIGNAL(clicked()),this,SLOT(myPaint()));

如下爲自定義輸入界面以及具體的函數,函數中使用了計算模塊給出的接口Add_Diagram(),實現了圖形的增長操做:

void Form::hand_out(){
string str=ui->first_edit->text().toStdString();
     const char*  ch=str.c_str();
    if(temp==NULL){
        ui->first_edit->setText("illegal input");
    }
    else{
    Add_Diagram((char*)ch,1);
    ui->first_edit->setText("successful insert");}
    //close();}

}

圖像繪製部分的函數較爲複雜,使用了第三方庫Qcustomplot完成。因爲繪製部分偏向於UI部分單獨完成,所以在UI中定義了一系列的函數幫助進行繪圖工做:

void paint::paintEvent(QPaintEvent *){

    QCustomPlot *customPlot = ui->qcustomPlot;
       customPlot->clearItems();
       customPlot->clearGraphs();
       ui->qcustomPlot->addGraph();
       ui->qcustomPlot->graph(0)->setLineStyle(QCPGraph::LineStyle::lsNone);
       ui->qcustomPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
       ui->qcustomPlot->axisRect()->setupFullAxesBox();
       ui->qcustomPlot->rescaleAxes();
       ui->qcustomPlot->xAxis->setRange(-100000,100000);
        ui->qcustomPlot->yAxis->setRange(-100000,100000);
       for (int i = 0 ; i <(int) myallshape.size();i++){
           double x1,y1,x2,y2;
           string type=myallshape.at(i)->mtype;
            if(type=="R"){
           Ray* myray=(Ray*)myallshape.at(i);
           x1= myray->mx1;
           y1 = myray->my1;
           x2 = x1+myray->mx2;
           y2 = y1+ myray->my2;
           paintRay(customPlot,x1,y1,x2,y2);
           }
           else if(type=="S"){
                Seg* myray=(Seg*)myallshape.at(i);
                x1= myray->mx1;
                y1 = myray->my1;
                x2 = x1+myray->mx2;
                y2 = y1+ myray->my2;
                paintSegment(customPlot,x1,y1,x2,y2);
            }
            else if(type=="L"){
                 Line* myline=(Line*)myallshape.at(i);
                  if(myline->mspecial==0){
                      x1=1;
                      y1=myline->ma+myline->mb;
                      x2=2;
                      y2=2*myline->ma+myline->mb;
                      paintLine(customPlot,x1,y1,x2,y2);
                  }
                  if(myline->mspecial==1){
                      x1=myline->ma;
                      y1=1;
                      x2=myline->ma;
                      y2=2;
                      paintLine(customPlot,x1,y1,x2,y2);
                  }
            }
                  else if(type=="C"){
                   Circle* mycircle=(Circle*)myallshape.at(i);
                   x1=mycircle->mx;
                   y1=mycircle->my;
                   x2=mycircle->mr;
                   paintCircle(customPlot,x1-x2,y1+x2,x1+x2,y1-y2);
                  }


           if (i%5 == 0)   customPlot->replot();
       }

       customPlot->replot();

}

其餘部分因爲篇幅有限,不給出具體UI圖片,只給出具體函數代碼:

刪除操做:

void mydelete::delete_this(){
    string str=ui->lineEdit->text().toStdString();
    const char*  ch=str.c_str();
    Sub_Diagram((char*)ch,1);
   ui->lineEdit->setText("successful delete");
}

求解交點個數並顯示:

result::result(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::result)
{
    ui->setupUi(this);
    myallpoint.clear();
    Calculate();
   QStandardItemModel* ItemModel = new QStandardItemModel(this);
    int result=myallpoint.size();
    QString string = QString::number(result);
      QStandardItem *item = new QStandardItem(string);
      ItemModel->appendRow(item);
      ui->listView->setModel(ItemModel);
     //ui->listView->setFixedSize(600,600);
}

11、界面模塊與計算模塊的對接。詳細地描述 UI 模塊的設計與兩個模塊的對接,並在博客中截圖實現的功能。(4')

經過計算模塊事先給出的接口,UI模塊只須要調用這些接口就可以實現求解交點、增長圖形、刪除圖形、清空和處理文件輸入的操做,給出的接口以下所示:

extern vector <Shape*> myallshape;
extern unordered_set<Point*, Hash_Point, Equal_Point> myallpoint;
void Add_Diagram(char* input1, int ifoutsource);
void Sub_Diagram(char* input2, int ifoutsource);
void File_InputProcess();
void Clear();
void Calculate();

UI程序經過引入dll程序以及調用.h文件就能快速實現所須要的一系列操做。

  • load from file中調用了File_Inputprocess()接口函數完成圖形導入操做
  • directly input中調有Add_Diagram()完成手動圖形導入操做
  • show point number經過Calculate()完成交點計算操做
  • delete經過調用Sub_Diagram()完成圖形刪除操做
  • clear經過調用Clear()完成數據清空操做
  • draw操做調用了myallshape數據變量,進行圖像繪製

12、描述結對的過程,提供兩人在討論的結對圖像資料(好比 Live Share 的截圖)。關於如何遠程進行結對參見做業最後的注意事項。(1')

十3、看教科書和其它參考書,網站中關於結對編程的章節,說明結對編程的優勢和缺點。同時描述結對的每個人的優勢和缺點在哪裏(要列出至少三個優勢和一個缺點)。(5')

結對編程 結對夥伴
優勢 一、持續代碼複審,減小BUG產生。二、互相學習代碼風格,可以糾正本身自己的錯誤。三、可以讓雙方更高效地編程,增長工做效率。 代碼能力較爲熟練。 比較仔細,可以快速找到程序中的BUG。
缺點 一、磨合和交流都須要花費必定的時間。二、結對夥伴的能力直接影響了編程的質量。三、容易出現二人之間地位不平衡的狀況。 吧不太擅長測試,同時編寫程序BUG較多,而且交流不太主動。 編碼能力不強。

附加題部分

已完成對圓的處理(具體實現略),交換程序部分並無完成。

相關文章
相關標籤/搜索