描述任意多边形的类及计算其面积和周长

时间:2023-01-08 10:54:29

这是一个可以在平面坐标系中表示 任意多边形并且计算其面积和周长的类。 不过目前只能在第一象限计算。使用时较简便,只要把多边形的各个顶点传进去就可以了 (不用按顺序)
(其中面积的计算是参考 https://blog.csdn.net/hemmingway/article/details/7814494的公式)

程序中附带包含了point 类,line 类。
      point 类:
               计算点点距离的函数:
double distancebetweenpoint(const point &a, const point &b);

               计算点到线距离的成员函数:
double distancetoline(const line &line)const;


       line 类:
               获得直线标准方程的函数:
void getStandardEquation(double &A, double &B, double &C)const;//get the standard equation of line( Ax+By+C=0)

               计算直线长度的成员函数:

double length()const;
               判断两条直线是否相交的成员函数:
              
bool line::iflinemeet(const line &other)const//return a bool value represent whether this line meets the other line (1 stand for yes, 0 for no)

(第一次用不是很会排版)


以下是使用样例:
	point a(3, 6), b(3, 3), c(0, 5),d(5,5),e(10,5),f(5,8),g(10,8);
	point v[4] = { e,f,g,d};
	polygon m(4, v);
	cout << "  area is " << m.area() << "  perimeter is " << m.perimeter()<<endl;

area(面积)和perimeter(周长)是double类型的。

以下是头文件中类的声明等等:
#pragma once
#include<iostream>
using namespace std;

#define PI 3.1415926535
enum errortype { noside,cantcalculate };//the type in exceptions class which may occured in shape's functions


class line;
class point;
class error;

//the exception class
class error {
private:
	errortype eor;
public:
	error(errortype e) :eor(e) {}//constructor
	const errortype what()const { return eor; }//for inquiry
};

//class point used to represent a point in a rectangular coordinate system
class point {
	friend double distancebetweenpoint(const point &a, const point &b);//calculate the distance between two difference point(which isn't a member function of point)
public:
	double x;
	double y;
	point(double setx = 0, double sety = 0) { x = setx; y = sety; }//constructor
	double distancetoline(const line &line)const;//calculate the distance between this point and a line
	bool operator==(const point &ro)const;//compare by the x and y of the point
	void show()const { cout << "(" << x << "," << y << ")"; }//a simple output function for test
};

const point O(0, 0);//the origin in the rectangular coordinate system

//class line used to represent a line in a rectangular coordinate system
//with two different point, which may used to derive a class vector(for now, the difference between start point and end point doesn't make any sense)
class line {
	friend bool compareSlope(const line &l, const line &r);//compare(>) two lines' slope( no numercially, but geometrically)
public:
	point start;
	point end;
	line():start(0,0),end(1,0){}//default constructor which construct the line which go through (0,0) and (1,0)
	line(point startpoint, point endpoint) { start = startpoint; end = endpoint; }//constructor( construct with any two point on the line)
	void getStandardEquation(double &A, double &B, double &C)const;//get the standard equation of line( Ax+By+C=0)
	double length()const;//return the length of line
	bool iflinemeet(const line &other)const;//return a bool value represent whether this line meets the other line (1 stand for yes, 0 for no(coincide situation included))
};

//class polygon can represent arbitrary planar polygon in the rectangular coordinate system( in the first quadrant)
class polygon {
protected:
	point originalvertex;//the vertex of polygon which is the closest to original point
	//( if this shape doesn't have any vertex, then originalvertex is the center point of the shape
	point *vertexes;//save the vertexes of the polygon( the order of the vertexes follow the anti-clockwise and begin in orginalvertex)
	line *sides;//save the sides of the polygon( follow the same rule of the vertexes) 
	unsigned int sidenum;//the number of the polygon's sides
	void setoriginalvertex(point ov);//reset the originalvertex of the polygon

public:
	polygon( unsigned int numofside=0);//contructor which only set number of sides, thus its *sides and *vertexes is null pointer(originalvertex is (0,0))
	polygon(point Uoriginalvertex,unsigned int numofside = 0);//contructor which set number of sides and originalvertex, thus its *sides and *vertexes is null pointer
	polygon(unsigned int numofside, point vertex[]) throw(error);//contructor, whose parameter is a point array saved the vertexes of the polygon
	//if numofside is 0, function will throw a exception( within the exception class is the error type "noside")
	~polygon();//destructor
	polygon(const polygon &obj);//copy constructor
	point getoriginalvertex()const;//return the originalvertex
	int getnumofsides()const;//return the sides' number of  ploygon
	virtual double area()const throw(error);//return polygon's area( if polygon has no vertexes, this function will throw exception( within the exception class is the error type "cantcalculate")
	virtual double perimeter()const throw(error);//return polygon's perimeter( if polygon has no sides, this function will throw exception( within the exception class is the error type "cantcalculate")
	virtual void show()const {//a simple output function for test
		cout << "original vertex is "; originalvertex.show(); cout << " number of sides is"
			<< sidenum;
	}
};
bool compareSlope(const line &l, const line &r);

具体的实现文件:

#include"Shape.h"
#include<cmath>

//class point {

bool point::operator==(const point &ro)const {
	if (x == ro.x&&y == ro.y) {
		return true;
	}
	else {
		return false;
	}
}

double distancebetweenpoint(const point &a, const point &b)//calculate the distance between two difference point
{
	double x = (a.x - b.x);
	double y = (a.y - b.y);
	x = pow(x, 2);
	y = pow(y, 2);
	double dis = sqrt(x + y);
	return dis;
}

double point::distancetoline(const line &line)const//calculate the distance between this point and line
{
	double A, B, C;
	line.getStandardEquation(A, B, C);
	C = -C;
	
	//calculate the distance
	double dis =abs( (A*x + B*y + C) / sqrt(A*A + B*B));
	return dis;
}

//class line {

void line::getStandardEquation(double &A, double &B, double &C)const {
	//give the line's equation
	double dy1 = end.y - start.y;
	double dx1 = end.x - start.x;
	A = dy1; B = -dx1; C = start.y*dx1 - start.x*dy1;
	C = -C;
	return;
}

double line::length()const {//return the length of line
	return distancebetweenpoint(start, end);
}

bool line::iflinemeet(const line &other)const//return a bool value represent whether this line meets the other line (1 stand for yes, 0 for no)
{
	if (end.x<other.start.x || end.y<other.start.y || start.x>other.end.x || start.y>other.end.y)
	{
		return 0;
	}
	//get the line's equation
	double A1, B1, C1;
	getStandardEquation(A1, B1, C1);

	//get the line's equation
	double A2, B2, C2;
	other.getStandardEquation(A2, B2, C2);

	if ((A1 / A2) == (B1 / B2)) {
		if (C1 != C2) 
			return 0;
		else 
			return 1;
	}

	double rx, ry;//calculate the meet point
	ry = (C1*A2 / A1 - C2) / (B2 - A2*B1 / A1);
	rx = ((-1)*C2 + (-1)*B2*ry) / A2;

	if (rx<start.x || rx<other.start.x || rx>end.x || rx>other.end.x ||
		ry<start.y || ry<other.start.y || ry>end.y || ry>other.end.y) {
		return 0;
	}
	return 1;

}

//class shape {

polygon::polygon(const polygon &obj) {
	sidenum = obj.sidenum;
	originalvertex = obj.originalvertex;
	if (!obj.vertexes) {
		vertexes = nullptr;
	}
	else {
		vertexes = new point[obj.sidenum];
		for (int i = 0; i < sidenum; i++) {
			vertexes[i] = obj.vertexes[i];
		}
	}
	if (!obj.sides) {
		sides = nullptr;
	}
	else {
		sides = new line[obj.sidenum];
		for (int i = 0; i < sidenum; i++) {
			sides[i] = obj.sides[i];
		}
	}
}

double polygon::perimeter()const {
	if (!sides) {//the shape's can't be represent/calculate this way
		throw error(cantcalculate);
	}
	double perimeter = 0;
	for (int i = 0; i < sidenum; i++) {
		perimeter += sides[i].length();
	}
	return perimeter;
}

//the way of calculate the area is refered from the website below:
//https://blog.csdn.net/hemmingway/article/details/7814494
double polygon::area()const {
	if (!vertexes) {//the shape has no vertex
		throw error(cantcalculate);
	}
	double s=0;
	int n = sidenum - 1;
	for (int i = 0; i <= n - 1; i++) {
		s += (vertexes[i].x - vertexes[0].x)*(vertexes[i + 1].y - vertexes[0].y) - (vertexes[i].y - vertexes[0].y)*(vertexes[i + 1].x - vertexes[0].x);
	}
	s = abs(s) / 2;
	return s;
}

void polygon::setoriginalvertex(point ov) {
	originalvertex = ov;
}

int polygon::getnumofsides()const {
	return sidenum;
}

point polygon::getoriginalvertex()const {
	return originalvertex;
}

polygon::~polygon() {
	if (vertexes != nullptr) {
		delete[]vertexes;
	}
	if (sides != nullptr) {
		delete[]sides;
	}
}

polygon::polygon(point Uoriginalvertex, unsigned int numofside ):vertexes(nullptr),sides(nullptr),sidenum(numofside),originalvertex(Uoriginalvertex) {}

polygon::polygon(unsigned int numofside):sidenum(numofside),vertexes(nullptr),sides(nullptr),originalvertex(O) {}

bool compareSlope(const line &l, const line &r) {//compare(>) two lines' slope( no numercially, but geometrically)
	bool flagl, flagr;
	flagl = flagr = 0;
	if (l.end.x == l.start.x)//whether the slope is existed
		flagl = 1;
	if (r.end.x == r.start.x)
		flagr = 1;
	if (flagl&&flagr)
		return 0;

	if (flagl) {
		double k = (r.end.y - r.start.y) / (r.end.x - r.start.x);
		if (k >= 0)
			return 1;
		else
			return 0;
	}
	if (flagr) {
		double k = (l.end.y - l.start.y) / (l.end.x - l.start.x);
		if (k >= 0)
			return 0;
		else
			return 1;
	}
	double kr = (r.end.y - r.start.y) / (r.end.x - r.start.x);
	double kl = (l.end.y - l.start.y) / (l.end.x - l.start.x);
	if (kl >= 0 ) {
		if (kr >= 0)
			return kl > kr;
		else
			return 0;
	}
	if (kl < 0) {
		if (kr < 0)
			return kl > kr;
		else
			return 1;
	}
}

bool aTempCompareFunctionfor_polygonConstructor(double kl, double kr) {
	if (kl >= 0) {
		if (kr >= 0)
			return kl > kr;
		else
			return 0;
	}
	if (kl < 0) {
		if (kr < 0)
			return kl > kr;
		else
			return 1;
	}
}

polygon::polygon(unsigned int numofside, point vertex[]) {
	if (numofside == 0) {
		throw error(errortype(0));
	}

	//find the originalvertex
	sidenum = numofside; double min = distancebetweenpoint(O, vertex[0]);
	for (int i = 0; i < numofside; i++) {
		if (distancebetweenpoint(O, vertex[i]) <= min) {
			originalvertex = vertex[i];
		}
	}

	//set the vertexes in anti-clock order( only make sense when all the vertexes are in the first quadrant )
	class dot {//use a list structure to reorder
	public:
		point v;
		double k;//the slope of the line which go through the original vertex and this point
		bool flag;//whether the point is the original vertex
		dot *next;
		dot() = default;
		dot(point vertex, point original) :v(vertex),  next(nullptr), flag(0) {
			if (vertex.x == original.x) {//the slope doesn't exist, so I use a really big numerical value to represent it
				k = 1000000000000;
				if (vertex.y == original.y)
					flag = 1;
			}
			else {
				k = (vertex.y - original.y) / (vertex.x - original.x);
			}
		}
		dot& operator=(const dot &ro) { v = ro.v;  k = ro.k; next = ro.next; flag = ro.flag; }
		dot(const dot &ro) { v = ro.v; k = ro.k; next = nullptr; flag = ro.flag;	}
	};
	typedef dot* dotp;

	int i=1;
	dot *head = new dot(vertex[0],originalvertex);//create the head node
	while (head->flag) {
		delete head;
		head = new dot(vertex[i], originalvertex);
		i++;
	}
	
	dot *p1, *b1; b1 = head; 
	for ( ; i < numofside; i++) {//create the list
		p1 = new dot(vertex[i], originalvertex);
		if (p1->flag) { delete p1; continue; }
		b1->next = p1;
		b1 = p1;
	}

	//the reorder operation
	int num = sidenum-1;
	dotp *arr = new dotp[num];
	dotp b, p, *t, *todelete;
	t = new dotp;//这里只是为了堵住编译器的嘴
	todelete = t;
	b = p = head;

	for ( i = 0; i < num; i++) {
		arr[i] = p;
		p = p->next;
	}

	for (int j = 0; j<num; j++) {
		double temp;
		for (int i = j; i<num; i++) {
			if (i == j) {
				temp = arr[i]->k;
				t = &arr[i];
			}
			if (!aTempCompareFunctionfor_polygonConstructor(arr[i]->k, temp)){
				temp = arr[i]->k;
				t = &arr[i];
			}
		}
		dotp lin = arr[j];
		arr[j] = *t;
		*t = lin;
	}

	head = arr[0];
	b = head;
	for ( i = 1; i < num; i++) {
		b->next = arr[i];
		b = arr[i];
	}
	b->next = nullptr;


	//create the polygon's vertexes and sides
	vertexes = new point[sidenum];
	sides = new line[sidenum];
	vertexes[0] = originalvertex;
	p1 =  head;
	for ( i = 1; i < num+1; i++) {
		vertexes[i] = p1->v;
		line ts(vertexes[i - 1], vertexes[i]);
		sides[i-1] = ts;
		p1 = p1->next;
	}
	line ts(vertexes[sidenum - 1], vertexes[0]);
	sides[sidenum - 1] = ts;

	//clean up
	delete todelete;
	for ( i = 0; i < num; i++) {
		p = head->next;
		delete head;
		head = p;
	}
}