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