// line_symmetry.cpp --- 多角形が線対称かどうか判定する。
// Copyright (C) 2013 片山博文MZ. All rights reserved.
#include <iostream>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
// 実数型。
typedef double numeric_t;
// 位置ベクトルの比較の際の許容される誤差。
const numeric_t TOLERANCE1 = 0.25;
// 単位ベクトルの比較の際の許容される誤差。
const numeric_t TOLERANCE2 = 0.015625;
// 平面ベクトル。
class Point2D
{
public:
numeric_t m_x, m_y;
public:
Point2D()
{
}
Point2D(numeric_t x, numeric_t y) : m_x(x), m_y(y)
{
}
Point2D(const Point2D& p) : m_x(p.m_x), m_y(p.m_y)
{
}
void GetXY(numeric_t& x, numeric_t& y) const
{
x = m_x; y = m_y;
}
void SetXY(numeric_t x, numeric_t y)
{
m_x = x; m_y = y;
}
bool IsZero() const
{
return Length() <= TOLERANCE1;
}
void SetZero()
{
m_x = m_y = 0;
}
// 長さ。
numeric_t Length() const
{
return sqrt(m_x * m_x + m_y * m_y);
}
// 垂直なベクトル。
Point2D Perpendicular() const
{
return Point2D(m_y, -m_x);
}
// 単位ベクトル。
Point2D Unit() const
{
return *this / Length();
}
Point2D& operator=(const Point2D& p)
{
m_x = p.m_x; m_y = p.m_y;
return *this;
}
Point2D& operator+=(const Point2D& p)
{
m_x += p.m_x; m_y += p.m_y;
return *this;
}
Point2D& operator-=(const Point2D& p)
{
m_x -= p.m_x; m_y -= p.m_y;
return *this;
}
Point2D& operator*=(numeric_t d)
{
m_x *= d; m_y *= d;
return *this;
}
Point2D& operator/=(numeric_t d)
{
m_x /= d; m_y /= d;
return *this;
}
friend Point2D operator+(const Point2D& p1, const Point2D& p2)
{
Point2D p(p1);
p += p2;
return p;
}
friend Point2D operator-(const Point2D& p1, const Point2D& p2)
{
Point2D p(p1);
p -= p2;
return p;
}
friend bool operator==(const Point2D& p1, const Point2D& p2)
{
numeric_t dx = p2.m_x - p1.m_x, dy = p2.m_y - p1.m_y;
return sqrt(dx * dx + dy * dy) <= TOLERANCE1;
}
friend bool operator!=(const Point2D& p1, const Point2D& p2)
{
return !(p1 == p2);
}
friend numeric_t operator*(const Point2D& p1, const Point2D& p2)
{
return p1.m_x * p2.m_x + p1.m_y * p2.m_y;
}
friend Point2D operator*(const Point2D& p1, numeric_t d)
{
Point2D p(p1);
p *= d;
return p;
}
friend Point2D operator/(const Point2D& p1, numeric_t d)
{
Point2D p(p1);
p /= d;
return p;
}
friend Point2D operator*(numeric_t d, const Point2D& p1)
{
return p1 * d;
}
// 中点。
friend Point2D MidPoint(const Point2D& p1, const Point2D& p2)
{
return Point2D((p1.m_x + p2.m_x) / 2, (p1.m_y + p2.m_y) / 2);
}
friend ostream& operator<<(ostream& o, const Point2D& p)
{
o << "(" << p.m_x << ", " << p.m_y << ")";
return o;
}
// 一直線上にあるかどうか?
friend bool OnOneLine(const Point2D& p1, const Point2D& p2, const Point2D& p3)
{
return ((p2 - p1).Unit() - (p3 - p2).Unit()).Length() <= TOLERANCE2;
}
};
int main(void)
{
int i, j, n, i1, i2;
numeric_t x, y, g;
Point2D mp, unit, p1, p2, p3, q, r;
vector<Point2D> polygon;
bool f;
// 入力。
cout << "多角形の頂点の個数: ";
cin >> n;
if (n < 0)
{
cout << "データが不正です。" << endl;
return 1;
}
for (i = 0; i < n; i++)
{
cout << "頂点#" << (i + 1) << "のx座標とy座標: ";
cin >> x >> y;
polygon.push_back(Point2D(x, y));
}
// 重複した頂点を取り除く。
f = false;
for (i = 0; i < n - 1; i++)
{
p1 = polygon[i];
p2 = polygon[i + 1];
if (p1 == p2)
{
memmove(&polygon[i], &polygon[i + 1], (n - i - 1) * sizeof(Point2D));
polygon.resize(--n);
i--;
f = true;
}
}
if (n > 1)
{
p1 = polygon[n - 1];
p2 = polygon[0];
if (p1 == p2)
{
polygon.resize(--n);
f = true;
}
}
if (f)
cout << "重複した頂点を取り除きました。" << endl;
// 約180度の角の頂点を取り除く。
f = false;
for (i = 0; i < n - 2; i++)
{
p1 = polygon[i];
p2 = polygon[i + 1];
p3 = polygon[i + 2];
if (OnOneLine(p1, p2, p3))
{
memmove(&polygon[i + 1], &polygon[i + 2], (n - i - 2) * sizeof(Point2D));
polygon.resize(--n);
i--;
f = true;
}
}
while (n > 2)
{
p1 = polygon[n - 2];
p2 = polygon[n - 1];
p3 = polygon[0];
if (OnOneLine(p1, p2, p3))
{
polygon.resize(--n);
f = true;
continue;
}
break;
}
while (n > 2)
{
p1 = polygon[n - 1];
p2 = polygon[0];
p3 = polygon[1];
if (OnOneLine(p1, p2, p3))
{
memmove(&polygon[0], &polygon[1], (n - 1) * sizeof(Point2D));
polygon.resize(--n);
f = true;
continue;
}
break;
}
if (f)
cout << "約180度の角の頂点を取り除きました。" << endl;
// 表示。
if (n > 0)
{
for (i = 0; i < n; i++)
{
cout << "頂点#" << (i + 1) << ": " << polygon[i] << endl;
}
}
else
{
cout << "頂点はありません。" << endl;
}
// 0角形、1角形、2角形は、常に線対称。
if (n <= 2)
{
cout << "頂点が2個以下なので線対称です。" << endl;
return 0;
}
// 頂点の角の二等分線が対称の軸か?
for (i = 0; i < n; i++)
{
// 隣り合う3頂点。
p1 = polygon[(i - 1 + n) % n];
p2 = polygon[i];
p3 = polygon[(i + 1) % n];
// 角に隣り合う2辺の長さが等しいか?
if (fabs((p2 - p1).Length() - (p3 - p2).Length()) <= TOLERANCE1)
{
// 角に隣り合う2頂点の中点。
mp = MidPoint(p1, p3);
// 角の二等分線に平行な単位ベクトル。
if ((mp - p2).IsZero())
unit = (p3 - p1).Perpendicular().Unit();
else
unit = (mp - p2).Unit();
f = true;
for (j = 1; j <= n / 2; j++)
{
// 対称か判定する2頂点のインデックス。
i1 = (i - j + n) % n;
i2 = (i + j) % n;
g = (polygon[i1] - mp) * unit; // 中点から垂線までの長さ。
q = mp + unit * g; // 対称の軸上の点(垂線と交わる)。
r = q - (polygon[i1] - q); // 軸に対して対称な位置。
//cout << "1%%" << (r - polygon[i2]).Length() << endl;
if (r != polygon[i2])
{
f = false;
break;
}
}
if (f)
{
cout << "頂点#" << (i + 1) << "で線対称です。" << endl;
return 0;
}
}
}
// 辺の垂直二等分線が対称の軸か?
for (i = 0; i < n; i++)
{
// 隣り合う2頂点。
p1 = polygon[i];
p2 = polygon[(i + 1) % n];
// 辺の中点。
mp = MidPoint(p1, p2);
// 辺に対する垂直単位ベクトル。
if ((p2 - p1).Perpendicular().IsZero())
unit.SetZero();
else
unit = (p2 - p1).Perpendicular().Unit();
f = true;
for (j = 1; j < (n + 1) / 2; j++)
{
// 対称か判定する2頂点のインデックス。
i1 = (i - j + n) % n;
i2 = (i + j + 1) % n;
g = (polygon[i1] - mp) * unit; // 中点から垂線までの長さ。
q = mp + unit * g; // 対称の軸上の点(垂線と交わる)。
r = q - (polygon[i1] - q); // 軸に対して対称な位置。
//cout << "2%%" << (r - polygon[i2]).Length() << endl;
if (r != polygon[i2])
{
f = false;
break;
}
}
if (f)
{
cout << "頂点#" << (i + 1) << "と頂点#" << ((i + 1) % n + 1) <<
"の間で線対称です。" << endl;
return 0;
}
}
cout << "線対称ではありません。" << endl;
return 0;
}