Ticket #9855: trac_9855_tachyon.patch

File trac_9855_tachyon.patch, 17.5 KB (added by mhampton, 10 years ago)

Improves tachyon support

  • sage/plot/plot3d/tachyon.py

    # HG changeset patch
    # User Marshall Hampton <hamptonio@gmail.com>
    # Date 1284238603 18000
    # Node ID 29d76f1e05dc8be7029720ee5d9640087671f162
    # Parent  c58f82863c75b2e53662e08cd550bb398653174c
    trac_9855: fix support for project options in tachyon
    
    diff -r c58f82863c75 -r 29d76f1e05dc sage/plot/plot3d/tachyon.py
    a b  
    1515
    1616One can also directly control Tachyon, which gives a huge amount of
    1717flexibility. For example, here we directly use Tachyon to draw 3
    18 spheres on the coordinate axes. Notice that the result is
    19 gorgeous::
     18spheres on the coordinate axes::
    2019
    2120    sage: t = Tachyon(xres=500,yres=500, camera_center=(2,0,0))
    2221    sage: t.light((4,3,2), 0.2, (1,1,1))
     
    2827    sage: t.sphere((0,0,0.5), 0.2, 't4')
    2928    sage: t.show()
    3029
     30For scenes with many reflections it is helpful to increase the raydepth option, and turn on antialiasing.  The following scene is an extreme case with many reflections between four cotangent spheres::
     31
     32    sage: t = Tachyon(camera_center=(0,-4,1), xres = 800, yres = 600, raydepth = 12, aspectratio=.75, antialiasing = 4)
     33    sage: t.light((0.02,0.012,0.001), 0.01, (1,0,0))
     34    sage: t.light((0,0,10), 0.01, (0,0,1))
     35    sage: t.texture('s', color = (.8,1,1), opacity = .9, specular = .95, diffuse = .3, ambient = 0.05)
     36    sage: t.texture('p', color = (0,0,1), opacity = 1, specular = .2)
     37    sage: t.sphere((-1,-.57735,-0.7071),1,'s')
     38    sage: t.sphere((1,-.57735,-0.7071),1,'s')
     39    sage: t.sphere((0,1.15465,-0.7071),1,'s')
     40    sage: t.sphere((0,0,0.9259),1,'s')
     41    sage: t.plane((0,0,-1.9259),(0,0,1),'p')
     42    sage: t.show() # long time
     43
     44Different projection options are available.  The following examples all use a sphere and cube::
     45
     46    sage: cedges = [[[1, 1, 1], [-1, 1, 1]], [[1, 1, 1], [1, -1, 1]], [[1, 1, 1], \
     47          [1, 1, -1]], [[-1, 1, 1], [-1, -1, 1]], [[-1, 1, 1], [-1, 1, -1]], \
     48          [[1, -1, 1], [-1, -1, 1]], [[1, -1, 1], [1, -1, -1]], [[-1, -1, 1], \
     49          [-1, -1, -1]], [[1, 1, -1], [-1, 1, -1]], [[1, 1, -1], [1, -1, -1]],\
     50           [[-1, 1, -1], [-1, -1, -1]], [[1, -1, -1], [-1, -1, -1]]]
     51
     52The default projection is 'perspective'::
     53
     54    sage: t = Tachyon(xres = 800, yres = 600, camera_center = (-1.5,0.0,0.0), zoom = .2)
     55    sage: t.texture('t1',color=(0,0,1))
     56    sage: for ed in cedges:
     57    ...    t.fcylinder(ed[0],ed[1],.05,'t1')
     58    sage: t.light((-4,-4,4),.1,(1,1,1))
     59    sage: t.show()
     60
     61Another option is projection='fisheye', which requires frustrum information.  The frustrum data is [bottom angle, top angle, left angle, right angle]::
     62
     63    sage: t = Tachyon(xres = 800, yres = 600, camera_center = (-1.5,0.0,0.0), \
     64          projection='fisheye', frustum=(-1.2, 1.2, -1.2, 1.2))
     65    sage: t.texture('t1',color=(0,0,1))
     66    sage: for ed in cedges:
     67    ...    t.fcylinder(ed[0],ed[1],.05,'t1')
     68    sage: t.light((-4,-4,4),.1,(1,1,1))
     69    sage: t.show()
     70
     71Finally there is the projection=perspective_dof option.  This may not be implemented correctly; sometimes the setting seems to be ignored::
     72
     73    sage: T = Tachyon(xres=800,antialiasing=4, raydepth=10, projection = 'perspective_dof', focallength = '1.0', aperture = '.0025')
     74    sage: T.light((0,5,7),1.0,(1,1,1))
     75    sage: T.texture('t1', opacity=1, specular = .3)
     76    sage: T.texture('t2', opacity=1, specular = .3, color = (0,0,1))
     77    sage: T.texture('t3', opacity = 1, specular = 1, color = (1,.8,1), diffuse=0.2)
     78    sage: T.plane((0,0,-1),(0,0,1),'t3')
     79    sage: ttlist = ['t1','t2']
     80    sage: tt = 't1'
     81    sage: T.cylinder((0,0,.1),(1,1/3,0),.05,'t3')
     82    sage: for q in srange(-3,100,.15):
     83    ...    if tt == 't1':
     84    ...        tt = 't2'
     85    ...    else:
     86    ...        tt = 't1'
     87    ...    T.sphere((q,q/3+.3*sin(3*q),.1+.3*cos(3*q)), .1, tt)
     88    sage: T.show()
     89
     90Image files in the ppm format can be used to tile planes or cover cylinders or spheres.  In this example an image is created and then used to tile the plane::
     91
     92    sage: from sage.misc.misc import tmp_filename
     93    sage: T = Tachyon(xres = 800, yres = 600, camera_center = (-2.0,-.1,.3), projection='fisheye', frustum=(-1.0, 1.0, -1.0, 1.0))
     94    sage: T.texture('t1',color=(0,0,1))
     95    sage: for ed in cedges:
     96    ...    T.fcylinder(ed[0],ed[1],.05,'t1')
     97    sage: T.light((-4,-4,4),.1,(1,1,1))
     98
     99    sage: fname = tmp_filename()
     100    sage: T.save(fname+'.png')
     101    sage: r2 = os.system('convert '+fname+'.png '+DATA+'t1.ppm') # optional
     102    sage: T = Tachyon(xres = 800, yres = 600, camera_center = (-2.0,-.1,.3), projection='fisheye', frustum=(-1.0, 1.0, -1.0, 1.0))  # optional
     103    sage: T.texture('t1',color=(1,0,0), specular=.9)  # optional
     104    sage: T.texture('p1',color=(1,1,1),opacity = .1, imagefile=DATA+'t1.ppm', texfunc=9)  # optional
     105    sage: T.sphere((0,0,0),.5,'t1')  # optional
     106    sage: T.plane((0,0,-1),(0,0,1),'p1')  # optional
     107    sage: T.light((-4,-4,4),.1,(1,1,1))  # optional
     108    sage: T.show()  # optional
     109    sage: os.unlink(fname+'.png')  # optional
     110
     111
    31112AUTHOR:
    32113
    33114- John E. Stone (johns@megapixel.com): wrote tachyon ray tracer
     
    40121
    41122- Leif Hille: key idea for bugfix for texfunc issue (trac #799)
    42123
    43 - Marshall Hampton: improved doctests, rings, axis-aligned boxes.
     124- Marshall Hampton: improved doctests, rings, axis-aligned boxes, documentation.
    44125
    45126TODO:
    46127
     
    72153    - ``xres`` - (default 350)
    73154    - ``yres`` - (default 350)
    74155    - ``zoom`` - (default 1.0)
    75     - ``antialiasing`` - (default False)
     156    - ``antialiasing`` - (default False) Values larger than 1
     157      oversample, improves quality at the cost of speed.
    76158    - ``aspectratio``  - (default 1.0)
    77159    - ``raydepth`` - (default 5)
    78160    - ``camera_center`` - (default (-3, 0, 0))
    79161    - ``updir`` - (default (0, 0, 1))
    80162    - ``look_at`` - (default (0,0,0))
    81     - ``viewdir`` - (default None)
    82     - ``projection`` - (default 'PERSPECTIVE')
     163    - ``viewdir`` - (default None), otherwise list of three numbers
     164    - ``frustum`` - (default ''), otherwise list of four numbers. Only
     165      used with projection='fisheye'.
     166    - ``focallength`` - (default ''), otherwise a number. Only used
     167      with projection='perspective_dof'.
     168    - ``aperture`` - (default ''), otherwise a number.  Only used
     169      with projection='perspective_dof'.
     170    - ``projection`` - (default 'PERSPECTIVE'), otherwise
     171      'perspective_dof' or 'fisheye'.
    83172
    84173    OUTPUT: A Tachyon 3d scene.
    85174
     
    199288        sage: t.texture('white', color=(1,1,1), opacity=1, specular=1, diffuse=1)
    200289        sage: t.plane((0,0,-100), (0,0,-100), 'white')
    201290        sage: t.show()
     291
     292    Use of a fisheye lens perspective.
     293
     294    ::
     295   
     296        sage: T = Tachyon(xres = 800, yres = 600, camera_center = (-1.5,-1.5,.3), projection='fisheye', frustum=(-1.0, 1.0, -1.0, 1.0))
     297        sage: T.texture('t1',color=(0,0,1))
     298        sage: cedges = [[[1, 1, 1], [-1, 1, 1]], [[1, 1, 1], [1, -1, 1]],\
     299        [[1, 1, 1], [1, 1, -1]], [[-1, 1, 1], [-1, -1, 1]], [[-1, 1, 1], \
     300        [-1, 1, -1]], [[1, -1, 1], [-1, -1, 1]], [[1, -1, 1], [1, -1, -1]],\
     301        [[-1, -1, 1], [-1, -1, -1]], [[1, 1, -1], [-1, 1, -1]], [[1, 1, -1], \
     302        [1, -1, -1]], [[-1, 1, -1], [-1, -1, -1]], [[1, -1, -1], [-1, -1, -1]]]
     303        sage: for ed in cedges:
     304        ...    T.fcylinder(ed[0],ed[1],.05,'t1')
     305        sage: T.light((-4,-4,4),.1,(1,1,1))
     306        sage: T.show()
     307
     308    Use of the projection=perspective_dof option.  This may not be implemented correctly.
     309
     310    ::
     311
     312        sage: T = Tachyon(xres=800,antialiasing=4, raydepth=10, projection = 'perspective_dof', focallength = '1.0', aperture = '.0025')
     313        sage: T.light((0,5,7),1.0,(1,1,1))
     314        sage: T.texture('t1', opacity=1, specular = .3)
     315        sage: T.texture('t2', opacity=1, specular = .3, color = (0,0,1))
     316        sage: T.texture('t3', opacity = 1, specular = 1, color = (1,.8,1), diffuse=0.2)
     317        sage: T.plane((0,0,-1),(0,0,1),'t3')
     318        sage: ttlist = ['t1','t2']
     319        sage: tt = 't1'
     320        sage: T.cylinder((0,0,.1),(1,1/3,0),.05,'t3')
     321        sage: for q in srange(-3,100,.15):
     322        ...    if tt == 't1':
     323        ...        tt = 't2'
     324        ...    else:
     325        ...        tt = 't1'
     326        ...    T.sphere((q,q/3+.3*sin(3*q),.1+.3*cos(3*q)), .1, tt)
     327        sage: T.show()
     328
    202329    """
    203330    def __init__(self,
    204331                 xres=350, yres=350,
     
    210337                 updir = (0, 0, 1),
    211338                 look_at = (0,0,0),
    212339                 viewdir = None,
    213                  projection = 'PERSPECTIVE'):
     340                 projection = 'PERSPECTIVE', focallength = '',
     341                 aperture = '',
     342                 frustum = ''):
    214343        r"""
    215344        Creates an instance of the Tachyon class.
    216345
     
    229358        self._camera_center = camera_center
    230359        self._updir = updir
    231360        self._projection = projection
     361        self._focallength = focallength
     362        self._aperture = aperture
     363        self._frustum = frustum
    232364        self._objects = []
    233365        if viewdir is None:
    234366            self._viewdir = [look_at[i] - camera_center[i] for i in range(3)]
     
    346478
    347479            sage: t = Tachyon(raydepth = 16, zoom = 2, antialiasing = True)
    348480            sage: t._camera().split()[3:10]
    349             ['aspectratio', '1.0', 'antialiasing', '1', 'raydepth', '16', 'center']
     481            ['zoom', '2.0', 'aspectratio', '1.0', 'antialiasing', '1', 'raydepth']
    350482        """
    351         return r"""
     483        camera_out = r"""
    352484           camera
     485              projection %s"""%(tostr(self._projection))
     486        if self._focallength != '':
     487            camera_out = camera_out + r"""
     488              focallength %s"""%(float(self._focallength))
     489        if self._aperture != '':
     490            camera_out = camera_out + r"""
     491              aperture %s"""%(float(self._aperture))
     492        camera_out = camera_out + r"""
    353493              zoom %s
    354494              aspectratio %s
    355495              antialiasing %s
    356496              raydepth %s
    357497              center %s
    358498              viewdir %s
    359               updir %s
    360            end_camera
    361         """%(float(self._zoom), float(self._aspectratio),
     499              updir %s"""%(float(self._zoom),
     500             float(self._aspectratio),
    362501             int(self._antialiasing),
    363502             int(self._raydepth),
    364503             tostr(self._camera_center),
    365504             tostr(self._viewdir),
    366505             tostr(self._updir))
     506        if self._frustum != '':
     507            camera_out = camera_out + r"""
     508              frustum %s"""%(tostr(self._frustum))
     509        camera_out = camera_out + r"""
     510           end_camera"""
     511        return camera_out
    367512
    368513    def str(self):
    369514        r"""
     
    380525            sage: t.sphere((0.5,0,0), 0.2, 't3')
    381526            sage: t.sphere((0,0,0.5), 0.2, 't4')
    382527            sage: t.str().find('PLASTIC')
    383             567
     528            595
    384529        """
    385530        return r"""
    386531        begin_scene
     
    397542        r"""
    398543        Creates a light source of the given center, radius, and color.
    399544       
    400         EXAMPLES::
     545        TESTS::
    401546
    402547            sage: q = Tachyon()
    403548            sage: q.light((1,1,1),1.0,(.2,0,.8))
    404             sage: q.str().split('\n')[17]
    405             '        light center  1.0 1.0 1.0 '
     549            sage: lightindex = q.str().index('light')
     550            sage: q.str()[lightindex:].split('\n')[0]
     551            'light center  1.0 1.0 1.0 '
    406552        """
    407553        self._objects.append(Light(center, radius, color))
    408554
    409     def texfunc(self, type=0, center=(0,0,0), rotate=(0,0,0), scale=(1,1,1)):
     555    def texfunc(self, ttype=0, center=(0,0,0), rotate=(0,0,0), scale=(1,1,1), imagefile = ''):
    410556        r"""
    411557        INPUT:
    412558
     
    420566           5. 3D gradient noise function (can't remember what it looks
    421567              like)
    422568           6. Don't remember
    423            7. Cylindrical Image Map, requires ppm filename (don't know
    424               how to specify name in sage?!)
    425            8. Spherical Image Map, requires ppm filename (don't know
    426               how to specify name in sage?!)
    427            9. Planar Image Map, requires ppm filename (don't know how
    428               to specify name in sage?!)
     569           7. Cylindrical Image Map, requires ppm filename (with path)
     570           8. Spherical Image Map, requires ppm filename (with path)
     571           9. Planar Image Map, requires ppm filename (with path)
    429572
    430573        -  ``center`` - (default: (0,0,0))
    431574        -  ``rotate`` - (default: (0,0,0))
     
    439582            sage: t.plane((0,0,0),(0,0,1),'black')
    440583            sage: t.show()
    441584        """
    442         type = int(type)
    443         if type < 0 or type > 9:
     585        ttype = int(ttype)
     586        if ttype < 0 or ttype > 9:
    444587            raise ValueError, "type must be an integer between 0 and 9"
    445         return Texfunc(type,center,rotate,scale).str()
     588        return Texfunc(ttype,center,rotate,scale,imagefile=imagefile).str()
    446589
    447590    def texture(self, name, ambient=0.2, diffuse=0.8,
    448591                specular=0.0, opacity=1.0,
    449                 color=(1.0,0.0, 0.5), texfunc=0, phong=0, phongsize=.5, phongtype="PLASTIC"):
     592                color=(1.0,0.0, 0.5), texfunc=0, phong=0, phongsize=.5, phongtype="PLASTIC", imagefile = ''):
    450593        r"""
    451594        INPUT:
    452595
     
    490633            sage: show(t)
    491634        """
    492635        if texfunc and not isinstance(texfunc, Texfunc):
    493             texfunc = self.texfunc(int(texfunc))
     636            texfunc = self.texfunc(int(texfunc), imagefile=imagefile)
    494637        self._objects.append(Texture(name, ambient, diffuse,
    495638                                     specular, opacity, color, texfunc,
    496                                      phong,phongsize,phongtype))
     639                                     phong,phongsize,phongtype,imagefile=imagefile))
    497640
    498641    def texture_recolor(self, name, colors):
    499642        r"""
     
    570713        r"""
    571714        Creates an infinite plane with the given center and normal.
    572715
    573         EXAMPLES::
     716        TESTS::
    574717
    575718            sage: t = Tachyon()
    576719            sage: t.plane((0,0,0),(1,1,1),'s')
    577             sage: t.str()[338:380]
     720            sage: plane_pos = t.str().index('plane')
     721            sage: t.str()[plane_pos:plane_pos+42]
    578722            'plane center  0.0 0.0 0.0  normal  1.0 1.0'
    579723        """
    580724        self._objects.append(Plane(center, normal, texture))
     
    606750            sage: t = Tachyon()
    607751            sage: t.fcylinder((1,1,1),(1,2,3),.01,'s')
    608752            sage: len(t.str())
    609             423
     753            451
    610754        """
    611755        self._objects.append(FCylinder(base, apex, radius, texture))
    612756
     
    804948             tostr(self._color))
    805949
    806950class Texfunc:
    807     def __init__(self, type=0,center=(0,0,0), rotate=(0,0,0), scale=(1,1,1)):
     951    def __init__(self, ttype=0,center=(0,0,0), rotate=(0,0,0), scale=(1,1,1), imagefile = ''):
    808952        r"""
    809953        Creates a texture function.
    810954
     
    812956
    813957            sage: from sage.plot.plot3d.tachyon import Texfunc
    814958            sage: t = Texfunc()
    815             sage: t._type
     959            sage: t._ttype
    816960            0
    817961        """
    818         self._type = type
     962        self._ttype = ttype
    819963        self._center = center
    820964        self._rotate = rotate
    821965        self._scale = scale
     966        self._imagefile = imagefile
    822967
    823968    def str(self):
    824969        r"""
     
    829974            sage: from sage.plot.plot3d.tachyon import Texfunc
    830975            sage: t = Texfunc()
    831976            sage: t.str()
    832             '0 center  0.0 0.0 0.0  rotate  0.0 0.0 0.0  scale  1.0 1.0 1.0 '
     977            '0'
    833978        """
    834         if type == 0:
     979        if self._ttype == 0:
    835980            return "0"
    836         return r"""%d center %s rotate %s scale %s"""%(self._type,
     981        elif self._ttype < 7 and self._ttype > 0:
     982            return r"""%d center %s rotate %s scale %s"""%(self._ttype,
    837983                                                      tostr(self._center),
    838984                                                      tostr(self._rotate),
    839985                                                      tostr(self._scale))
     986        elif self._ttype < 9:
     987            return r"""%d %s center %s rotate %s scale %s"""%(self._ttype, self._imagefile,tostr(self._center),tostr(self._rotate),tostr(self._scale))
     988        elif self._ttype == 9:
     989            return r"""%d %s center %s rotate %s scale %s
     990            uaxis 1.0 0.0 0.0
     991            vaxis 0.0 1.0 0.0"""%(self._ttype,
     992                                                      self._imagefile,
     993                                                      tostr(self._center),
     994                                                      tostr(self._rotate),
     995                                                      tostr(self._scale))
     996        else:
     997            raise ValueError
     998
    840999
    8411000class Texture:
    8421001    def __init__(self, name, ambient=0.2, diffuse=0.8,
    8431002                 specular=0.0, opacity=1.0,
    844                  color=(1.0,0.0, 0.5), texfunc=0, phong=0, phongsize=0, phongtype="PLASTIC"):
     1003                 color=(1.0,0.0, 0.5), texfunc=0, phong=0, phongsize=0, phongtype="PLASTIC", imagefile = ''):
    8451004        r"""
    8461005        Stores texture information.
    8471006
     
    8621021        self._phong = phong
    8631022        self._phongsize = phongsize
    8641023        self._phongtype = phongtype
     1024        self._imagefile = imagefile
    8651025
    8661026    def recolor(self, name, color):
    8671027        r"""
     
    8781038            'color  0.1 0.2 0.3  '
    8791039        """
    8801040        return Texture(name, self._ambient, self._diffuse, self._specular, self._opacity,
    881                              color, self._texfunc, self._phong, self._phongsize, self._phongtype)
     1041                             color, self._texfunc, self._phong, self._phongsize, self._phongtype, self._imagefile)
    8821042
    8831043    def str(self):
    8841044        r"""