0001###########################################################################
0002# PyGeo - a dynamic geometry toolkit
0003# Copyright (C) 2005 Arthur Siegel (ajsiegel@optonline.com)
0004#
0005# This library is free software; you can redistribute it and/or
0006# modify it under the terms of the GNU Lesser General Public
0007# License as published by the Free Software Foundation; either
0008# version 2.1 of the License, or (at your option) any later version.
0009#
0010# This library is distributed in the hope that it will be useful,
0011# but WITHOUT ANY WARRANTY; without even the implied warranty of
0012# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013# Lesser General Public License for more details.
0014#
0015# You should have received a copy of the GNU Lesser General Public
0016# License along with this library; if not, write to the Free Software
0017# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0018#
0019#PyGeo homepage: http://pygeo.sourceforge.net
0020###########################################################################
0021
0022"""
0023abstract classes for geometric objects of the complex plane
0024"""
0025
0026#Python modules
0027#----------perhaps not--------------
0028#import cmath
0029
0030#PyGeo modules
0031from pygeo.base.analytics._cposition import CPosition
0032from pygeo.base.support.pygeoconstants import CYAN,BLACK, BLUE,GREEN, YELLOW, RED
0033from pygeo.base.support.pygeoconstants import  ROUND, LINES, TINYFONT, DIAMOND
0034from pygeo.base.analytics.pygeomath import *
0035from pygeo.base.support.pygeoopts import COMPLEX_MAX
0036from pygeo.base.abstracts._element import Element,freepoints
0037import pygeo.base.drawing.zdraw as Z_Draw
0038
0039
0040class _zPoint(CPosition,Element,Z_Draw.zdrawPoint):
0041    """ a zero dimensional object with a defined postion on the complex plane
0042
0043inherits:  Element__ , CPosition__ , zdrawPoint__
0044
0045defining instance attributes 
0046
0047    - real: the real coordinate
0048    - imag: the imaginary coordinate
0049
0050__ class-base.abstracts._element.Element.html
0051__ class-base.analytics._cposition.CPosition.html
0052__ class-base.drawing.zdraw.zdrawPoint.html
0053    """
0054
0055    __opts__= Element.__opts__[:] + ["pointsize","tracewidth","tracecolor",
0056                                    "maxtrace","mintrace", "fontsize",
0057                                    "fontcolor","fontXofffset","fontYofffset","tracecurve","style"]
0058
0059    def __init__(self,*args,**kws):
0060        """intialize attributes, with keywords arguments or defaults"""
0061
0062        self.Not_null = True
0063        self._pos=None
0064        Element.__init__(self,*args,**kws)
0065        self.color = kws.get("color",CYAN)
0066        self.pointsize=kws.get("pointsize",.1)
0067        self.style=kws.get("style",ROUND)
0068        if self.label:
0069            self.fontsize=kws.get("fontsize",TINYFONT)
0070            self.fontcolor=kws.get("fontcolor",BLACK)
0071            self.fontXoffset=kws.get("fontXoffset",self.pointsize/10.+3)
0072            self.fontYoffset=kws.get("fontYoffset",self.pointsize/10.+3)
0073        if self.trace:
0074            self.tmparray=CPosition()
0075            self.tmparray.set(self)
0076            self.tracecurve=kws.get("tracecurve",True)
0077            self.mintrace=kws.get("mintrace",.0001)
0078            self.maxtrace=kws.get("maxtrace",50.)
0079            self.tracewidth=kws.get("tracewidth",self.pointsize/2.0)
0080            self.tracecolor=kws.get("tracecolor",self.color)
0081        self.deps=[]
0082
0083    def __description(self, precision):
0084        if self.real != 0.:
0085            return self.__class__.__name__ + "(%.*g%+.*gj)"%(precision,
0086                                             self.real, precision, self.imag)
0087        else:
0088            return self.__class__.__name__ + "%.*gj"%(precision, self.imag)
0089
0090    def __repr__(self):
0091        return self.__description(self.PREC_REPR)
0092
0093    def __str__(self):
0094        return self.__description(self.PREC_STR)
0095
0096    def transform(self,mat,zpoint):
0097        """the Mobius transformation represented by a given complex 2x2 matrix, assigned
0098        to the given point"""
0099        mat = matrixmultiply(mat,self.homogenous())
0100        zpoint.toC(mat)
0101
0102   #     self.setHermitian()
0103   #     zpoint.from_hermitian(self._hermitian.transform(mat))
0104        return True
0105
0106
0107    def uRotate(self,ucenter,angle):
0108        """transform cuurent position as the effect of a rotation of the Riemann sphere
0109        around the given point as axis and the given angle"""
0110
0111        h_angle=angle/2.
0112        cs=cos(h_angle)
0113        sn=sin(h_angle)
0114        a=complex(cs,ucenter.z*sn)
0115        b=complex(-ucenter.y,ucenter.x)*sn
0116        c=-b.conjugate()
0117        d=a.conjugate()
0118        mat1=([[a,b],[c,d]])
0119        mat2=self.homogenous()
0120        self.toC(matrixmultiply(mat1,mat2))
0121        return True
0122
0123    def toC(self,h_array):
0124        """set the position as te projection of a given homogenous complex number to the
0125        complex plane"""
0126
0127        self.set(h_array[0]/h_array[1])
0128        return True
0129
0130    def uVector(self):
0131        """return the vector represnting hte prjojection of the zpoint to the Riemann sphere"""
0132
0133        modplus=self.mod2()+1.
0134        modminus=self.mod2()-1.
0135        xy=2*self/modplus
0136        z=modminus/modplus
0137        return vector(xy.real,xy.imag,z)
0138
0139    def to_uSphere(self,upoint):
0140        """set the given point to the projection of the zpoint to the Riemann sphere"""
0141
0142        upoint.set(self.uVector())
0143        return True
0144
0145    def from_hermitian(self,h):
0146        """set the position on the zplane from the Hermitian matrix representation of the point"""
0147
0148        try:
0149            self.set(-h.C/h.A)
0150        except ZeroDivisionError:
0151            return self.set(CPosition(COMPLEX_MAX,COMPLEX_MAX))
0152    def setHermitian(self):
0153        """set the Hermitian matrix representation of the point"""
0154
0155        A=1
0156        C=-self
0157        B=C.conjugate()
0158        D = B*C
0159        self._hermitian = Hermitian([[A,B],[C,D]])
0160        return self._hermitian
0161
0162
0163class _zFreePosition(_zPoint):
0164    """ an O dimensional object of the complex plane that can picked and moved, either
0165freely on the plane or constrained to a given geoemtric object of the plane. 
0166             
0167inherits:  
0168 
0169    class. `_zPoint`__ 
0170
0171defining instance attributes 
0172
0173    - real: the real coordinate
0174    - imag: the imaginary coordinate
0175
0176__ class-base.abstracts._complex._zPoint.html
0177    """
0178
0179    def __init__(self,*args,**kws):
0180        """intialize attributes, with keywords arguments or defaults"""
0181
0182        _zPoint.__init__(self,*args,**kws)
0183        self.color = kws.get("color",BLUE)
0184        self.initcolor=self.color
0185        self.pointsize=kws.get("pointsize",.12)
0186        self.style=ROUND
0187        self.initpointsize=self.pointsize
0188        self.initpos=CPosition(self.real,self.imag)
0189        freepoints.append(self)
0190        self.dependants=[]
0191
0192    def _add_dependant(self,e):
0193        """ called when the intilization and registration of an object
0194        determines it to be dependant of an instance derived from  _zFreePosoition"""
0195
0196        self.dependants.append(e)
0197
0198    def reset(self):
0199        """Reset to position of start-up input"""
0200        self.pointsize=self.initpointsize
0201        self.drawcolor = self.initcolor
0202        self.set(self.initpos)
0203        _zPoint.update(self)
0204
0205
0206class _zLine(Element,Z_Draw.zdrawLine):
0207    """a straight line of the complex plane , representing a complex circle of 
0208infinite radius
0209             
0210inherits 
0211
0212    Element__ , zdrawLine__  
0213
0214defining instance attributes 
0215    
0216    - p1: a complex point on the line 
0217    - p2  a complex point on the line 
0218    - _hermitian: the hermitian matrix associated with the line
0219
0220__ class-base.abstracts._element.Element.html
0221__ class-base.drawing.zdraw.zdrawLine.html
0222    
0223    """
0224
0225    __opts__ = _zPoint.__opts__[:]+["linewidth","label_ratio","show_normal"]
0226    __opts__.remove("pointsize")
0227
0228    def __init__(self,*args,**kws):
0229        """intialize attributes, with keywords arguments or defaults"""
0230
0231        Element.__init__(self,*args,**kws)
0232        self.precision = kws.get("precision",40)
0233        self.linewidth=kws.get("linewidth",.02)
0234        self.color = kws.get("color",GREEN)
0235        self.show_normal=kws.get("show_normal",False)
0236        self.normal_width=kws.get("normal_width",.02)
0237        self.bounds1 = None
0238        self.bounds2 = None
0239        if self.label:
0240            self.fontsize=kws.get("fontsize",TINYFONT)
0241            self.fontcolor=kws.get("fontcolor",BLACK)
0242            self.fontXoffset=kws.get("fontXoffset",self.pointsize/10.+3)
0243            self.fontYoffset=kws.get("fontYoffset",self.pointsize/10.+3)
0244            self.lratio=kws.get("label_ratio",.5)
0245        self.p1=_zPoint(append=False)
0246        self.p2=_zPoint(append=False)
0247
0248    def findSelf(self):
0249        self.set_hermitian_from_points()
0250        return True
0251
0252    def __description(self):
0253        if hasattr(self,'p1'):
0254            return self.__class__.__name__ + "(%s,%s)" %(self.p1,self.p2)
0255        else:
0256            return self.__class__.__name__ + "(%s,%s)"
0257
0258    def __repr__(self):
0259        return self.__description()
0260
0261    def __str__(self):
0262        return self.__description()
0263
0264    def transform(self,mat,h_circle):
0265        """ transforms the Hermitian matrix defining the line by the given Mobius transformation
0266        matrix"""
0267
0268        t1=inverse(mat)
0269        h_circle._hermitian =self._hermitian.transform(mat)
0270        h_circle.set_radius_from_hermitian()
0271        return True
0272
0273    def to_uSphere(self,uCircle):
0274        """ sets the given ucircle to the projection of the line to the Riemann sphere"""
0275
0276        h=self._hermitian
0277        a=h.B+h.C
0278        b=(h.B-h.C)*1j
0279        c=h.D-h.A
0280        d=h.D+h.A
0281        v=vector(a.real,b.real,-c)
0282        try:
0283            uCircle._u.set(v.norm())
0284            uCircle._d = d =  d/v.mag*-1.
0285        except ZeroDivisionError:
0286            uCircle._u.set(vector(0,0,1))
0287            uCircle._d=0
0288        u=uCircle._u
0289        uCircle.set_s_from_u(u)
0290        uCircle._center.set(u*d)
0291        pdist = uCircle._center.mag2
0292        uCircle._radiusSquared=1-pdist
0293        #try:
0294        uCircle._radius=sqrt(uCircle._radiusSquared)
0295        #except ValueError:
0296        #    uCircle._radius=0
0297        return True
0298
0299    def getDirection(self):
0300        """the normalized direction vector of the line"""
0301
0302        v = self.p2-self.p1
0303        v /= v.mod()
0304        return v
0305
0306    def getAngle(self):
0307        """the angle of the line with the real axis"""
0308
0309        return self.getDirection().arg()
0310
0311    def getNormal(self):
0312
0313        c = CPosition(self.p2.imag-self.p1.imag,self.p1.real-self.p2.real)
0314        c /= c.mod()
0315        return c
0316
0317    def getDistance(self):
0318        n=self.getNormal()
0319        return n.real*self.p1.real + n.imag*self.p1.imag
0320
0321    def set_hermitian_from_points(self):
0322        d=self.getNormal().arg()
0323        n = CPosition( cos(d),sin(d))
0324        self._hermitian = Hermitian([[0,n.conjugate()],[n,-self.getDistance()*2]])
0325
0326
0327class _zCircle(Element,Z_Draw.zdrawCircle):
0328    """ the set of points the complex plane that are equidistant from a given point 
0329of the plane
0330
0331inherits:  
0332
0333    Element__ , zdrawCircle__                
0334
0335
0336defining instance attributes 
0337    
0338    - _center: the complex point on circle's center
0339    - _cpoint: a complex point on circle circumference
0340    - _hermitian: the hermitian matrix associated with the circle
0341    - _radius: the circle's radius 
0342    - _radiusSquared: the square of the circle's radius 
0343
0344__ class-base.abstracts._element.Element.html
0345__ class-base.drawing.zdraw.zdrawCircle.html
0346   
0347    """
0348
0349    __opts__= Element.__opts__[:] + ["style","linewidth","precision","fixed","circle_type","O"]
0350
0351    def __init__(self,*args,**kws):
0352        """intialize attributes, with keywords arguments or defaults"""
0353
0354        self.precision = kws.get("precision",40)
0355        self.style = kws.get("style",LINES)
0356        self.linewidth = kws.get("linewidth",.02)
0357        self.show_normal=False
0358        self._u=vector(0,0,1)
0359        self._s=CPosition(1,0)
0360        self._radiusSquared=0
0361        self._radius=0
0362        self._d=0
0363        self._center=_zPoint(append=False)
0364        self._cpoint=_zPoint(append=False)
0365        Element.__init__(self,*args,**kws)
0366        self.color = kws.get("color",BLUE)
0367        O = kws.get("O","+")
0368        if O=="+":
0369           self.a=1
0370        else:
0371           self.a=-1
0372
0373    def findSelf(self):
0374        self.set_hermitian_from_radius()
0375        return True
0376
0377    def __description(self):
0378        return self.__class__.__name__ + "(%s,%.5f)" %(self._center,self._radius)
0379
0380    def __repr__(self):
0381        return self.__description()
0382
0383    def __str__(self):
0384        return self.__description()
0385
0386    def transform(self,mat,h_circle):
0387        t1=inverse(mat)
0388        h_circle._hermitian=self._hermitian.transform(mat)
0389        h_circle.set_radius_from_hermitian()
0390        return True
0391
0392    def to_uSphere(self,uCircle):
0393        h=self._hermitian
0394        a=h.B+h.C
0395        b=(h.B-h.C)*1j
0396        c=h.D-h.A
0397        d=h.D+h.A
0398        v=vector(a.real,b.real,-c)
0399        try:
0400           uCircle._u.set(v.norm())
0401           uCircle._d = d =  d/v.mag*-1
0402        except ZeroDivisionError:
0403           uCircle._u.set(vector(0,0,1))
0404           uCircle._d=0
0405        u=uCircle._u
0406        uCircle.set_s_from_u(u)
0407        uCircle._center.set(u*d)
0408        pdist = uCircle._center.mag2
0409        uCircle._radiusSquared=1-pdist
0410        try:
0411            uCircle._radius=rad=sqrt(uCircle._radiusSquared)
0412        except ValueError:
0413            uCircle._radius=rad=0
0414        uCircle._cpoint.set(uCircle._s*rad+uCircle._center)
0415        return True
0416
0417    def set_radius_from_hermitian(self):
0418        h=self._hermitian
0419        try:
0420            self._center.set(-h.C/h.A)
0421            fact=h.D/h.A
0422            self._radiusSquared = absolute(self._center.mod2() - fact)
0423            self._radius = sqrt(self._radiusSquared)
0424        except ZeroDivisionError:
0425            pass
0426
0427    def set_radius_from_cpoint(self):
0428        self._radiusSquared=self._center.distanceSquared(self._cpoint)
0429        self._radius=sqrt(self._radiusSquared)
0430
0431    def set_hermitian_from_radius(self):
0432        center=self._center
0433        A=self.a
0434        C=center*-A
0435        B=C.conjugate()
0436        D=(center*center.conjugate()-self._radiusSquared)*A
0437
0438        self._hermitian= Hermitian([[A,B],[C,D]])
0439
0440
0441class _zPointArray(Element,Z_Draw.zdrawArray):
0442    """an array of points on the complex plane with a defined 
0443geometric relationship
0444
0445inherits  
0446    
0447    Element__ , zdrawArray__ 
0448
0449defining instance attributes 
0450    
0451    zpoints: positioned complex points of the array
0452
0453__ class-base.abstracts._element.Element.html
0454__ class-base.drawing.zdraw.zdrawArray.html
0455     
0456     """
0457
0458    __opts__= Element.__opts__[:] + ["pointsize","density","style"]
0459
0460    def __init__(self,*args,**kws):
0461        """intialize attributes, with keywords arguments or defaults"""
0462
0463        Element.__init__(self,*args,**kws)
0464        self.density=kws.get('density',50)
0465        self.pointsize=kws.get('pointsize',.1)
0466        self.style=kws.get("style",DIAMOND)
0467        self.color = kws.get("color",CYAN)
0468        self.zpoints=[]
0469        self.deps=[]
0470
0471    def __iter__(self):
0472        for zpoint in self.zpoints:
0473            yield zpoint
0474
0475    def __len__(self):
0476        return len(self.zpoints)
0477
0478    def init(self):
0479        p_append=self.zpoints.append
0480        i=0
0481        while i < self.density:
0482            n=_zPoint(pointsize=self.pointsize,color=self.
0483                 color,level=self.level,append=False,style=self.style)
0484            n.init()
0485            p_append(n)
0486            i+=1
0487        Element.init(self)
0488
0489
0490class _zLineArray(Element,Z_Draw.zdrawArray):
0491    """an array of lines on the complex plane with a defined geometric relationship
0492
0493inherits  
0494
0495    Element__ ,  zdrawArray__
0496
0497defining instance attributes 
0498    
0499    zlines: positioned lines of the complex plane
0500
0501__ class-base.abstracts._element.Element.html
0502__ class-base.drawing.zdraw.zdrawArray.html
0503
0504    """
0505
0506    __opts__= Element.__opts__[:] + ["density","linewidth"]
0507
0508    def __init__(self,*args,**kws):
0509        """intialize attributes, with keywords arguments or defaults"""
0510
0511        Element.__init__(self,*args,**kws)
0512        self.density=kws.get('density',50)
0513        self.linewidth=kws.get('linewidth',.015)
0514        self.color = kws.get("color",YELLOW)
0515        self.zlines=[]
0516        self.deps=[]
0517
0518    def __iter__(self):
0519        for zline in self.zlines:
0520            yield zline
0521
0522    def __len__(self):
0523        return len(self.zlines)
0524
0525    def init(self):
0526        p_append=self.zlines.append
0527        i=0
0528        while i < self.density:
0529            n=_zLine(linewidth=self.linewidth,color=self.
0530                color,level=self.level,append=False)
0531            n.init()
0532            p_append(n)
0533            i+=1
0534        Element.init(self)
0535
0536
0537class _zCirclePencil(Element,Z_Draw.zdrawArray):
0538    """an array of circles on the complex plane with a defined geometric relationship
0539
0540inherits  
0541
0542    Element__ , zdrawArray__ 
0543
0544defining instance attributes 
0545    
0546    zcircles: positioned circles of the complex plane
0547
0548__ class-base.abstracts._element.Element.html
0549__ class-base.drawing.zdraw.zdrawArray.html
0550    
0551    """
0552
0553    __opts__= Element.__opts__[:] + ["linewidth","style","density","precision"]
0554
0555    def __init__(self,*args,**kws):
0556        """intialize attributes, with keywords arguments or defaults"""
0557
0558        self.precision=kws.get("precision",70)
0559        self.linewidth=kws.get("linewidth",.015)
0560        self.style=kws.get("style",LINES)
0561        self.density=kws.get("density",30)
0562        Element.__init__(self,*args,**kws)
0563        self.color=kws.get("color",RED)
0564        self.deps=[]
0565        self.zcircles=[]
0566
0567    def __len__(self):
0568        return len(self.circles)
0569
0570    def __iter__(self):
0571        for zcircle in self.zcircles:
0572            yield zcircle
0573
0574    def init(self):
0575        c_append=self.zcircles.append
0576        i=0
0577        while i < self.density:
0578            n=_zCircle(color=self.color,level=self.level,
0579                   precision=self.precision,style=self.style,append=False)
0580            n.init()
0581            c_append(n)
0582            i+=1
0583        Element.init(self)
0584
0585
0586class _zTransformation(Element,Z_Draw.zdrawArray):
0587    """a Mobius transformation of geometric objects of the complex plane
0588
0589inherits  
0590
0591    Element__ ,  zdrawArray__
0592
0593defining instance attributes 
0594    
0595    _mobius: the Mobius tranformation matrix defining the transformation
0596
0597__ class-base.abstracts._element.Element.html
0598__ class-base.drawing.zdraw.zdrawArray.html
0599   
0600   """
0601
0602    __opts__= Element.__opts__[:] + ["normal_from"]
0603
0604    def __init__(self,*args,**kws):
0605        """intialize attributes, with keywords arguments or defaults"""
0606
0607        Element.__init__(self,*args,**kws)
0608        self.color = kws.get("color",None)
0609        self.level = kws.get("level",None)
0610        self.deps=[]
0611
0612    def findSelf(self):
0613        if self._getMobius():
0614            for e,t in zip(self.elements,self.transforms):
0615                e.transform(self._mobius,t)
0616            return True
0617        else:
0618            return False
0619
0620    def __iter__(self):
0621        for transform in self.transforms:
0622            yield transform
0623
0624    def _getMobius(self):
0625        pass
0626
0627    def init(self):
0628        self.transforms=[]
0629        for e in self.elements:
0630            if self.color:
0631                color=self.color
0632            else:
0633                color=e.color
0634            if self.level:
0635                level=self.level
0636            else:
0637                level=e.level
0638            if isinstance(e,_zPoint):
0639                self.transforms.append(_zPoint(pointsize=e.pointsize,
0640                                       color=color,level=level,export = self.export,
0641                                       style=e.style,append=False))
0642            elif isinstance(e,_zCircle):
0643                self.transforms.append(_zCircle(linewidth=e.linewidth,
0644                                       color=color,level=level,export = self.export,
0645                                       append=False))
0646            elif isinstance(e,_zLine):
0647                self.transforms.append(_zCircle(linewidth=e.linewidth,
0648                                       color=color,level=level,export = self.export,
0649                                       append=False))
0650            elif isinstance(e,_zCirclePencil):
0651                for circle in e:
0652                    self.transforms.append(_zCircle(linewidth=e.linewidth,
0653                                          color=color,level=level,export = self.export,
0654                                          append=False))
0655        for t in self.transforms:
0656            t.init()
0657        Element.init(self)
0658
0659# module attributes
0660__author__ = "Arthur Siegel <ajsiegel@optonline.com>"
0661__date__ = "Date: 2006-02-02 "
0662__revision__ = "Revision: a1"
0663__url__ = "URL: http://sourceforge.net/projects/pygeo"
0664__copyright__ ="GPL <http://www.opensource.org/licenses/gpl-license.php>"