Skip to content
Eric Kerfoot edited this page Dec 12, 2016 · 11 revisions

Meshes are composed of 3 elements:

  • Nodes: a Vec3Matrix of nodes, this is either 1 column of cartesian coordinates, or 3 columns of (coordinates, normals, xi) triples.
  • Indices: a series of IndexMatrix objects representing topology indices, these are indexing functions into the node matrix or into field matrices
  • Fields: a series of RealMatrix objects representing stored field data, these can have any number of columns but each row is the data element for an element in a topology

Meshes are either the data for scene objects or are the representation data derived from scene object meshes. Typically mesh data is stored in a PyDataSet object which handles creating and organizing the three classes of data. A PyDataSet has one matrix for nodes but can contain multiple matrices for indices or fields.

The index and field matrices define metadata tags which describe their intended use and relationships to one another. The method meta() is used to query and set metadata values, either manually for individual matrices or for whole data sets by the PyDataSet object when it is constructed. A PyDataSet also can have metadata tags which describe properties, eg. whether it was cloned from another object. The StdProps enumeration lists the names of metadata tags in use by the framework.

Nodes

For a scene object data set, the nodes matrix is a single column list of points in Cartesian space. This matrix can be constructed manually, for example to define the three vertices of a triangle:

vecs=Vec3Matrix('vecs',3)
vecs[0]=vec3(0,0,0)
vecs[1]=vec3(1,0,0)
vecs[2]=vec3(0.5,0,1)

An easier way of doing the above is with the helper function listToMatrix:

vecs=listToMatrix([vec3(0,0,0),vec3(1,0,0),vec3(0.5,0,1)],'vecs')

Indices

Index matrices define the topology of meshes by indexing those values in the nodes or fields matrices that define each element. Each row of an index matrix defines one element, each integer in that row indexes a value in another matrix that gives the data value for that control point of the element.

Indices which define topologies must also have a element type, which is the name of a member of ElemType. The element type defines the basis function, geometry, and other properties for a geometric object. This information is necessary to know how to interpolate values within the topology.

An index set for a single triangle, whose nodes are defined above, can be defined as follows:

tri=IndexMatrix('tri',ElemType._Tri1NL,1,3)
tri[0]=(0,1,2)

This defines a linear triangle topology with a single element whose indices are (0,1,2). Again more simply with the helper function:

tri=listToMatrix([(0,1,2)],'tri',ElemType._Tri1NL)

The element type for this index is Tri1NL, that is a linear triangle geometry type using the Nodal Lagrange basis function. Using this type states that each row of tri represents the indices in nodes (and other matrices) for the control points for one triangle. Since this is linear, there must be 3 indices for the 3 vertices of the triangle, therefore tri must have 3 columns.

Other element types have different numbers of control points and the column count of index matrices using that type must match; eg. Tri2NL is a quadratic triangle type which must have 6 control points. Some element types have an varying number of control points which require extra arguments when the basis function is evaluated, typically index matrices using such types will store these arguments as metadata values.

For tri to be recognized as a spatial topology its isspatial metadata tag must be set to True:

tri.meta(StdProps._isspatial,'True')

This tag can also be set to False to make it explicit that a matrix is not a spatial topology, if no tag is set meta() returns an empty string which should be interpreted as an ambiguity or the equivalent to False depending on context. The helper function isSpatialIndex checks this criteria. Typically this tag is set by the PyDataSet object when it is constructed.

Fields

Fields store data values for other objects. Each row of a field matrix stores a data element for a spatial node, an element of a topology, or some other object that makes sense in another context. A simple field defining a single real value for each node in the triangle can be created as such using the helper function:

trif=listToMatrix([1.0,2.0,3.0],'trif')

How a field relates to other data, and thus how it can be rendered, is a complex issue stored as metadata tags. A field must be associate with an index matrix defined as its spatial topology, naming it in the spatial metadata value. The field stores data for the object defined by the matrix, implying the field can be rendered as an augmentation of that topology's representation. All fields must have this relationship, which by default when added to a PyDataSet object will be set to the first spatial matrix present if not already set.

There are three different field types as well:

  • per-node meaning each row corresponds 1-to-1 to each spatial node in the mesh
  • per-element meaning each row corresponds 1-to-1 to each element in the associated topology (and so defines a value for every node of any element simultaneously)
  • topology meaning the field has its own topology, each row corresponds 1-to-1 to each node in that topology, and the elements of that topology correspond 1-to-t to those in the associated spatial topology.

In the latter case a topology must be named in the topology metadata value. The field topology defines the relationship between field elements and the interpolation scheme in the same way a spatial matrix defines the topology in Cartesian space using nodes. A Field topology must have a element type which is used for interpolation and must have the same number of elements as the spatial topology; typically the spatial and field topologies are the same matrix.

If a field is per-element then there is no topology since the relationship between values is obvious and implicit. There is also no interpolation since the values in the field are applied to the whole of each element. The metadata field elemdata should be set to True to indicate this is such a field, otherwise confusion can result when the framework attempts to determine what type of field it is.

