pythonでカスタムクラスは2平面が平行であるか,同一平面であるかを判断する
10026 ワード
# coding= utf-8
from decimal import Decimal, getcontext
from Vector import Vector
getcontext().prec = 30
class Plane(object):
NO_NONZERO_ELTS_FOUND_MSG = 'No nonzero elements found'
def __init__(self, normal_vector=None, constant_term=None):
self.dimension = 3
# if not normal_vector:
# all_zeros = ['0']*self.dimension
# normal_vector = Vector(all_zeros)
self.normal_vector = normal_vector
# if not constant_term:
# constant_term = Decimal('0')
# self.constant_term = Decimal(constant_term)
self.constant_term = constant_term
self.set_basepoint()
def set_basepoint(self):
try:
n = self.normal_vector
c = self.constant_term
basepoint_coords = [0]*self.dimension
initial_index = Plane.first_nonzero_index(n)
initial_coefficient = n[initial_index]
basepoint_coords[initial_index] = c/initial_coefficient
self.basepoint = Vector(basepoint_coords)
except Exception as e:
if str(e) == Plane.NO_NONZERO_ELTS_FOUND_MSG:
self.basepoint = None
else:
raise e
def __str__(self):
num_decimal_places = 3
def write_coefficient(coefficient, is_initial_term=False):
coefficient = round(coefficient, num_decimal_places)
if coefficient % 1 == 0:
coefficient = int(coefficient)
output = ''
if coefficient < 0:
output += '-'
if coefficient > 0 and not is_initial_term:
output += '+'
if not is_initial_term:
output += ' '
if abs(coefficient) != 1:
output += '{}'.format(abs(coefficient))
return output
n = self.normal_vector
try:
initial_index = Plane.first_nonzero_index(n)
terms = [write_coefficient(n[i], is_initial_term=(i==initial_index)) + 'x_{}'.format(i+1)
for i in range(self.dimension) if round(n[i], num_decimal_places) != 0]
output = ' '.join(terms)
except Exception as e:
if str(e) == self.NO_NONZERO_ELTS_FOUND_MSG:
output = '0'
else:
raise e
constant = round(self.constant_term, num_decimal_places)
if constant % 1 == 0:
constant = int(constant)
output += ' = {}'.format(constant)
return output
#
def is_paraller_to(self,p):
p1 = self.normal_vector
p2 = p.normal_vector
return p1.is_paraller_to(p2)
#
def __eq__(self,p):
if self.normal_vector.is_zero():
if not p.normal_vector.is_zero():
return False
else:
diff = self.constant_term - p.constant_term
return MyDecimal(diff).is_near_zero()
elif p.normal_vector.is_zero():
return False
if not self.is_paraller_to(p):
return False
x0 = self.basepoint
y0 = p.basepoint
basepoint_different = x0.minus(y0)
n = self.normal_vector
return basepoint_different.is_orthogonal_to(n)
@staticmethod
def first_nonzero_index(iterable):
for k, item in enumerate(iterable):
if not MyDecimal(item).is_near_zero():
return k
raise Exception(Plane.NO_NONZERO_ELTS_FOUND_MSG)
class MyDecimal(Decimal):
def is_near_zero(self, eps=1e-10):
return abs(self) < eps
p1 = Plane(normal_vector= Vector([-0.412,3.806,0.728]),constant_term = -3.46)
p2 = Plane(normal_vector= Vector([1.03,-9.515,-1.82]),constant_term = 8.65)
print p1.is_paraller_to(p2)
print ' ',p1 ==p2
p1 = Plane(normal_vector= Vector([2.611,5.528,0.283]),constant_term = 4.6)
p2 = Plane(normal_vector= Vector([7.715,8.306,5.342]),constant_term = 3.76)
print p1.is_paraller_to(p2)
p1 = Plane(normal_vector= Vector([-7.926,8.625,-7.212]),constant_term = -7.952)
p2 = Plane(normal_vector= Vector([-2.692,2.875,-2.404]),constant_term = -2.443)
print p1.is_paraller_to(p2)
print ' ',p1 == p2
#
# True
# True
# False
# True
# False
# [Finished in 0.1s]
# coding=utf-8
from math import sqrt, acos, pi
class Vector(object):
"""docstring for Vector"""
""" , """
CANNOT_NORMALIZE_ZERO_VECTOR_MSG = 'Cannot normalize the zero vector'
NO_UNIQUE_ORTHOGONAL_COMPONENT_MSG = "No_unique_orthogonal_component_msg"
ONLY_DEFINZED_IN_TWO_THREE_DIMS_MSG = 'Only_defined_in_two_three_dims_msg'
def __init__(self, coordinates):
super(Vector, self).__init__()
try:
if not coordinates:
raise ValueError
self.coordinates = tuple(coordinates)
self.dimension = len(coordinates)
except ValueError:
raise ValueError('The coordinates must be nonempty')
except TypeError:
raise TypeError('The coordinates must be an iterable')
# ''' python print '''
def __str__(self):
return 'Vector: {}'.format(self.coordinates)
def __eq__(self, v):
return self.coordinates == v.coordinates
def __getitem__(self,item):
return self.coordinates[item]
def scalarMultiply(self,num):
new_corrdinate =[num * x for x in self.coordinates]
return Vector(new_corrdinate)
def minus(self,v):
new_corrdinate = [x-y for x,y in zip(self.coordinates, v.coordinates)]
return Vector(new_corrdinate)
#
def magnitude(self):
coordinates_squared = [x**2 for x in self.coordinates]
return sqrt(sum(coordinates_squared))
#
def normalized(self):
try:
magnitude = self.magnitude()
return Vector([x *1.0/ magnitude for x in self.coordinates])
except ZeroDivisionError:
raise Exception('Cannot normalized the zero myVector2')
#
def dot(self, v):
return sum([round(x*y,3) for x,y in zip(self.coordinates, v.coordinates)])
#
def angle_with(self,v,in_degress=False):
try:
u1 = self.normalized()
u2 = v.normalized()
angle_in_radians = acos(u1.dot(u2))
if in_degress:
degrees_per_radian = 180./pi
return angle_in_radians * degrees_per_radian
else:
return angle_in_radians
except Exception as e:
if str(e) == self.CANNOT_NORMALIZE_ZERO_VECTOR_MSG:
raise Exception('Cannot compute an angle with the zero vector')
else:
raise e
# , ,
# , , acos
#
def vectorType(self, v):
result = ""
# print (self.dot(v))
if self.dot(v) ==0:
result += "Orthogonal "
else:
result += "NoOrthogonal "
new_cordinates = [y/x for x,y in zip(self.coordinates, v.coordinates)]
compair = new_cordinates[0]
for i in new_cordinates:
if round(compair,3) != round(i,3):
result += "NoParaller"
return result
result += "Paraller"
return result
#
def is_orthogonal_to(self, v, tolerance=1e-10):
return abs(self.dot(v)) < tolerance
#
def is_paraller_to(self,v):
return (self.is_zero() or v.is_zero() or self.angle_with(v) ==0 or self.angle_with(v) == pi)
def is_zero(self, tolerance = 1e-10):
return self.magnitude()< tolerance
def component_parallel_to(self, basis):
try:
u = basis.normalized()
weight = self.dot(u)
return u.scalarMultiply(weight)
except Exception as e:
if str(e) == self.CANNOT_NORMALIZE_ZERO_VECTOR_MSG:
raise Exception(self.CANNOT_NORMALIZE_ZERO_VECTOR_MSG)
else:
raise e
def component_orthogonal_to(self, basis):
try:
projection = self.component_parallel_to(basis)
return self.minus(projection)
except Exception, e:
if str(e) == self.NO_UNIQUE_ORTHOGONAL_COMPONENT_MSG:
raise Exception(self.NO_UNIQUE_ORTHOGONAL_COMPONENT_MSG)
else:
raise e
#
def cross(self,v):
try:
x_1,y_1,z_1 = self.coordinates
x_2,y_2,z_2 = v.coordinates
new_cordinates = [ y_1*z_2 - y_2*z_1,
-(x_1*z_2 - x_2*z_1),
x_1*y_2 - x_2*y_1]
return Vector(new_cordinates)
except Exception, e:
msg = str(e)
if msg == 'need more than 2 values to unpack':
self_embedded_in_R3 = Vector(self.coordinates + ('0',))
v_embedded_in_R3 = Vector(v.coordinates + ('0',))
return self_embedded_in_R3.cross(v_embedded_in_R3)
elif (msg == 'too many values to unpack' or msg == 'need more than 1 value to unpack'):
raise Exception(self.ONLY_DEFINZED_IN_TWO_THREE_DIMS_MSG)
else:
raise e
#
def area_of_parallelogram_with(self,v):
cross_product = self.cross(v)
return cross_product.magnitude()
#
def area_of_triangle_with(self, v):
return self.area_of_parallelogram_with(v)/2