# 3D Graphics

# 3D Graphics

Sage offers a 3D Graphics API which works in a way very similar to it's 2D API. The API is made up of primitives
that can be combined together and then displayed using **show**. 3D Graphics are shown using a Java applet called
Jmol which is designed as viewer for chemical structures.

## 3D Graphics Primitives

Sage provides a variety of primitives for 3D Graphics from common shapes to platonic solids. Here is a list of the main primitives provided by Sage:

- line3d
- text3d
- arrow3d
- sphere
- cylinder
- cone
- box
- torus
- tetrahedron
- cube
- octahedron
- dodecahedron
- icosahedron

## Affine Transformations

Like many 3D APIs Sage supports affine transformations for 3D graphics including rotate, scale, and translate.

The following methods are provided:

- scale: takes a list of the values [x,y,z] specifying the amount to scale by.
- translate: takes a list of values [x,y,z] specifying the amount to translate by.
- rotate: takes a rotation vector v, and float specifying the rotation around v in radians.
- rotateX: takes a float value specifying the rotation around X in radians
- rotateY: takes a float value specifying the rotation around Y in radians
- rotateZ: takes a float value specifying the rotation around Z in radians

## Creating 3D Graphics

### Basics

If you've read the section on 2D Graphics you might remember that 2D graphics in Sage are created by adding primitives to a
**Graphics** object. However, due to a bug, or strange implementation decision which ever you prefer, this isn't possible
with 3D graphics (they did promise that this will change in future versions). We have a few possible options we can use instead.

Let's start with a simple example of creating a sphere with some text.

g = sphere( (0,0,0), 1 ) g += text3d( "This is a sphere", (1,1,1) ) g.show()

or we can also do it this way

g = 0 g += sphere( (0,0,0), 1 ) g += text3d( "This is a sphere", (1,1,1) ) g.show()

As you may have guessed by looking at the code *sphere( (0,0,0), 1 )* creates a sphere at position (0,0,0) with a radius of 1,
and *text3d( "This is a sphere", (1,1,1) )* creates the text *This is a sphere* at position (1,1,1).

### Colors

Colors can be set in several different ways for 3D objects in Sage. First colors can be specified as strings such as '**blue'** or as rgb values using a 3-Tuple such as (0.5,0.2,0.35). Secondly you can specify one color for the entire object or specify a color for each face of the object. How to specify colors on a per face basis is dependent on the type of object you have.

Example: Create a cube with all sides blue:

cube( (0,0,0), 2, color='blue' )

Example Create a cube with different colors for each face:

cube( (0,0,0), 2, color=['white', 'blue', 'green', 'orange', 'red', 'black'] )

## The show Function

The show function for 3D Graphics allows for several useful parameters:

- frame: boolean specifies whether or not there should be a frame drawn around the objects.
*Default: True* - figsize: integer, specifies the size of the Java applet.
- aspect_ratio: integer, default 1
- zoom: integer, default 1

# Tachyon Ray Tracer

In addition to the standard 3D Graphics API Sage also provides the ability to create 3D images using the Tachyon Ray Tracer.

## Creating Images with Standard Graphics3D Objects

The simplest way to create 3D images using Tachyon is with standard 3D Graphics object in Sage. This is done by passing the keyword parameter **viewer** to the show function and giving it the value **tachyon**. This will output a png image instead of starting up the jmol applet.

Simple Example:

s = sphere( (0,0,0), 1 ) s.show( viewer='tachyon' )

## Interacting Directly with Tachyon

Sage also provides a interface to interact directly with Tachyon. This process is a bit more complex and involves setting up a camera position as well as adding lights, textures, and objects.

Let's start by walking through a simple example:

We start by constructing a Tachyon object. We must pass in several parameters to the Tachyon constructor:

- xres: integer, image x resolution
- yres: integer, image y resolution
- camera_center: 3-Tuple, specifies the location of the camera
- look_at: 3-Tuple, specifies the location the camera is looking at
- updir: 3-Tuple, specifies the camera's up vector
- raydepth: integer

For anyone familiar with openGL the camera_center, look_at, and updir, correspond to directly to the parameters of gluLookAt.

Other parameters available include:

- antialiasing: boolean
- projection: string, default 'PERSPECTIVE'
- aspectratio: float, default 1.0