If a field is per-node then there is also no topology since there is no relation between values in this case. A per-node field can be associated with any spatial topology so this doesn't need to be set in metadata fields, although the field nodedata should be set to True. The function isPerNodeField checks whether a field fulfills the criteria for this type. The advantage of a per-node field is that multiple spatial topologies can use the same field simultaneously; the other two types can be used by only topology.

PyDataSet and MeshSceneObject

With these two matrices we can construct the actual scene object and add it to the scene:

ds=PyDataSet('TriDS',nodes,[tri],[trif])
obj=MeshSceneObject('Tri',ds)
mgr.addSceneObject(obj)

This constructs the PyDataSet object with the node and triangle matrices, then constructs the scene object with the data and adds it to the scene. The user can now interact with the object named "Tri".

A PyDataSet object can also be constructed directly from Python data structures. Internally listToMatrix will be used to construct matrices from lists of values or lists of tuples of values. The following is an equivalent way to create ds:

vecs=[vec3(0,0,0),vec3(1,0,0),vec3(0.5,0,1)]
tri=('tri',ElemType._Tri1NL,[(0,1,2)])
trif=('trif',[(1.0,2.0,3.0)])
ds=PyDataSet('TriDS',vecs,[tri],[trif])

The tri value is an example of an index set tuple, giving the name, type, and list of tuple values, but omits the optional boolean value stating whether the index is a spatial topology or not. The trif value is an example of the field tuple, giving the name and data but omitting the names of the field's spatial and topology index sets.

If the metadata tags for the input matrices are not set, by default the constructor for PyDataSet will attempt to assign sensible values, although this will cause confusion in more complex cases especially when fields are present. The metadata settings can be examined as such:

>>> print ds
Dataset TriDS
  Nodes: 3 x 1 (72.00B)
  Indices:
    tri: 1 x 3 (12.00B)
  Fields:
    trif: 3 x 1 (24.00B)
  Mem Total: 108.00B
>>> for i in ds.enumIndexSets():
...  print i,'\n',i.meta()
... 
IndexMatrix<tri> 
isspatial = True
>>> for f in ds.enumDataFields():
...  print f,'\n',f.meta()
... 
RealMatrix<trif> 
nodedata = True

A simple volume representation of this object can be constructed as such:

rep=obj.createRepr(ReprType._volume)
mgr.addSceneObjectRepr(rep)

The material Rainbow can be used in conjunction with the data field to apply a coloration to this triangle:

rep.applyMaterial('Rainbow',field='trif')

As a result we get on screen:

Multiple Topologies and Complexity

A mesh may be defined by multiple spatial topologies. This implies multiple index matrices refer to positions in one large node matrix. This implies certain problems for topology fields since they need to be associated with one spatial topology but also might need a separate field topology whose indices start from 0. Consider the following mesh:

trinodes=[vec3(0,0,0),vec3(1,0,0),vec3(0.5,0,1),]
triinds=[(0,1,2)]
trifield=[1,2,3]

quadnodes=[vec3(1.1,0,0),vec3(2.1,0,0),vec3(1.1,0,1),vec3(2.1,0,1)]
quadinds=[(4,5,6,7)]
quadfield=[1,2,3,4]

nodes=trinodes+quadnodes

inds=[('tris',ElemType._Tri1NL,triinds),('quads',ElemType._Quad1NL,quadinds)]
fields=[('trif',trifield,'tris'),('quadf',quadfield,'quads')]
ds=PyDataSet('MeshTest',nodes,inds,fields)

This defines one triangle next to one quad. Each field is associated with the correct spatial topology, however neither has their own topology so the spatial topology is used instead. This is fine for trifield since triinds indexes values from 0 to 2, however quadinds indexes values from 4 to 7, which are the correct indices in nodes but are not correct indices for quadfield. To solve the problem, quadfield must have its own topology with the same shape as quadinds but indexing from 0:

trinodes=[vec3(0,0,0),vec3(1,0,0),vec3(0.5,0,1),]
triinds=[(0,1,2)]
trifield=[1,2,3]

quadnodes=[vec3(1.1,0,0),vec3(2.1,0,0),vec3(1.1,0,1),vec3(2.1,0,1)]
quadinds=[(4,5,6,7)]
quadfield=[1,2,3,4]
quadfieldtopo=[(0,1,2,3)]

nodes=trinodes+quadnodes

inds=[('tris',ElemType._Tri1NL,triinds),('quads',ElemType._Quad1NL,quadinds),('quadft',ElemType._Quad1NL,quadfieldtopo,False)]
fields=[('trif',trifield,'tris'),('quadf',quadfield,'quads','quadft')]
ds=PyDataSet('MeshTest',nodes,inds,fields)

Note the added index matrix quadft which is explicitly stated to not be a spatial topology, the field quadf is defined with quads as its spatial topology and quadft as its field topology.

Clone this wiki locally