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"""
0023base abstract class from which all geometric objects are derived
0024"""
0025
0026#import constant values (e.g. color names)  used throughout
0027from pygeo.base.support.pygeoconstants import BLACK
0028
0029#import the 4x4 identity matrix defined in the pygeomath modul
0030from pygeo.base.analytics.pygeomath import identity
0031
0032#initialize list of all Elements in scene with effect on dependencies
0033elements=[]
0034
0035#initialize list of Elements that can be picked and moved - some have constraints
0036freepoints=[]
0037
0038def register(element,args):
0039    """ recursive function called upom the initialization of geometric ojects, 
0040    registering a direct or indirect dependance with movable points in a scene"""
0041
0042    import pygeo.base.abstracts._real as Real
0043    import pygeo.base.abstracts._complex as Complex
0044    import pygeo.base.abstracts._usphere as USphere
0045    for i in args:
0046        if  (isinstance(i,Real._FreePosition)
0047            or isinstance(i,Complex._zFreePosition)
0048            or isinstance(i,USphere._uFreePosition)) :
0049               i._add_dependant(element)
0050        if hasattr(i,'args'):
0051            register(element,i.args)
0052
0053def get_args(args):
0054    """ a function to filter geometric objects (i.e. descendants of the Element class)
0055    from other arguments (e.g. numeric) that intialize an object"""
0056    e=[]
0057    for arg in args:
0058        if isinstance(arg,Element):
0059            e.append(arg)
0060    return e
0061
0062def method_get(sigs,args):
0063    """arguments to Element classes are tested against their constructor signature and
0064    returned ordered as per signature"""
0065
0066    def order(sig,_args):
0067        w=[]
0068        args=_args[:]
0069        for S in sig:
0070            v=[issubclass(a.__class__,S) for a in args]
0071            if 1 in v:
0072                w.append(args[v.index(1)])
0073                args.pop(v.index(1))
0074        return w
0075    args=list(args)
0076    for i in range(len(args)):
0077        if type(args[i])== int:
0078            args[i]=float(args[i])
0079    ret=[]
0080    for S in sigs:
0081        if len(args) == len(S):
0082            final_args = order(S,args)
0083            if len(final_args) == len(S):
0084                 ret.append([final_args,sigs.index(S)])
0085    if ret:
0086        return max(ret)
0087    else:
0088        return None,None
0089
0090class Element(object):
0091    """
0092the abstract class from which all PyGeo geometric objects derive
0093"""
0094
0095    __opts__ = ["color","initcolor","trace","level",
0096              "texture","extend","povout","label","append","export"]
0097
0098    def __init__(self,*args,**kws):
0099        """ intialization of the common attributes of geometric objects to default
0100        values, overridden to the extent that avalable keyword arguemtns are provided"""
0101
0102        self.append = (kws.get("append",True))
0103
0104        # all classes have an associated list ('__opt__') of valid keyword arguments
0105        # and we test that given named arguments are valid.
0106        for key in kws:
0107            if key not in self.__opts__:
0108                print 'WARNING "%s" not a valid keyword for initialization of %s' %(key,self.__class__.__name__)
0109
0110        #we toggle this to 0 when a calculated point or an element dependent on such point
0111        #is unobtainable for 'real' space, i.e. it is an imaginary number
0112        self.Not_null=True
0113
0114        # we toggle this to 0 when we want an elment used in the construction calculations,, but not
0115        # to be rendered
0116        self.show=True
0117
0118        # the color in which is the screen representation of the object will be rendered
0119        self.color = (kws.get("color",BLACK))
0120
0121        # we need this to be able to reset the rendering color to that which is selected
0122        # by script where it might be changed temporarily by interactivity, e.g.
0123        # to indicate that it has been interactively picked.
0124        self.initcolor = self.color
0125
0126        # signify whether the inherited object is to be traced during an interactive session.
0127        # Used for rendering loci of points
0128        self.trace=kws.get("trace",False)
0129
0130        # the visibility of elements can be controlled from the GUI based on its 'level' attribute
0131        # set to 1 by default
0132        self.level=kws.get("level",1)
0133
0134        # certain elements can 'extend' other elements, e.g. a intersection point of
0135        # a line can extend the segment of the line rendered to the intersection point
0136        # this is a toggle
0137        self.extend=kws.get("extend",False)
0138        self.export=kws.get("export",True)
0139
0140        # a Pov-Ray recognised texture can be associated with certain elements when
0141        # exported to Pov-Ray
0142        self.texture=kws.get("texture",None)
0143
0144        # list that holds the VPython rendering primitives associated with a class
0145        self.rend=[]
0146
0147        #let's kept track whether the rendering is visible
0148        self.visible= True
0149
0150        # list that holds secondary ('extended') VPython rendering primitives
0151        # associated with a class
0152        self.crend=[]
0153
0154        # list that loci elements when doing tracing
0155        self.rtrace=[]
0156
0157        # text label to be rendered with element
0158        self.label=kws.get("label", None)
0159
0160        # flag indicating whether there has been a change in the Boolean return value
0161        # of the update calculation for an Element
0162        self.r_Flag=True
0163
0164        # flag indicating whether there has been a change in status of an Element on which
0165        # it is depednant from real to imaginary, or imaginary to real
0166        self.n_Flag=True
0167
0168        # flag indicating whether intial drawinf set-up has occurred
0169        self.init_draw=False
0170
0171        self.register=kws.get("register", True)
0172
0173        self.args=[]
0174
0175        if self.append:
0176            elements.append(self)
0177            self.args=get_args(args)
0178            register(self,self.args)
0179
0180        #self.init()
0181
0182    def __iter__(self):
0183        """ make all objects iterable, returning "self" in the simplest instance"""
0184
0185        all = [self] + self.rtrace
0186        for a in all:
0187            yield a
0188
0189    def __len__(self):
0190        """length of the iterable assocaited with the object"""
0191
0192        all = [self] + self.rtrace
0193        return len(all)
0194
0195    def update(self):
0196        """method that is called on change events, calling reoutines to find current
0197        position based on dependecies, current visibility status, etc."""
0198
0199        tflag =  self.findSelf()
0200        if tflag <> self.r_Flag:
0201            self._togglenull()
0202        self.setshow()
0203        self.r_Flag =tflag
0204
0205    def findSelf(self):
0206        """ the  function called to recalculate an objects postion at a change event,
0207        to be overridden in subclasses"""
0208
0209        return True
0210
0211    def rmatrix(self):
0212        """each object has a 4x4 matrix associated with it, representing the object's axis of
0213        rotation and translation from the origin. Needed for the implementation of the
0214        Projection class.  Set to the identity matrix by default."""
0215
0216        return identity(4).astype('d')
0217
0218    def _allreal(self):
0219        """test whether any Elements on which there is a dependency
0220        have become 'null', i.e. not calculatable - .e.g, imaginary when
0221        working within real space"""
0222
0223        nflag = 0 not in [p.Not_null for p in self.args]
0224        if nflag <> self.n_Flag:
0225            if not nflag:
0226                if self.Not_null:
0227                    self._setnull(True)
0228            else:
0229                self._setnull(False)
0230        self.n_Flag=nflag
0231
0232    def setshow(self):
0233        """check for change in visibiliy status and toggle
0234        visibility as necessary, reset change flag to 0 on
0235        effectuating change in visiblity"""
0236
0237        if self.Not_null and self.show:
0238            if not self.visible:
0239                for e in self:
0240                    for r in e.rend:
0241                        r.visible=True
0242                if self.label:
0243                    self.lab.visible=True
0244                for c in self.crend:
0245                    c.visible=True
0246                self.visible=True
0247        else:
0248            if self.visible:
0249                for e in self:
0250                    for r in e.rend:
0251                        r.visible=False
0252                if self.label:
0253                    self.lab.visible=False
0254                for c in self.crend:
0255                    c.visible=False
0256                self.visible=False
0257        self._redraw()
0258        if self.extend:
0259            self.setext()
0260
0261    def _togglenull(self):
0262        """ toggle the 'null' flag, indicating whether the current postion of the
0263        object's position is visible - e.g. is real when working in real space"""
0264
0265        if self.Not_null:
0266           self._setnull(True)
0267        else:
0268           self._setnull(False)
0269
0270    def _setnull(self,to_null=True):
0271        """ set object and the objects dependant on it to 'null' when
0272        its position cannot be made visible- e.g. imaginary when working in real
0273        space."""
0274
0275        if to_null:
0276            if self.Not_null:
0277                self.Not_null=False
0278                for d in self.deps:
0279                    d.Not_null=False
0280        else:
0281            if not self.Not_null:
0282                self.Not_null=True
0283                for d in self.deps:
0284                    d.Not_null=True
0285
0286    def init(self):
0287        """ method to be overridden for objects needing an supplementary initiliazation routine after
0288        the call to __init___"""
0289
0290        if self.level > 1:
0291            self.show=False
0292        if not self.init_draw:
0293            self.draw()
0294        if self.append:
0295            self.update()
0296
0297    def reset(self):
0298        """Routine to return Element to intial position.
0299        Default is to run update routine"""
0300
0301        self.update()
0302
0303    def reset_trace(self):
0304        """ method to be overridden to erase loci of objects that are traceable"""
0305
0306        pass
0307
0308    def reset_trace_curves(self):
0309        """ method to be overridden to erase curves traced by point objects"""
0310
0311        pass
0312
0313
0314# module attributes (for pudge auto documentation)
0315__author__ = "Arthur Siegel <ajsiegel@optonline.com>"
0316__date__ = "Date: 2006-02-02"
0317__revision__ = "Revision: a1"
0318__url__ = "http://sourceforge.net/projects/pygeo"
0319__license__ ="GPL <http://www.opensource.org/licenses/gpl-license.php>"