0001"""
0002**line** in space representing breathless length
0003"""
0004
0005__Def__ =       ['Line']
0006
0007__Classes__ =   ['LineFromPoints', 'PlanePerp', 'PlanesLine', 'ParaLine',
0008                'Transversal','NearLine', 'ConicPolar','LinePerp',
0009                'CirclePolar']
0010
0011__all__= __Classes__ + __Def__
0012
0013__doc_hints__={'m_type':'factory'}
0014
0015import pygeo.base.abstracts._real as Real
0016
0017from pygeo.base.analytics._position3 import Position3
0018from pygeo.base.support.pygeoconstants import *
0019from pygeo.base.analytics.pygeomath import cross3, vector, solve, array, determinant,                                  matrixmultiply, quadratic, absolute, sqrt,                                  transpose, AlgebraError
0022from pygeo.base.abstracts._element import Element
0023from pygeo.base.support.pygeoopts import DO_TESTS, EPS
0024
0025
0026class LineFromPoints(Real._Line):
0027    """
0028*line* connecting the **2 given points**
0029    
0030    inherits
0031    
0032        `_Line`__
0033
0034__ class-base.abstracts._real._Line.html   
0035    
0036    """
0037    __doc_hints__=  {'factory':'Line',
0038                    'args':['point1,point2']}
0039
0040    def __init__(self,p1,p2,**kws):
0041        Real._Line.__init__(self,*[p1,p2],**kws)
0042        self.p1=p1
0043        self.p2=p2
0044        self.init()
0045
0046    def __repr__(self):
0047        return self.__class__.__name__ + "(" + str(self.p1) +"," +                                          str(self.p2) + ")"
0049
0050    def findSelf(self):
0051        if DO_TESTS:
0052            if self.p1 == self.p2:
0053                print self.__class__.__name__
0054                print """points are not distinct, no line defined, 
0055                        returned False"""
0056                return False
0057        return True
0058
0059
0060class PlanePerp(Real._Line):
0061    """
0062*line* perpendicular to the given **plane** and through the given **point**
0063    
0064    inherits
0065    
0066        `_Line`__
0067
0068__ class-base.abstracts._real._Line.html   
0069    
0070    """
0071    __doc_hints__=  {'factory':'Line',
0072                    'args':['plane,point'],
0073                    'conditions':['point not on plane'],
0074                    'otherwise':'None'}
0075
0076    def __init__(self,plane,p1,**kws):
0077        self.plane=plane
0078        self.drawlen=kws.get("drawlen",20)
0079        Real._Line.__init__(self,*[plane,p1],**kws)
0080        self.p1=p1
0081        self.deps=[self.p2]
0082        self.init()
0083
0084    def findSelf(self):
0085        t=self.p1.onPlane(self.plane)
0086        if t:
0087            self.p2.set(self.plane._u*self.drawlen+self.p1)
0088        else:
0089            self.p2.set(self.p1)
0090            self.p2.toPlane(self.plane)
0091        return True
0092
0093class LinePerp(Real._Line):
0094    """
0095*line* through **2nd given point** and perpendicular to the line through
0096**1st and 2nd given points**, on the plane of 3 given points
0097    
0098     inherits
0099    
0100        `_Line`__
0101
0102__ class-base.abstracts._real._Line.html   
0103   
0104    """
0105    __doc_hints__=  {'factory':'Line',
0106                    'args':['point1,point2,point3'],
0107                    'conditions':['points unique'],
0108                    'otherwise':'None'}
0109
0110    def __init__(self,p1,p2,p3,**kws):
0111        self.n1 = p1
0112        self.drawlen=kws.get("drawlen",20)
0113        self.n3 = p3
0114        self.n2=p2
0115        self.u1=Position3()
0116        self.u2=Position3()
0117        self.n=Position3()
0118        Real._Line.__init__(self,*[p1,p2,p3],**kws)
0119        self.deps=[self.p1,self.p2]
0120        self.init()
0121
0122    def findSelf(self):
0123        n1=self.n1
0124        n2=self.n2
0125        n3=self.n3
0126        u1=self.u1
0127        u2=self.u2
0128        drawlen=self.drawlen/2.
0129        u1.set((n2-n1).norm())
0130        d1 = u1.dot(n2)
0131        u2.set(cross3(n1,n2,n3).norm())
0132        d2=u2.dot(n2)
0133        direction = u2.cross(u1).norm()
0134        self.n.set(vector(solve(
0135                  array([u2,u1,direction]),
0136                  array([d2,d1,0.]))))
0137        self.p1.set(direction*drawlen+self.n)
0138        self.p2.set(direction*-drawlen+self.n)
0139        return True
0140
0141class PlanesLine(Real._Line):
0142    """
0143*line* determined by the intersection of the **2 given planes** 
0144    
0145     inherits
0146    
0147        `_Line`__
0148
0149__ class-base.abstracts._real._Line.html   
0150    
0151    """
0152    __doc_hints__=  {'factory':'Line',
0153                    'args':['plane1,plane2'],
0154                    'conditions':['planes not parallel'],
0155                    'otherwise':'None'}
0156
0157    __opts__ = Real._Line.__opts__[:]+["drawlen"]
0158
0159    def __init__(self,plane1,plane2,**kws):
0160        self.plane1=plane1
0161        self.plane2=plane2
0162        self.drawlen=kws.get("drawlen",20)
0163        Real._Line.__init__(self,*[plane1,plane2],**kws)
0164        self.deps=[self.p1,self.p2]
0165        self.init()
0166
0167    def getDirection(self):
0168        return self.plane1._u.cross(self.plane2._u).norm()
0169
0170    def getNormal(self):
0171        plane1=self.plane1
0172        plane2=self.plane2
0173        return vector(solve(
0174                           array([plane1._u,plane2._u,self.getDirection()]),
0175                           array([plane1._d,plane2._d,0.])))
0176
0177    def parameters(self):
0178        plane1=self.plane1
0179        plane2=self.plane2
0180        direction = self.getDirection()
0181        try:
0182            normal =  vector(solve(
0183                             array([plane1._u,plane2._u,direction]),
0184                             array([plane1._d,plane2._d,0.])))
0185        except AlgebraError:
0186            print self.__class__.__name__
0187            print "planes parallel, not intersection found"
0188            return None,None
0189        return normal,direction
0190
0191    def findSelf(self):
0192        normal,direction=self.parameters()
0193        if normal:
0194            drawlen=self.drawlen/2.
0195            self.p1.set(direction*drawlen+normal)
0196            self.p2.set(direction*-drawlen+normal)
0197            return True
0198        else:
0199            print self.__class__.__name__
0200            print " planes parallel, returned False"
0201            return False
0202
0203
0204class ParaLine(Real._Line):
0205    """
0206*line* parallel to the given **line**, through the given **point**, 
0207on the plane of the point and the line.
0208    
0209     inherits
0210    
0211        `_Line`__
0212
0213__ class-base.abstracts._real._Line.html   
0214    
0215    """
0216    __doc_hints__=  {'factory':'Line',
0217                    'args':['point,line'],
0218                    'conditions':['point not on line'],
0219                    'otherwise':'None'}
0220
0221    def __init__(self,line,point,**kws):
0222        self.line=line
0223        self.point=point
0224        Real._Line.__init__(self,*[line,point],**kws)
0225        self.drawlen=kws.get("drawlen",20)
0226        self.deps=[self.p1,self.p2]
0227        self.init()
0228
0229    def parameters(self):
0230        direction=self.line.getDirection()
0231        normal =  self.point - self.point.dot(direction)*direction
0232        return normal, direction
0233
0234    def findSelf(self):
0235        normal,direction=self.parameters()
0236        drawlen=self.drawlen/2.
0237        self.p1.set(direction*drawlen+normal)
0238        self.p2.set(direction*-drawlen+normal)
0239        return True
0240
0241class NearLine(Real._Line):
0242    """
0243*line* connecting the nearest points of the **2 given lines**
0244    
0245     inherits
0246    
0247        `_Line`__
0248
0249__ class-base.abstracts._real._Line.html   
0250    
0251    """
0252
0253    __doc_hints__=  {'factory':'Line',
0254                    'args':['line1,line2'],
0255                    'conditions':['lines are not coincident',
0256                                    'lines do not intersect'],
0257                    'otherwise':'None; None'}
0258
0259    def __init__(self,line1,line2,**kws):
0260        self.line1=line1
0261        self.line2=line2
0262        Real._Line.__init__(self,*[line1,line2],**kws)
0263        self.lines=[self.line1,self.line2]
0264        self.drawlen=kws.get("drawlen",20)
0265        self.deps=[self.p1,self.p2]
0266        self.extend=kws.get("extend",False)
0267        self.init()
0268
0269    def findSelf(self):
0270        n1,d1=self.line1.parameters()
0271        n2,d2=self.line2.parameters()
0272        c=d1.cross(d2)
0273        o=n2-n1
0274        try:
0275            f1=determinant(array((o,d2,c)))/c.mag2
0276            f2=determinant(array((o,d1,c)))/c.mag2
0277            self.p1.set(d1*f1+n1)
0278            self.p2.set(d2*f2+n2)
0279            if self.p1 == self.p2:
0280                print self.__class__.__name__
0281                print "Lines intersect, Near line of zero length"
0282                return False
0283        except ZeroDivisionError:
0284            print self.__class__.__name__
0285            print "lines are parallel, near line undefined, returned False"
0286            return False
0287        return True
0288
0289    def setext(self):
0290        if self.Not_null:
0291            if self.show:
0292                if (self.line1.get_extension(self.p1) or
0293                        self.line1.get_extension(self.p2)):
0294                        self.line1._redraw()
0295                if (self.line2.get_extension(self.p1) or
0296                        self.line2.get_extension(self.p2)):
0297                        self.line2._redraw()
0298
0299
0300class Transversal(Real._Line):
0301    """
0302*line* through the given **point** and intersecting the **2 given lines**
0303    
0304     inherits
0305    
0306        `_Line`__
0307
0308__ class-base.abstracts._real._Line.html   
0309    
0310    """
0311    __doc_hints__=  {'factory':'Line',
0312                    'args':['line1,line2,point']}
0313
0314    def __init__(self,line1,line2,point,**kws):
0315        self.line1=line1
0316        self.line2=line2
0317        self.point=point
0318        Real._Line.__init__(self,*[line1,line2,point],**kws)
0319        self.deps=[self.p1,self.p2]
0320        self.pointsize=kws.get('pointsize',5)
0321        self.extend=kws.get("extend",False)
0322        self.lines=[self.line1,self.line2]
0323        self.ip1=Position3()
0324        self.ip2=Position3()
0325        self.init()
0326
0327    def findSelf(self):
0328        ip1=self.ip1
0329        ip2=self.ip2
0330        line1=self.line1
0331        line2=self.line2
0332        point=self.point
0333        u1= cross3(line1.p1,line1.p2,point)
0334        d1=u1.dot(line1.p1)
0335        u2= cross3(line2.p1,line2.p2,point)
0336        d2=u2.dot(line2.p1)
0337        dir=u1.cross(u2)
0338        try:
0339            ip1.set(vector(solve(
0340                   array([u1,u2,dir]),
0341                   array([d1,d2,0.]))))
0342        except AlgebraError:
0343            print self.__class__.__name__
0344            if u1 == u2:
0345                print """lines conincident, transversal undefined, 
0346                        returned False"""
0347            else:
0348               print """point is on a given line, transversal undefined, 
0349                        returned False"""
0350            return False
0351
0352        ip2.set(dir + ip1)
0353        self.p1.toInterSection(ip1,ip2,line1.p1,line1.p2)
0354        self.p2.toInterSection(ip1,ip2,line2.p1,line2.p2)
0355        return True
0356
0357    def setext(self):
0358        self.get_extension(self.point)
0359        for line in self.lines:
0360            if self.Not_null:
0361                if self.show:
0362                    if line.show:
0363                        line.get_extension(self.p1)
0364
0365
0366class CirclePolar(Real._Line):
0367    """
0368*line* polar to the given **point** with the respect to the given **circle**.
0369    
0370     inherits
0371    
0372        `_Line`__
0373
0374__ class-base.abstracts._real._Line.html   
0375    
0376    """
0377    __doc_hints__=  {'factory':'Line',
0378                    'args':['circle,point,<chord=(boolean)>'],
0379                    'conditions':['point and circle are coplanar',
0380                                'if chord = True,point exterior to circle'],
0381                    'otherwise':'None'}
0382
0383    __opts__= Real._Line.__opts__[:] + ["chord"]
0384
0385    def __init__(self,circle,epoint,**kws):
0386        self.circle=circle
0387        self.epoint=epoint
0388        Real._Line.__init__(self,*[circle,epoint],**kws)
0389        self.chord=kws.get('chord',True)
0390        self.drawlen=kws.get("drawlen",20)
0391        self.deps=[self.p1,self.p2]
0392        self.u=Position3()
0393        self.n=Position3()
0394        self.tp=Position3()
0395        self.init()
0396
0397    def findSelf(self):
0398        circle=self.circle
0399        point=self.epoint
0400        if DO_TESTS:
0401            t=point.onPlane(circle)
0402        else:
0403            t=True
0404        if t:
0405            drawlen=self.drawlen/2.
0406            self.p2.toInvertPoint(circle,self.epoint)
0407            self.u.set((self.p2-self.epoint).norm())
0408            d = self.u.dot(self.p2)
0409            direction = circle._u.cross(self.u).norm()
0410            try:
0411                self.n.set(vector(solve(
0412                                  array([circle._u,self.u,direction]),
0413                                  array([circle._d,d,0.]))))
0414            except AlgebraError:
0415                 print self.__class__.__name__
0416                 print "epoint at center, polar at infinity, returned False"
0417                 return False
0418            self.p1.set(direction*drawlen+self.n)
0419            self.p2.set(direction*-drawlen+self.n)
0420            if self.chord:
0421                self.tp.set(self.circle._center)
0422                self.tp.toLine(self.p1,self.p2)
0423                dist=self.tp.distanceSquared(circle._center)
0424                if sqrt(dist) > circle._radius:
0425                    print self.__class__.__name__
0426                    print """no real point of intersection for circle and 
0427                            line returned False"""
0428                    return False
0429                if not dist < EPS:
0430                    rad2=circle._radiusSquared
0431                    factor=sqrt(rad2-dist)/self.tp.distance(self.p1)
0432                    self.p1.set((self.p1-self.tp)*factor+self.tp)
0433                    self.p2.set(self.tp*2-self.p1)
0434                else:
0435                # line is through circle center
0436                    self.p1.set(direction*circle._radius+self.tp)
0437                    self.p2.set(direction*-circle._radius+self.tp)
0438            return True
0439        else:
0440            print self.__class__.__name__
0441            print """circle and point are not coplanar, no polar defined, 
0442                    returned False"""
0443            return False
0444
0445class ConicPolar(Real._Line):
0446    """
0447*line* polar to the given **point** with respect to the given **conic**
0448    
0449     inherits
0450    
0451        `_Line`__
0452
0453__ class-base.abstracts._real._Line.html   
0454    
0455    """
0456
0457    __doc_hints__=  {'factory':'Line',
0458                    'args':['conic,point,<chord=(boolean)>'],
0459                    'conditions':['point and conic are coplanar',
0460                                'if chord = True,point exterior to conic'],
0461                    'otherwise':'None;None'}
0462
0463    __opts__= Element.__opts__[:] + ["chord"]
0464
0465    def __init__(self,conic,point,**kws):
0466        self.conic=conic
0467        self.point=point
0468        Real._Line.__init__(self,*[conic,point],**kws)
0469        self.deps=[self.p1,self.p2]
0470        self.chord=kws.get('chord',True)
0471        if self.chord:
0472            self.seg=True
0473        self.tp=Position3()
0474        self.init()
0475
0476    def findSelf(self):
0477        if DO_TESTS:
0478            t = self.point.coPlanar(self.conic.p1,self.conic.p2,self.conic.p3)
0479        else:
0480            t = True
0481        if t:
0482            tp=self.tp
0483            equat=self.conic.getPlane()
0484            tp.toXY(self.point)
0485            C=self.conic.getC()
0486            L=matrixmultiply(self.tp,C)
0487            if absolute(
0488                matrixmultiply(L,transpose(tp))) < EPS: #pointis on conic
0489                try:
0490                   L1=array([0,-L[2]/L[1],1])
0491                except ZeroDivisionError:
0492                   print self.__class__.__name__
0493                   print "ZDE"
0494                   L1=array([-L[2]/L[0],0,1])
0495                   return False
0496                self.p1.set(self.point)
0497                self.p2.fromXY(equat,L1)
0498            else:
0499                L= matrixmultiply(C*2,tp)
0500                tp1=array([0.,-L[2]/L[1],1.])
0501                tp2=array([-L[2]/L[0],0.,1.])
0502                cx = matrixmultiply(matrixmultiply(C,tp1),transpose(tp1))
0503                ax = matrixmultiply(matrixmultiply(C,tp2),transpose(tp2))
0504                bx =2*matrixmultiply(matrixmultiply(C,tp2),transpose(tp1))
0505                h= quadratic(ax,bx,cx)
0506                if h[0]:
0507                    x1=tp1+tp2*h[0]
0508                    x2=tp1+tp2*h[1]
0509                else:
0510                    if self.chord:
0511                        x1=tp1+tp2
0512                        x2=tp1+tp2
0513                        print self.__class__.__name__
0514                        print """point interior to conic, no chord defined, 
0515                                returning False"""
0516                        return False
0517                    else:
0518                        x1=tp1
0519                        x2=tp2
0520                x1z=array([x1[0]/x1[2],x1[1]/x1[2],0])
0521                x2z=array([x2[0]/x2[2],x2[1]/x2[2],0])
0522                self.p1.fromXY(equat,x1z)
0523                self.p2.fromXY(equat,x2z)
0524            return True
0525        else:
0526            print self.__class__.__name__
0527            print """point and conic not coplanar, polar undefined, 
0528                    returned False"""
0529            return False
0530
0531
0532def  Line(*args,**kws):
0533    """
0534'class factory' function returns instance of object derived from the _Line 
0535abstract class, representing breathless length in space
0536    """
0537
0538    from pygeo.base.abstracts._element import method_get
0539    from pygeo.base.support.pygeoexceptions import Argument_Type_Error
0540    from pygeo.base.geometry_real.pointarrays import Conic
0541
0542    __sigs__=[  [vector,vector],[Real._Plane,vector],
0543                [Real._Plane,Real._Plane],[Real._Line,vector],
0544                [Real._Line,Real._Line],[Real._Line,Real._Line,vector],
0545                [Conic,vector],[vector,vector,vector],[Real._Circle,vector]]
0546
0547    t,i = method_get(__sigs__,args)
0548
0549    if t is None:
0550        raise Argument_Type_Error(__sigs__,args)
0551    else:
0552        if i==0:
0553            return LineFromPoints(t[0],t[1],**kws)
0554        elif i==1:
0555            return PlanePerp(t[0],t[1],**kws)
0556        elif i==2:
0557            return PlanesLine(t[0],t[1],**kws)
0558        elif i==3:
0559            return ParaLine(t[0],t[1],**kws)
0560        elif i==4:
0561            return NearLine(t[0],t[1],**kws)
0562        elif i==5:
0563            return Transversal(t[0],t[1],t[2],**kws)
0564        elif i==6:
0565            return ConicPolar(t[0],t[1],**kws)
0566        elif i==7:
0567            return LinePerp(t[0],t[1],t[2],**kws)
0568        elif i==8:
0569            return CirclePolar(t[0],t[1],**kws)
0570        else:
0571            raise Argument_Type_Error(__sigs__,args)