代码
Polygon.h
/*
* Project: Polygon Class
* File: Polygon.h
* ------------------
*
* @author: Teddy van Jerry
*
* @version: 2021/01/03
* - initial version
*/
///
// NOTE:
// The class name Polygon can be misinterpreted
// as a function defined in C++ standard library.
// So remember to use the namespace tvj.
///
#pragma once
#define _POLYGON_H_
#include <stdio.h>
#include <conio.h>
#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include <sstream> // std::istringstream
#include <Windows.h>
// used when debugging
// #define DEBUG
struct Point
{
double X = 0;
double Y = 0;
};
namespace tvj
{
class Polygon
{
friend std::istream& operator>> (std::istream& in, tvj::Polygon& pol);
public:
// Constuctors will be released in later versions.
//Polygon(Point[]);
//Polygon(std::vector<Polygon>);
double perimeter();
double area();
unsigned edgeNumber();
private:
std::vector<Point> Pt;
double C = 0; // perimeter
double S = 0; // area
bool legal(double xi, double yi);
// add to C when new points added
double _C_(double xi, double yi);
// add to S when new points added
double _S_(double xi, double yi);
};
// input numbers into a vector and test whether it is 'end'
std::vector<double> cinvec_d(std::istream& input, int& index)
{
std::string cin_d;
std::getline(input, cin_d);
std::vector<double> vint;
if (cin_d == "end")
{
index = 1; // 'end' is inputted
return vint;
}
// change string into double
std::istringstream is(cin_d);
double i;
while (is >> i)
vint.push_back(i);
return vint;
}
// used to test whether there are crosses in sides
bool Polygon::legal(double xi, double yi)
{
double epsilon = 1E-12; // allowing for precision in calculation
if (this->Pt.size() >= 3)
{
for (int i = 1; i != this->Pt.size() - 1; i++)
{
for (int j = 0; j != i; j++)
{
// define
double x1 = Pt[i].X, y1 = Pt[i].Y;
double x2 = Pt[j].X, y2 = Pt[j].Y;
double x3 = (Pt.cend() - 1)->X, y3 = (Pt.cend() - 1)->Y;
double x4 = xi, y4 = yi;
double x_t = 0, y_t; // intersection
double denominator, numerator;
// test (using maths methods)
denominator = (y2 - y1) * (x4 - x3) - (y4 - y3) * (x2 - x1);
numerator = (y3 - y1) * (x4 - x3) * (x2 - x1) + x1 * (y2 - y1) * (x4 - x3) - x3 * (y4 - y3) * (x2 - x1);
if (fabs(denominator) < epsilon);
// The requirement has been lowered.
/*
{
if (fabs((yi - y1) * (xi - x2) - (yi - y2) * (xi - x1)) < epsilon) return false;
}
*/
else // fabs(denominator) >= epsilon
{
x_t = numerator / denominator;
// The next two lines are used when debugging
#ifdef DEBUG
std::cout << "\n" << i << "/" << x1 << " " << x_t << " " << j << "/" << x2
<< "\n " << x3 << " " << x4 << std::endl;
#endif // !DEBUG
// This part should be dealt with very carefully
// If x1=x2 or x3=x4, we cannot use x as the sole criterion
// because we cannot see whether the intersection is in the line
// so we have to use y this time
if (x1 == x2)
{
y_t = (y4 - y3) / (x4 - x3) * (x_t - x3) + y3;
if ((y_t - y1) * (y_t - y2) < epsilon && (x_t - x3) * (x_t - x4) < epsilon) return false;
}
else if (x3 == x4)
{
y_t = (y2 - y1) / (x2 - x1) * (x_t - x1) + y1;
if ((x_t - x1) * (x_t - x2) < epsilon && (y_t - y3) * (y_t - y4) < epsilon) return false;
}
else if ((x_t - x1) * (x_t - x2) < epsilon && (x_t - x3) * (x_t - x4) < epsilon) return false;
}
}
}
}
else if (Pt.size() == 2)
{
// if they coincide
if ((fabs(yi - Pt[1].Y) < epsilon && fabs(xi - Pt[1].X) < epsilon)
|| (fabs(yi - Pt[0].Y) < epsilon && fabs(xi - Pt[0].X) < epsilon)) return false;
}
else if (Pt.size() == 1)
{
// if they coincide
if (fabs(Pt[0].X - xi) < epsilon && fabs(Pt[0].Y - yi) < epsilon) return false;
}
return true;
}
// perimeter
double Polygon::_C_(double xi, double yi)
{
if (Pt.size() >= 2)
return sqrt((xi - (Pt.cend() - 2)->X) * (xi - (Pt.cend() - 2)->X) + (yi - (Pt.cend() - 2)->Y) * (yi - (Pt.cend() - 2)->Y));
else return 0;
}
// area
double Polygon::_S_(double xi, double yi)
{
if (Pt.size() >= 2)
// This is a maths formula.
return (xi * ((Pt.cend() - 2)->Y) / 2 - yi * ((Pt.cend() - 2)->X) / 2);
else return 0;
}
double Polygon::perimeter()
{
return this->C;
}
double Polygon::area()
{
return fabs(this->S);
}
unsigned Polygon::edgeNumber()
{
return this->Pt.size();
}
// the class:: used here is to avoid Polygon being interpreted as a function defined in Windows.h
std::istream& operator>> (std::istream& in, tvj::Polygon& pol)
{
int Index = 0;
while (1)
{
std::vector<double> row = cinvec_d(in, Index);
if (Index != 0)
{
// add the last one
pol.C += sqrt((pol.Pt[0].X - (pol.Pt.cend() - 1)->X) * (pol.Pt[0].X - (pol.Pt.cend() - 1)->X)
+ (pol.Pt[0].Y - (pol.Pt.cend() - 1)->Y) * (pol.Pt[0].Y - (pol.Pt.cend() - 1)->Y));
pol.S += pol.Pt[0].X * (pol.Pt.cend() - 1)->Y / 2 - pol.Pt[0].Y * (pol.Pt.cend() - 1)->X / 2;
break;
}
if (row.size() == 2 && pol.legal(row[0], row[1]))
{
Point this_point{ row[0], row[1] };
pol.Pt.push_back(this_point);
pol.C += pol._C_(row[0], row[1]);
pol.S += pol._S_(row[0], row[1]);
}
else
{
// It may have crossed lines or do not have exactly two numbers.
MessageBox(NULL, L"Illegal input of a coordinate.\nTry again!", L"Error", MB_ICONERROR);
}
}
return in;
}
}
// Copyright: 2021 Teddy van Jerry
测试主函数
main.cpp
#include "Polygon.h"
using namespace std;
int main()
{
tvj::Polygon Test1;
// input
cout << "Please input the coordinates of the polygon one in a line:" << endl;
cin >> Test1;
// output
cout << "\n-----------------------" << endl;
cout << "Edge Number: " << Test1.edgeNumber() << endl;
cout << " Perimeter: " << Test1.perimeter() << endl;
cout << " Area: " << Test1.area() << endl;
cout << "-----------------------\nALL RIGHTS RESERVED (C) 2021 Teddy van Jerry" << endl;
return 0;
}
// Copyright: 2021 Teddy van Jerry
输出示例
正确数据的时候:
输入数据存在交点的时候。
分析
- 使用详见头文件的 Note。
- 本头文件由我的博客 【C++ 程序】 多边形面积周长问题 改写而来。
ALL RIGHTS RESERVED © 2021 Teddy van Jerry
欢迎转载,转载请注明出处。