In [1]:
# this line is needed for the web notebook only
%matplotlib inline

CIVL4250 Numerical Methods in Engineering

Dr Dorival Pedroso

Code available on https://github.com/cpmech/CIVL4250py

Triangles

This notebook explains how triangles can be drawn using matplotlib.

The goals for the code developed here are:

  1. Define a vertex
  2. Define a triangle
  3. Loop over a set of triangles
  4. Draw a triangle
    1. Write the vertices numbers defining each triangle
    2. Write the triangle name; e.g. "P"

1 Define a vertex

A vertex can be uniquely defined by simply recording its Cartesian coordinates x and y. Nonetheless, for the sake of illustration and classification, we will also give an identification number (id) and a tag to each vertex.

The id is useful to identify vertices without the need for comparing coordinates.

The tag is useful to classify vertices; i.e. to group vertices. For example, tagging all vertices located along a given line defines a group of vertices. We will use negative integers as tags because this helps to differentiate between ids and tags. By using negative numbers, we can also say that 0 means that the vertex is not tagged.

In this way, one vertex is defined by a list with 4 items as follows


In [2]:
vertex_A = [0, -100, 0.5, 0.5] # id, tag, x, y

where the 4 items are the id, the tag (a negative number or zero), followed by the x and y coordinates.

For example, suppose another vertex in the same group of vertex_A is to be defined, then


In [3]:
vertex_B = [1, -100, 0.5, 1.0]

If a set of vertices is needed, this can be easily defined by means of


In [4]:
vertices = [vertex_A, vertex_B]

or


In [5]:
vertices = [[0, -100, 0.5, 0.5],
            [1, -100, 0.5, 1.0]]

which directly creates a list of lists with all vertices.

2 Define a triangle

To define triangles, a list of vertices V is firstly created as explained above; thus, suppose a list of vertices is given by


In [6]:
#     id   tag    x    y
V = [[ 0, -100, 0.0, 0.0], # vert # 0
     [ 1, -100, 1.5, 0.0], # vert # 1
     [ 2, -200, 1.5, 2.5], # vert # 2
     [ 3, -300, 0.0, 2.5]] # vert # 3

Then, a triangle can be defined by just selecting 3 vertices in the list above. Nonetheless, an id and a tag will also be given to each triangle.

Therefore, one triangle is specified by a list with 3 items as follows


In [7]:
triangle_T = [0, -1, [0, 1, 2]] # id, tag, list_of_vertices_ids

where:

  1. the first item is the id of the triangle;
  2. the second item is the tag of the triangle; and
  3. the third item is a list with the ids of the vertices in V.

Another triangle could be defined as follows


In [8]:
triangle_S = [1, -2, [0, 2, 3]]

and a list of triangles can be written as


In [9]:
C = [triangle_T, triangle_S]

or


In [10]:
C = [[0, -1, [0, 1, 2]],
     [1, -2, [0, 2, 3]]]

where the the variable C is used instead of triangles just for convenience. C means Cells.

3 Loop over a set of triangles

This can be easily accomplished with


In [11]:
print '=' * 58
print '%5s%5s (%6s,%6s) (%6s,%6s) (%6s,%6s)' % ('id', 'tag', 'x0','y0', 'x1','y1', 'x2','y2')
print '-' * 58
for c in C:
    id     = c[0]        # triangle's ID
    tag    = c[1]        # triangle's Tag
    n0     = c[2][0]     # first node defining this triangle
    n1     = c[2][1]     # second node defining this triangle
    n2     = c[2][2]     # third node defining this triangle
    P      = V[n0]       # first vertex (extracting from previous V list)
    Q      = V[n1]       # second vertex
    R      = V[n2]       # third vertex
    x0, y0 = P[2], P[3]  # coordinates of first vertex
    x1, y1 = Q[2], Q[3]  # coordinates of second vertex
    x2, y2 = R[2], R[3]  # coordinates of third vertex
    print '%5s%5s (%6.2f,%6.2f) (%6.2f,%6.2f) (%6.2f,%6.2f)' % (id, tag, x0,y0, x1,y1, x2,y2)