For our example we'll start with the camera at (0,5,-10) looking at the origin, with the up vector (0,1,0)

t = Tachyon( xres=800, yres=600, camera_center=(0,5,-10), look_at=(0,0,0), updir=(0,1,0), raydepth=24 )

Now we will add a light to our 'scene' using the *light* method.

The light method takes three parameters:

- center: 3-Tuple, specifies the location of the light
- radius: double, specifies the radius of the light
- color: 3-Tuple, specifies the rgb values of the light color

For our example we put our light at (-3,10,-2) with a radius of 1, and set the light color to white.

t.light( (-3,10,-2), 1, (1,1,1) )

Next we will create several textures which we can apply to our objects. The *texture* method takes quite a few parameters:

- name: string, unique identifier for the texture
- ambient: double, level of ambient light between 0 and 1
- diffuse: double, level of diffuse light between 0 and 1
- specular: double, level of specular light between 0 and 1
- opacity: double, opacity level 1.0 for solid, 0.0 for fully transparent
- color: 3-Tuple, rgb values of the texture color
- texfunc: a number between 0 and 9 representing one of the following options
- 0 : No special texture, plain shading
- 1 : 3D checkerboard function
- 2 : Grit Texture, randomized surface color
- 3 : 3D marble texture, uses object's base color
- 4 : 3D wood texture, light and dark brown
- 5 : 3D gradient noise function
- 6 : ??
- 7 : Cylindrical Image Map
- 8 : Spherical Image Map
- 9 : Planar Image Map

*Note:* texfunc options are taken via Sage's bug tracker, ticket #799

For our example we will create 5 different textures, t1-t5 which we'll use later:

t.texture( 't1', 0.2, 0.8, 0.8, 1.0, (1.0,0.0,0.5) ) t.texture( 't2', 0.2, 0.8, 0.8, 1.0, (1.0,0.0,1.0) ) t.texture( 't3', 0.2, 0.8, 0.8, 1.0, (1.0,0.0,0.0) ) t.texture( 't4', 0.2, 0.8, 0.8, 1.0, (0.0,1.0,0.0) ) t.texture( 't5', 0.2, 0.8, 0.8, 1.0, (0.0,0.0,1.0) )

Next for reasons we will see in a moment we will create a list of our texture names:

textures = ['t1','t2','t3','t4','t5']

Now we will add spheres in a circle, choosing a random texture for each circle. The *sphere* method takes 3 parameters:

- center: 3-Tuple, location of the center of the sphere
- radius: float, radius of the sphere
- texture: string, string identifier of the texture to be applied to the sphere

[ t.sphere( (5*sin(x),0,5*cos(x)), 1.0, textures[randint(0,len(textures)-1)] ) for x in [0..2*pi-.5,step=1] ]

Now we can use the *show* method to display our picture:

t.show()

Here's the resulting image:

## A more complex example

The first example produced a very simple image. This example from sagemath.org creates a more complex image drawing points on an elliptic curve.

We start off similar to the previous example by constructing our Tachyon object and adding lights and textures. *You may have notice there isn't an up vector specified. That's because it defaults to (0,1,0), I just put it in the previous example to explain it's purpose.*

t = Tachyon(xres=1000, yres=800, camera_center=(2,7,4), look_at=(2,0,0), raydepth=4) t.light((10,3,2), 1, (1,1,1)) t.light((10,-3,2), 1, (1,1,1)) t.texture('black', color=(0,0,0)) t.texture('red', color=(1,0,0)) t.texture('grey', color=(.9,.9,.9))

Next we add a plane and two cylinders to the scene:

t.plane((0,0,0),(0,0,1),'grey') t.cylinder((0,0,0),(1,0,0),.01,'black') t.cylinder((0,0,0),(0,1,0),.01,'black')

Next we create an **EllipticCurve** object, then we add a 100 new textures and spheres to the scene:

E = EllipticCurve('37a') P = E([0,0]) Q = P n = 100 for i in range(n): Q = Q + P c = i/n + .1 t.texture('r%s'%i,color=(float(i/n),0,0)) t.sphere((Q[0], -Q[1], .01), .04, 'r%s'%i)

Finally we display the image using the *show* method:

t.show()

Here is the resulting image: