In creating this website I wanted to begin with a visually interesting project that showcased mathematical principles. After some thought, I settled on using randomly generating vector fields to visualize the curl and divergence in various ways. In order to create additional depth for this project I also ended up throwing in my Principal Component Analysis (PCA) implementation used elsewhere on this website.
Let nrows and ncols denote the numbers of rows and columns in the background image we are going to generate. We can then randomly choose a number nbase of "base vectors" using a Poisson distribution. These base vectors will be assigned both a random direction and magnitude as well as a random location chosen within the bounds of the image. More specifically, let us use the 4-tuple B to represent the base vectors and their associated locations where (1) c represents the vector's column index, (2) r represents the vector's row index, (3) x represents the x-component of the vector, and (4) y represents the y-component of the vector:
From here, all additional vectors in the field are interpolated from the base vectors using the _computeRemainingVectors method of the VectorFieldGenerator class. The idea is that we provide a "softmax normalizer" parameter snorm which controls the relative contributions of each base vector; small normalizer values mean that vectors closely resemble their nearest base vector while large values mean that the field will be more tend to be more uniform. To elaborate further, the interpolated vector (x,y) at column index c and row index r is defined in the following way:
As an added feature, it is possible to apply affine transformations to the vector field in order to experiment with creating interesting visualizations. More specifically, the applyAffineTransformation method of the VectorField class takes in several m and b values as arguments which transform old vector values into new ones in the following way:
Note that for the vector fields shown below values of (m11,m12,m21,m22) = (1,0,1,1) and (b1,b2) = (0,0) were used for the affine transformation.
With this definition in place we can now start generating our vector fields. The VectorField class allows us to visualize the vector fields in two possible formats using the plotVectorField method:
(Left) Quiver representation of the generated vector field made with 1080 rows, 1920 columns, seed of 0 and softmax normalizer of 480.
(Right) Streamplot representation of the same vector field.
From here we can simply compute the Jacobian-related values of our vector fields using the commonly known equations for these quantities:
Shared settings for these images: seed of 0, softmax normalizer of 480
Notice the region of strong negative curl corresponding to the vectors turning clockwise on the right side of the image
Also notice the region of strong positive divergence and determinant near the top-middle where the arrows grow in length
In terms of the exact meaning of the above images, blue regions represent positive values of curl and divergence whereas red regions represent negative values. While these separate representations of curl and divergence are visually interesting they are not exactly what I had in mind to take this project over the finish line. To push this concept further I wanted to develop a way of combining these two images into a full RGB image. The idea for doing so is to create a 4-dimensional data set constructed from the pixels of these two images where:
The 1st component is the blue-intensity of the curl image
The 2nd component in the red-intensity of the curl image
The 3rd component is the blue-intensity of the divergence image
The 4th component in the red-intensity of the divergence image
This 4-dimensional data can then be "compressed" into 3-dimensional data using PCA. The three components of the compressed data can then be used as a basis for the red, green and blue channels of a newly generated image. Note that I have implemented 3 possible ways of taking this PCA data and getting associated RGB values: (1) "Keep Positive" means only creating color where PCA values are positive, (2) "Keep Negative" means only creating color where PCA values are negative, and (3) "Unclipped" means creating color for all PCA values.
The following examples are a selection of generated images that showcase common outputs the algorithm described above. The main goal of this section is to show the results of PCA as well as the effect that the softmax normalizer has on the underlying vector field.
Shared settings for these images: seed of 0, softmax normalizer of 320
Notice how dark regions in the "Keep Positive" image are not dark in the "Keep Negative" image (and vice versa)
However, it is important to note that the "Unclipped" image is more than simply the result of overlaying the positive and negative images
Shared settings for these images: seed of 0, PCA of all three Jacobian-related values
The same base vectors are used for these vector fields; the only difference is the relative strength at which base vectors affect their neighboring points
Shared settings for these images: softmax normalizer of 320, unclipped combination of curl and divergence using PCA
Notice how the colors in each image can vary significantly as a result of PCA
Background image generation from the background_generator folder of the active_projects repository
Script with image settings from generate_image.py
Curl and divergence combination from the dimensional_analysis folder of the infrastructure repository
PCA algorithm from dimension_reduction.py
Additional basic functionality from the common_needs folder of the infrastructure repository
Attribute privacy from privacy_helper.py
File dialogs from tkinter_helper.py
Object type checks from type_helper.py
VectorField2D class from vector_helper.py