Visualizing multivalued functions with Three.js has one major problem: `ParametricGeometry`

automatically connects all vertices in the geometry. Multivalued geometries constructed of several branches will inevitably have a branch cut somewhere in each parametrically generated part with connections that are inaccurate. The depicted branch cuts generally look horrendous.

As a basic example of the problem, consider an arbitrary root of the product of two displaced variables:

$f(x,y)=(z-{z}_{1}{)}^{1/n}(z-{z}_{2}{)}^{1/n}={r}_{1}^{1/n}{r}_{2}^{1/n}{e}^{i({\phi}_{1}+{\phi}_{2})/n}$

The radial variables are distances from each center of displacement,

${r}_{1}=\sqrt{(x-{x}_{1}{)}^{2}+(y-{y}_{1}{)}^{2}}\phantom{\rule{5em}{0ex}}{r}_{2}=\sqrt{(x-{x}_{2}{)}^{2}+(y-{y}_{2}{)}^{2}}$

and the arguments are evaluated relative to each center of displacement:

${\phi}_{1}={tan}^{-1}\left(\frac{y-{y}_{1}}{x-{x}_{1}}\right)\phantom{\rule{8em}{0ex}}{\phi}_{2}={tan}^{-1}\left(\frac{y-{y}_{2}}{x-{x}_{2}}\right)$

For visualizing the real part of this multivalued geometry, `ParametricGeometry`

is used in Cartesian coordinates for each branch and the individual branches merged into a single whole. Total phase is denoted by color, with values set in the HSL color space starting with red at a phase of zero. The naïve result is

Displacements can be altered by entering different values. The buttons select different roots of the product of displaced variables.

One can clearly see the problem in crossing the branch cuts: those extra vertical faces connecting sheets are just plain inaccurate and do not belong there. They are even clearer if individual sheets are displaced along the *x*-axis:

In this last interactive graphic the first sheet is centered in the view. Additional sheets can be seen after zooming out using a touch pad or mouse wheel.

From the source code, `ParametricGeometry`

creates a uniform grid in *uv*-space of equal triangles. The resulting triangles have their indices arranged in two configurations:

Visual inspection indicates that the triangles across branch cuts have a wildly different color at one vertex. These bad triangles can be detected by a difference in hue values between vertices greater than some tolerance level. The bad vertex in each of these triangles is then replaced with the corresponding vertex in either the previous or next sheet, with all corresponding vertices in all sheets shifted in the same direction simultaneously and vertex colors updated accordingly.

Since the two configuration of triangles are labeled rather differently with respect to *uv*-space, one would expect the detection process to operate differently on the two configurations. It turns out not to matter: one can take any side in both configurations that is greater than the tolerance level and use it to correct both configurations.

The only other consideration is the sign to apply in using the value of the hue difference to determine the direction in which bad vertices are shifted. This is easily found in practice from the visual affect on the resulting graphic from a wrong choice.

In the source code of this document, the function `heal()`

performs the task of identifying bad triangles and shifting bad vertices. The value of the variable `tolerance`

in this function is determined relative to the global variables `steps`

and `sheets`

. The variable `steps`

sets the density of triangles and is reduced as much as possible to prevent sluggish browser rending, but then `tolerance`

must be increased to prevent misidentification of triangles near the branch points. For a rendering with triangles having smaller hue differences along the edges, which occurs as the number of sheets increases or for a higher density of triangles, the tolerance level must be decreased. The simplest way to handle this is with an array of values producing a graphic without visible defects as the exponent varies.

With the function `heal()`

applied, the real part of the multivalued geometry is now

Beautiful!

For a multivalued geometry with a more complicated structure, a single healing pass may not be sufficient to rectify all branch cuts. In that case applying the function `heal()`

repeatedly should do the trick.

*Uploaded 2016.06.27 — Updated 2016.09.07*
analyticphysics.com