LoginSignup
5
7

More than 3 years have passed since last update.

SymPy Geometry 学習メモ(点・直線)

Last updated at Posted at 2019-09-08

はじめに

sympy.geometry の学習メモ ~点・直線 編~ です。

sympy.geometry(SymPiのGeometryモジュール)では、主に2次元平面上の直線多角形などについて外周長さや面積の計算、交差判定などができます。3次元空間における直線や平面も扱うことができますが、ここでは対象外とします。

Pythonのバージョンは 3.6.8、SymPiのバージョンは 1.3 を使用しました。

バージョン確認
import sys
import sympy
print(f'Python {sys.version}')
print(f'SymPi  {sympy.__version__}')

点を定義して、2点間の距離や中点、3つ以上の点が同一直線上にあるか?などを調べることができます。

次のコードは $p0$ から $p4$ までの 4点 を生成するものです。また、後ほど求める距離や中点などを視覚的に確認するために matplotlib でグラフ上に点をプロットしておきます。

from sympy.geometry import *
import matplotlib.pyplot as plt
%matplotlib inline

# ポイント(点)の生成
p0 = Point(1, 1)
p1 = Point(4, 4)
p2 = Point(6, 6)
p3 = Point(5, 2)

# グラフ描画
points = [p0, p1, p2, p3]
plt.figure(figsize=(5,5),dpi=96)
for i,p in enumerate(points) :
    plt.plot(p.x, p.y, color='0.0',marker='.')
    plt.text(p.x, p.y + 0.2, 'p{0}'.format(i) , size=10, 
             horizontalalignment='center', verticalalignment='bottom')

xRange=yRange=(0,8) 
plt.xlim(xRange)
plt.ylim(yRange)
plt.xticks(range(xRange[0],xRange[1]+1))
plt.yticks(range(yRange[0],yRange[1]+1))
plt.grid(color='blue', alpha=0.3)
plt.show()

ダウンロード.png

これらのについて、sympy.geometory を利用して距離や中点などを求めていきます。

print(f'(1) type = {type(p1)}') # 型
print(f'(2) (x,y)=({p1.x},{p1.y})') # 座標の参照
print(f'(3) p0とp3の距離 = {p0.distance(p3)}') # 2点間の距離
print(f'(4) p0とp3の中点 = {p0.midpoint(p3)}') # 中点
print(f'(5-1) {Point.is_collinear(p0,p1,p2)}') # 同一直線上にあるか?
print(f'(5-2) {Point.is_collinear(p1,p2,p3)}') # 同一直線上にあるか?
print(f'(5-3) {Point.is_collinear(p0,p1,p2,p3)}') # 同一直線上にあるか?
実行結果
(1) type = <class 'sympy.geometry.point.Point2D'>
(2) (x,y)=(4,4)
(3) p0とp3の距離 = sqrt(17)
(4) p0とp3の中点 = Point2D(3, 3/2)
(5-1) True
(5-2) False
(5-3) False

計算に平方根を含む場合は、そのまま出力されます。float型に変換するためには float(p0.distance(p3)) のように float() を利用します。

線(線分・直線)

直線(Line)線分(Segment) が区別されます。Line は2次元平面上を無限にどこまでも伸びていきます。一方、Segment は端点が与えられています。

線分

端点が与えられ、長さが有限の Segment(線分)は、先ほどの点(Point)を2個与えて生成します。

次のプログラムでは、3つの線分と1つの点を生成しています。

from sympy.geometry import *
import matplotlib.pyplot as plt
%matplotlib inline

# ポイント(点)の生成
k = Point(3, 5)

# セグメント(線分)の生成
s0 = Segment(Point(1, 1),Point(5,5))
s1 = Segment((1, 3),(5,2)) # Point()は省略可能
s2 = Segment((6, 7),(7,1)) # Point()は省略可能
segments = [s0,s1,s2]

# グラフ描画
plt.figure(figsize=(5,5),dpi=96)
for i,s in enumerate(segments) :
    plt.plot([s.p1.x, s.p2.x], [s.p1.y, s.p2.y])
    pn = 'p' + str(i)
    for p, m in zip( s.points,('',"'") ) :
        plt.plot(p.x, p.y, color='0.0',marker='.')
        plt.text(p.x, p.y + 0.2, pn+m , size=10, 
                 horizontalalignment='center', verticalalignment='bottom')

plt.plot(k.x, k.y, color='0.0',marker='.')
plt.text(k.x, k.y + 0.2, 'k', size=10, 
             horizontalalignment='center', verticalalignment='bottom')

xRange=yRange=(0,8) 
plt.xlim(xRange)
plt.ylim(yRange)
plt.xticks(range(xRange[0],xRange[1]+1))
plt.yticks(range(yRange[0],yRange[1]+1))
plt.grid(color='blue', alpha=0.3)
plt.show()

ダウンロード (1).png

Segment(線分)については、長さ、中点、2つの線分の交点などを求めることができます。傾きをダイレクトに取得できる関数・プロパティが見当たらなかったので gradient という関数を自作しています(Line(直線)の場合は slope というプロパティで取得可能なのですが・・・)。

