LoginSignup
2
3

More than 1 year has passed since last update.

シンプルで記述しやすい幾何ライブラリを作りました(C++、Python)

Last updated at Posted at 2022-08-28

 幾何問題に時間がかかるのがストレスだったので、主に自分用にライブラリを作りました。ちなみに幾何ライブラリの代表的なものでは https://ei1333.github.io/luzhiled/ があり、これにほとんどの機能が揃っているのですが、コンパイルが上手くいかなかったり、必要としている API を見つけるのに時間がかかったりすることが多かったので(自分が慣れていないせいで、このライブラリが悪いとかそういうことではないです)、自分用に機能を絞って作り直した、という感じです。

 インクルード、インポートは最小限に、簡潔に記述できるものを目指して作っています。

 ※以下のライブラリは二次元空間用です。「外積」は、正確には外積のスカラーとして扱っています。

C++

#include <iostream>
#include <math.h>
using namespace std;

struct Point
{
    double x = 0;
    double y = 0;

    void rot(double angle)
    {
        double new_x = x * cos(angle) - y * sin(angle);
        double new_y = x * sin(angle) + y * cos(angle);
        x = new_x;
        y = new_y;
    }

    void rot(int degree)
    {
        double pi = atan(1) * 4;
        double angle = double(degree) / 180 * pi;
        rot(angle);
    }

    double norm()
    {
        return sqrt(x * x + y * y);
    }

    double arg()
    {
        return atan2(y, x);
    }

    double operator*(Point &other)
    {
        return x * other.x + y * other.y;
    }

    double operator^(Point &other)
    {
        return x * other.y - y * other.x;
    }

    Point operator+(Point &other)
    {
        Point ans;
        ans.x = x + other.x;
        ans.y = y + other.y;
        return ans;
    }

    Point operator-(Point &other)
    {
        Point ans;
        ans.x = x - other.x;
        ans.y = y - other.y;
        return ans;
    }

    int ccw(Point B, Point C)
    {
        Point A = *this;
        Point AB = B - A;
        Point AC = C - A;
        double cross = AB ^ AC;
        if (cross > 0)
        {
            return 1;
        }
        else if (cross == 0)
        {
            return 0;
        }
        else
        {
            return -1;
        }
        return true;
    }
};

ostream &operator<<(ostream &os, Point p)
{
    os << "(" << p.x << "," << p.y << ")";
    return os;
}

説明

 インクルードする必要があるライブラリは math.h のみです。

メンバ

  • x $x$ 座標を返します。
  • y $y$ 座標を返します。

メソッド

  • norm() 長さを出します
  • arg() 偏角を出します。
  • rot(a) 点を回転させます。doubleを入力した場合はラジアンとして、int を入力した場合は度として扱います。
  • ccw(A,B) この点に対して、他の 2 点 $A,B$ が反時計回りに並んでいるかを検出します。1 は反時計回り、0 は同一直線上、-1 は時計回りです。

 また、演算子をオーバーロードしているので、Point 同士で演算ができます。

オペレータ

  • + それぞれの座標を足し算したものを返します。
  • - それぞれの座標を引き算したものを返します。
  • * Point を作用させた場合は内積を、double を作用させた場合はスカラー倍にしたものを返します。
  • ^ 外積を返します。

具体例

int main(void)
{
    Point A({1, 2});
    cout << A << endl; // (1,2)
    A.rot(90); // int を代入しているので 90 度回転
    cout << A << endl; // (-2,1)
    Point B({3, 4});
    cout << A + B << endl; // (1,5)
    cout << A - B << endl; // (-5,3)
    cout << A * B << endl; // -2 内積
    cout << A * 2 << endl; // (-4,2) スカラー倍
    cout << (A ^ B) << endl; // -11 外積
}

Python

from math import atan2, cos, pi, sin, sqrt


class Point:

    def __init__(self, x=0.0, y=0.0) -> None:
        self.x = x
        self.y = y
        self.norm = sqrt(self.x**2 + self.y**2)
        self.arg = atan2(self.y, self.x)

    def rot(self, angle) -> None: # angle is float/int
        if type(angle) == int:
            angle = angle/180*pi
        self.x, self.y = self.x * \
            cos(angle) - self.y*sin(angle), self.x * \
            sin(angle) + self.y*cos(angle)

    def __add__(self, other: 'Point') -> 'Point':
        return Point(self.x + other.x, self.y + other.y)

    def __sub__(self, other: 'Point') -> 'Point':
        return Point(self.x - other.x, self.y - other.y)

    def __mul__(self, other): # float/int -> Point, or Point -> float
        if type(other) == float or type(other) == int:
            return Point(self.x*other, self.y*other)
        return self.x * other.x + self.y * other.y

    def __xor__(self, other: 'Point') -> float:
        return self.x * other.y - self.y * other.x

    def ccw(self, B: 'Point', C: 'Point') -> int:
        AB = B - self
        AC = C - self
        cross = AB ^ AC
        if cross > 0:
            return 1
        elif cross == 0:
            return 0
        else:
            return -1

    def __str__(self) -> str:
        return f'({self.x},{self.y})'

説明

 インポートする必要があるライブラリは math のみです。

メンバ

  • x $x$ 座標を返します。
  • y $y$ 座標を返します。
  • norm 長さを出します
  • arg 偏角を出します。

メソッド

  • rot(a) 点を回転させます。floatを入力した場合はラジアンとして、int を入力した場合は度として扱います。
  • ccw(A,B) この点に対して、他の 2 点 $A,B$ が反時計回りに並んでいるかを検出します。1 は反時計回り、0 は同一直線上、-1 は時計回りです。

オペレータ

  • + それぞれの座標を足し算したものを返します。
  • - それぞれの座標を引き算したものを返します。
  • * Point を作用させた場合は内積を、double を作用させた場合はスカラー倍にしたものを返します。
  • ^ 外積を返します。

具体例

A = Point(1, 2)
print(A) # (1,2)
A.rot(90) # int を代入しているので 90 度回転
print(A) # (-2.0,1.0000000000000002)
B = Point(3, 4)
print(A+B) # (1.0,5.0)
print(A-B) # (-5.0,-3.0)
print(A*B) # -1.9999999999999991 // 内積
print(A*2) # (-4.0,2.0000000000000004) // スカラー倍
print(A ^ B) # -11.0 // 外積

Validation

 随時追加

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3