Visualization of Points, Curves, and Surfaces
The following examples describe low-level functionality for visualization that is only available from methods and is not associated with any solution data. For visualization based on solution data, see the section “Results” on page 48.
The examples below illustrate using the following plot types:
Once created, the plot type names are visible in the Settings window of each plot. In addition to the properties modified by the examples below, in the Settings window of these plot types, you can see the number of geometric entities created, such as number of points, line segments, and triangles. Just as for other types of plots, you can also change the Range of color and data, as well as Coloring and Style.
Points in 2D
The following code plots a circle of points using the Point Data plot type.
// A circle of points
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 2);
ResultFeature plot = pg.create("pt1", "PointData");
int N = 17;
double[][] p = new double[2][N];
double[] color = new double[N];
double R = 1000;
for (int i = 0; i < N; i++) {
  double angle = i*2*Math.PI/N;
  p[0][i] = R*Math.cos(angle);
  p[1][i] = R*Math.sin(angle);
  color[i] = p[1][i];
}
plot.set("pointdata", p)
  .set("colordata", color)
  .set("coloring", "colortable");
plot.run();
Comments
The first line
String pgTag = model.result().uniquetag("pg");
creates a unique tag for the plot group to be created. This is useful if you intend to add a varying number of plot groups in your model or application.
The line
ResultFeature pg = model.result().create(pgTag, 2);
creates a 2D Plot Group using the newly created unique tag. The second argument to create defines the dimension of the plot group (2 for 2D, 3 for 3D, and so on).
The line
ResultFeature plot = pg.create("pt1", "PointData");
creates a plot of the type PointData. This plot type is only available through methods.
The middle part of the example code generates the points making up the circle. The point coordinates are stored in the 2-by-N array p, along with color data in the array color of length N. The color data is, in this example, simply based on the index of the points and is used to control the coloring of each point based on a color table.
The last few lines populate the fields of the Point Data plot.
plot.set("pointdata", p)
  .set("colordata", color)
  .set("coloring", "colortable");
The property pointdata takes the 2-by-N array p as its input. The options for the coloring property are colortable or uniform.
To learn about the syntax for the additional properties available for a Point Data plot, you can run the above code in a blank model, browse to the Settings window for the Point Data plot, click Record Code, and change the corresponding plot properties. Note that the name of the plot type in the Settings window of the plot in the model tree is Point Data.
Turning Off Model History
When using this type of low-level functionality for larger sets of data, such as a large number of points, the stored model history may become excessively large. Because of this, it is recommended to temporarily turn off model history recording when using this type of functionality; see “Turning Off and Resetting The Model History” on page 53.
Points in 3D
The following code plots points in an undulating pattern in 3D using the Point Data plot type.
// Undulating points in 3D
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
ResultFeature plot = pg.create("pt1", "PointData");
int N = 37;
double[][] p = new double[3][N];
double[] color = new double[N];
double R = 1000;
for (int i = 0; i < N; i++) {
  double angle = i*2*Math.PI/N;
  p[0][i] = R*Math.cos(angle);
  p[1][i] = R*Math.sin(angle);
  p[2][i] = R*Math.cos(3*angle);
  color[i] = p[1][i];
}
plot.set("pointdata", p)
  .set("colordata", color)
  .set("coloring", "colortable")
  .set("sphereradiusscale", 1);
plot.run();
selectNode(pg);
Comments
When plotting 3D points the line
model.result().create(pgTag, 3);
has the second argument set to 3 in order to create a 3D Plot Group. In 3D, the point coordinates, p is a 3-by-N array.
The line
  .set("sphereradiusscale", 1);
controls the radius of the sphere used to render each point.
To automatically display the newly created plot, the line
selectNode(pg);
is added last in the code segment.
To get a denser set of points, you can increase the integer N to, say, 370.
Curve in 3D
The following code plots line segments in the shape of a 3D helix using the Line Data plot type.
// A 3D helix from line segments
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
ResultFeature plot = pg.create("line1", "LineData");
int N = 100;
double[][] p = new double[3][N];
int[][] t = new int[2][N-1];
for (int i = 0; i < N; i++) {
  double s = 4*Math.PI*i/N;
  p[0][i] = s/5;
  p[1][i] = Math.sin(s);
  p[2][i] = Math.cos(s);
  if (i > 0) {
    t[0][i-1] = i-1;
    t[1][i-1] = i;
  }
}
plot.set("pointdata", p)
  .set("elementdata", t);