print(f'(1) type = {type(s0)}') # 型
print(f'(2) length = {s0.length}') # 長さ

print(f'(3) direction = {s0.direction}') # 方向?

gradient = lambda s: s.direction.y / s.direction.x 
print(f'(4-1) 線分s0の傾き = {gradient(s0)}') # 傾き
print(f'(4-2) 線分s1の傾き = {gradient(s1)}') # 傾き
print(f'(4-3) 線分s2の傾き = {gradient(s2)}') # 傾き

print(f'(5) 中点 = {s0.midpoint}')
print(f'(6) 点kとの距離 = {s0.distance(k)}')
print(f'(7-1) 線分s0と線分s1の交点 = {s0.intersection(s1)}')
print(f'(7-2) 線分s0と線分s2の交点 = {s0.intersection(s2)}')
実行結果
(1) type = <class 'sympy.geometry.line.Segment2D'>
(2) length = 4*sqrt(2)
(3) direction = Point2D(4, 4)
(4-1) 線分s0の傾き = 1
(4-2) 線分s1の傾き = -1/4
(4-3) 線分s2の傾き = -6
(5) 中点 = Point2D(3, 3)
(6) 点kとの距離 = sqrt(2)
(7-1) 線分s0と線分s1の交点 = [Point2D(13/5, 13/5)]
(7-2) 線分s0と線分s2の交点 = []

線分同士の交点を求めると結果はリストで返されます。線分$s0$ と $s1$ の交点は $(2.6, 2.6)$ 、線分$s0$ と $s2$ の交点は「無し(空のリスト)」で、この結果が正しいことはグラフから視覚的に確認できます($13/5=2.6$)。

また、distance により、任意の点との距離を求めることもできます。

直線

今度は、空間を無限に伸びていく Line(直線)を扱います。直線は「2点を与える」「1点とと傾きを与える」「Segment(線分)を与える」によって生成することができます。

import sympy
from sympy.geometry import *
import matplotlib.pyplot as plt
%matplotlib inline

# ライン(直線)の生成
m0 = Line( Point(1, 1), Point(3,2) )
m1 = Line( (2,6), slope = sympy.Rational(-2,3) ) # 傾き -2/3
m2 = Line( Segment((0, 5),(4,6)) ) # 線分から生成
lines = [m0,m1,m2]

# グラフ描画
plt.figure(figsize=(5,5),dpi=96)
xRange=yRange=(0,8) 

# 描画のために x=0 と x=8 の直線(グラフ両端の垂直線)を生成
mr0 = Line(Point(xRange[0], 0), slope = sympy.oo )
mr1 = Line(Point(xRange[1], 0), slope = sympy.oo )

for i,m in enumerate(lines) :
    p1 = (m.intersection(mr0))[0] # x=0 と 直線m の交点
    p2 = (m.intersection(mr1))[0] # x=8 と 直線m の交点
    plt.plot([p1.x, p2.x], [p1.y, p2.y])
    plt.text( p2.x, p2.y, ' m{0}'.format(i), size = 10, verticalalignment='center')

plt.xlim(xRange)
plt.ylim(yRange)
plt.xticks(range(xRange[0],xRange[1]+1))
plt.yticks(range(yRange[0],yRange[1]+1))
plt.grid(color='blue', alpha=0.3)
plt.show()

ダウンロード (2).png

直線については、次のような値の取得や計算ができます。(3) は、直線を $ax + by + c = 0$ という式で表現したときの各係数をタプル $(a, b, c)$ で取得するものです。(4) のY軸との切片は、直接取得できなかったので係数から計算しています。

print(f'(1) type = {type(m0)}') # 型
print(f'(2) m0の傾き = {m0.slope}') 
print(f'(3) m1の方程式の係数 = {m1.coefficients}') 

intercept = lambda m: -m.coefficients[2] / m.coefficients[1] 
print(f'(4) m1のY軸切片 = {intercept(m1)}') 

print(f'(5) 直線m1 と m2 の交点 = {m1.intersection(m2)}') 
実行結果
(1) type = <class 'sympy.geometry.line.Line2D'>
(2) m0の傾き = 1/2
(3) m1の方程式の係数 = (2/3, 1, -22/3)
(4) m1のY軸切片 = 22/3
(5) 直線m1とm2の交点 = [Point2D(28/11, 62/11)]

Line からは equation により、sympy式を取得することができます。以下は、Lineから直線の式を取得して、$x$ に数値を代入して、$y$ について解いています(具体的には、直線m1について $x=4$ のときの $y$ の値を求めています)。

x, y = sympy.symbols('x y')
px = 4
py = sympy.solve(m1.equation(x,y).subs(x, px),y)[0]
print(f'直線m1 において、xが {px} のとき、yは {py} (={float(py):.1f}) となる')
実行結果
直線m1 において、xが 4 のとき、yは 14/3 (=4.7) となる

つづき

5
7
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
5
7