print '=' * 58


==========================================================
   id  tag (    x0,    y0) (    x1,    y1) (    x2,    y2)
----------------------------------------------------------
    0   -1 (  0.00,  0.00) (  1.50,  0.00) (  1.50,  2.50)
    1   -2 (  0.00,  0.00) (  1.50,  2.50) (  0.00,  2.50)
==========================================================

4 Draw a triangle

Polygons are drawn with matplotlib using the path and patches commands. See e.g. Matplotlib Shapes.

path defines a polygon by means of an analogy to drawing by hand. First, the pen is moved (MOVETO) to the first point, then it is moved along a line to the next point (LINETO). Finally, the patch (the polygon) is created by closing the set of lines (CLOSEPOLY).

For instance, the patch defining one triangle is given by


In [12]:
import matplotlib.pyplot  as plt
import matplotlib.path    as mph
import matplotlib.patches as mpc

# 1: define the path data corresponding to the geometry of one Triangle => like drawing by hand
dum = 0 # dummy variable
path_data = [ [mph.Path.MOVETO,    [0.0, 0.0] ],
              [mph.Path.LINETO,    [1.5, 0.0] ],
              [mph.Path.LINETO,    [1.5, 2.5] ],
              [mph.Path.CLOSEPOLY, [dum, dum] ] ]

# 2: create the path structure from the path data
blackbox = zip(*path_data) # don't worry about this line
path = mph.Path(blackbox[1], blackbox[0])

# 3: create the shape structure with colors and lines for MatPlotLib
shape = mpc.PathPatch(path, facecolor='lightyellow', edgecolor='black')

# 4: grab the current axis (if none, a new figure is created) and then add the shape to it
plt.gca().add_patch(shape)
plt.axis('equal');


Now, a function can be created encapsulating the previous commands for the sake of convenience. In this way, the function could be called within a loop and thus many triangles could be drawn.

This function can also perform the writing of triangles' label (=ids) as requested.

A function to draw one triangle is thus defined as follows


In [13]:
def DrawTriangle(V, T, color='#e9eefe'):
    
    # 0: collect triangles' data
    id, tag    = T[0],    T[1]
    n0, n1, n2 = T[2][0], T[2][1], T[2][2]
    P,  Q,  R  = V[n0],   V[n1],   V[n2]
    
    # 1: define the path data corresponding to one Triangle
    path_data = [ [mph.Path.MOVETO,    P[2:4] ],
                  [mph.Path.LINETO,    Q[2:4] ],
                  [mph.Path.LINETO,    R[2:4] ],
                  [mph.Path.CLOSEPOLY,  [0,0] ] ]

    # 2: create the path structure from the path data
    blackbox = zip(*path_data)
    path = mph.Path(blackbox[1], blackbox[0])

    # 3: create the shape structure with colors and lines for MatPlotLib
    shape = mpc.PathPatch(path, facecolor=color, edgecolor='black')

    # 4: grab the current axis (if none, a new figure is created) and then add the shape to it
    plt.gca().add_patch(shape)
    
    # 5: write triangle's id
    centre_x = (P[2] + Q[2] + R[2]) / 3.0
    centre_y = (P[3] + Q[3] + R[3]) / 3.0
    plt.text(centre_x, centre_y, '%d' % id, color='blue')

where V is the list of vertices and T is one triangle defined as in the previous sections.

The color string can be a name as defined here or a HTML color code; we will use the latter.

Also note that in Python, arguments followed by "= something" are optional and are already defined with default values. For instance, the color '#e9eefe' is already chosen.

The vertices ids can be easily written also using the text command.

Now, all triangles in C can be drawn as follows


In [14]:
# draw triangles
for c in C:
    DrawTriangle(V, c)

# write vertices ids
for v in V:
    plt.text(v[2], v[3], '%d' % v[0], color='red')

# use equal x-y scales
plt.axis('equal');