plot.run();
selectNode(pg);
Comments
The line
ResultFeature plot = pg.create("line1", "LineData");
creates a plot of the type LineData. This plot type is only available through methods. Just as for Point Data plots, the point coordinates p is a 3-by-N array. In addition to pointdata, the LineData plot type takes elementdata as its input. In the example, this is represented by the 2-by-N array t and contains indexes to the columns of p, corresponding to the start and end points of the lines.
In a similar way, line segments can be plotted in 2D by creating a 2D plot group and by letting the point coordinates be a 2-by-N array. See also “Points in 2D” on page 160.
Triangulated Shape in 2D
The following code plots triangles in the shape of a 2D pentagon by using the Surface Data plot type.
// A 2D pentagon from triangles
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 2);
ResultFeature plot = pg.create("surf1", "SurfaceData");
int N = 5;
double[][] p = new double[2][N+1];
int[][] t = new int[3][N];
p[0][0] = 0;
p[1][0] = 0;
for (int i = 0; i < N; i++) {
  double angle = i*2*Math.PI/N;
  p[0][i+1] = Math.cos(angle);
  p[1][i+1] = Math.sin(angle);
  t[0][i] = 0;
  t[1][i] = i+1;
  t[2][i] = 1+(i+1)%N;
}
plot.set("pointdata", p)
  .set("elementdata", t);
plot.run();
selectNode(pg);
Comments
The line
ResultFeature plot = pg.create("surf1", "SurfaceData");
creates a plot of the type SurfaceData. This plot type is only available through methods. Just as for 2D Point Data plots, the point coordinates p is a 2-by-N array. In addition to pointdata, and similar to the LineData plot type, the SurfData plot type takes elementdata as its input. In the example, this is represented by the 3-by-N array t and contains indexes to the columns of p, corresponding to the vertexes of the triangles. The ordering of the point indexes in the array t is not important for 2D Surface Data plots.
Function Surface in 3D
The following code plots triangles in the shape of a 3D rotationally symmetric sinc-function surface by using the Surface Data plot type.
// A 3D sinc(r) function surface
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
ResultFeature plot = pg.create("surf1", "SurfaceData");
int Nx = 51;
int Ny = 51;
double[][] p = new double[3][Nx*Ny];
int[][] t = new int[3][2*(Nx-1)*(Ny-1)];
double[] color = new double[Nx*Ny];
int pos = 0;
for (int i = 0; i < Ny; i++) {
  for (int j = 0; j < Nx; j++) {
    double x = 20*(j-Nx/2)/Nx;
    double y = 20*(i-Ny/2)/Ny;
    double r = Math.sqrt(x*x+y*y);
    double z = 4*((r == 0) ? 1 : (Math.sin(r)/r));
    p[0][pos] = x;
    p[1][pos] = y;
    p[2][pos] = z;
    color[pos] = z;
    pos++;
  }
}
pos = 0;
for (int i = 0; i < Ny-1; i++) {
  for (int j = 0; j < Nx-1; j++) {
    int p00 = Nx*i+j;
    int p01 = Nx*i+j+1;
    int p10 = Nx*(i+1)+j;
    int p11 = Nx*(i+1)+j+1;
    t[0][pos] = p00;
    t[1][pos] = p01;
    t[2][pos] = p11;
    pos++;
    t[0][pos] = p00;
    t[1][pos] = p11;
    t[2][pos] = p10;
    pos++;
  }
}
plot.set("pointdata", p)
  .set("elementdata", t)
  .set("colordata", color)
  .set("coloring", "colortable");
plot.run();
selectNode(pg);
Comments
This example is similar to “Triangulated Shape in 2D” on page 163, but with the point array being a 3-by-N array for 3D surfaces. For Surface Data plots in 3D, the ordering of the indexes in the elementdata array t matters. It determines the direction of the surface normal, which is used for the lighting effect when using Scene Light in the Graphics window. The surface normal of a triangle is determined according to the “right-hand rule”. In mathematical terms, the surface normal is defined as the vector product:
where the indexes into t represent the rows in one of the columns of t and p represents a column in the array of points p.
To ensure that the lighting effect produces expected results, the triangle surface normal directions need to consistently point in the same direction as the intended overall surface normal direction. As an alternative to making sure that the indexes come in the correct order, the normal direction may be given as an additional input to a Surface Data plot. This is shown in the next example section, Sphere in 3D.
Sphere in 3D
The following code plots triangles in the shape of a 3D sphere by using the Surface Data plot type.
// A coarse sphere with user-supplied normals
 
int Nx = 20;
int Ny = 10;
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
ResultFeature plot = pg.create("surf1", "SurfaceData");
double[][] p = new double[3][Nx*Ny];
double[][] normals = new double[3][Nx*Ny];
int[][] t = new int[3][2*(Nx-1)*(Ny-1)];
double[] color = new double[Nx*Ny];
int pos = 0;
double R = 10;
for (int i = 0; i < Ny; i++) {
  for (int j = 0; j < Nx; j++) {
    double theta = Math.PI*i/(Ny-1);
    double phi = 2*Math.PI*j/(Nx-1);
    double x = R*Math.sin(theta)*Math.cos(phi);
    double y = R*Math.sin(theta)*Math.sin(phi);
    double z = R*Math.cos(theta);
    p[0][pos] = x;
    p[1][pos] = y;
    p[2][pos] = z;
    normals[0][pos] = x;
    normals[1][pos] = y;
    normals[2][pos] = z;
    color[pos] = z;
    pos++;
  }
}
pos = 0;
for (int i = 0; i < Ny-1; i++) {
  for (int j = 0; j < Nx-1; j++) {
    int p00 = Nx*i+j;
    int p01 = Nx*i+j+1;
    int p10 = Nx*(i+1)+j;
    int p11 = Nx*(i+1)+j+1;
    t[0][pos] = p00;
    t[1][pos] = p01;
    t[2][pos] = p11;
    pos++;
    t[0][pos] = p00;
    t[1][pos] = p11;
    t[2][pos] = p10;
    pos++;
  }
}
plot.set("pointdata", p)
  .set("elementdata", t)
  .set("colordata", color)
  .set("normaldata", normals)
  .set("coloring", "colortable");
plot.run();
selectNode(pg);
Comments
In this example, information about the surface normal direction is not given implicitly by the triangle orientation, but instead explicitly by the parameter normaldata by means of the 3-by-Nx*Ny array normals containing surface normal vectors at each point. The normal vectors do not need to be normalized; only the direction is used. The coloring of the sphere is based on the z-coordinate of each triangle point and is stored for each point in the 3-by-Nx*Ny array color.
The sphere is constructed from a discrete grid defined in terms of spherical coordinate angles, where each grid cell is divided into two triangles. The number of triangles t is then given by 2*(Nx-1)*(Ny-1).
Tube Plot in 3D, Logarithmic Spiral
The following code plots a tube in 3D in the shape of a logarithmic spiral by using the Tube Data plot type.
// A logarithmic tube spiral in 3D
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
ResultFeature plot = pg.create("tube1", "TubeData");
int N = 1000;
double[][] p = new double[3][N];
double[] radius = new double[N];
double[] color = new double[N];
for (int i = 0; i < N; i++) {
  double par = 0.005*i;
  p[0][i] = Math.exp(par)*Math.cos(10*par);
  p[1][i] = Math.exp(par)*Math.sin(10*par);
  p[2][i] = 0.1*i;
  radius[i] = 0.2*Math.sqrt(i+1);
  color[i] = i;
}
plot.set("pointdata", p)
  .set("radiusdata", radius)
  .set("colordata", color)
  .set("coloring", "colortable");
plot.run();
selectNode(pg);
Comments
A Tube Data plot is similar to a Point Data, plot but with an absolute radius array given as an argument to radiusdata. For the Point Data plot type, there is a similar sphereradiusscale.
Arrows in 2D
The following code plots arrows in a circular pattern by using the Arrow Data plot type.
// Arrows in a circular pattern in 2D
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 2);
ResultFeature plot = pg.create("arrow1", "ArrowData");
int N = 17;
double[][] p = new double[2][N];
double[][] vec = new double[2][N];
double len = 0.2;
for (int i = 0; i < N; i++) {
  double angle = 2*Math.PI*i/N;
  p[0][i] = Math.cos(angle);
  p[1][i] = Math.sin(angle);
  vec[0][i] = -len*p[0][i];
  vec[1][i] = -len*p[1][i];
}
plot.set("pointdata", p)
  .set("vectordata", vec);
plot.run();
selectNode(pg);
Comments
An Arrow Data plot associates an array of vectors, in the example vec, to each point p.
Arrows in 3D
The following code plots arrows in a logarithmic spiral pattern by using the Arrow Data plot type.
// Arrows in a logarithmic spiral pattern in 3D
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
ResultFeature plot = pg.create("arrow1", "ArrowData");
int N = 1000;
double[][] p = new double[3][N];
double[][] vec = new double[3][N];
double[] color = new double[N];
for (int i = 0; i < N; i++) {
  double par = 0.005*i;
  p[0][i] = Math.exp(par)*Math.cos(10*par);
  p[1][i] = Math.exp(par)*Math.sin(10*par);
  p[2][i] = 0.1*i;
  double len = Math.sqrt(p[0][i]*p[0][i]+p[1][i]*p[1][i]+p[2][i]*p[2][i]);
  for (int j = 0; j < 3; j++) {
    vec[j][i] = 4*p[j][i]/len;
  }
  color[i] = i;
}
plot.set("pointdata", p)
  .set("vectordata", vec)
  .set("colordata", color)
  .set("coloring", "colortable");
plot.run();
selectNode(pg);
Comments
In this example, in addition to the example in the section “Arrows in 2D”, color data is used based on the point index.
Annotations in 2D
The following code renders text strings in a circular pattern by using the Annotation Data plot type.
// Letters in a circular pattern in 2D
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 2);
 
for (int i = 0; i < 26; i++) {
  double angle = 2*Math.PI*i/26;
  ResultFeature plot = pg.create("ann"+i, "AnnotationData");
  plot.set("pos", new double[]{Math.cos(angle), Math.sin(angle)})
    .set("text", "ABCDEFGHIJKLMNOPQRSTUVWXYZ".substring(i, i+1))
    .set("showpoint", false);
}
pg.run();
selectNode(pg);
Comments
The property pos takes as its input an array of length 2 representing 2D coordinates for the position of the string to be rendered. The property text takes as its input the string to be rendered. The Boolean property showpoint determines if a point, at the 2D coordinate position, should be rendered or not.
Annotations in 3D with LaTeX Syntax
The following code renders text strings with Greek letters of different colors at the corners of a cube by using the Annotation Data plot type.
// Greek letters at the corners of a cube
 
String pgTag = model.result().uniquetag("pg");
ResultFeature pg = model.result().create(pgTag, 3);
 
String[] texts = {"\\alpha", "\\beta", "\\gamma", "\\delta", "\\epsilon", "\\zeta", "\\eta", "\\theta"};
String[] colors = {"black", "blue", "cyan", "gray", "green", "magenta", "red", "yellow"};
for (int x = 0; x < 2; x++) {
  for (int y = 0; y < 2; y++) {
    for (int z = 0; z < 2; z++) {
      int index = x+2*y+4*z;
      ResultFeature plot = pg.create("ann"+index, "AnnotationData");
      plot.set("pos", new double[]{x, y, z})
        .set("text", "$"+texts[index]+"$")
        .set("latexmarkup", true)
        .set("color", colors[index]);
    }
  }
}
pg.run();
selectNode(pg);
Comments
The Boolean property latexmarkup determines if the text should be interpreted using LaTeX syntax or not.