# ComplexPortraits

Domain coloring for complex functions.

## Table of Contents

- What are portraits?
- Phase colors and colormaps
- Installing the ComplexPortraits package
- Using the package
- Creating, viewing and saving portraits
- PlotRecipe support
- Examples of color schemes

## What are portraits?

To visualize a function f: ℂ → ℂ these steps are done:

- Coloring of the codomain/target set: every w ∊ ℂ gets a color, lets call it C(w).
- To get the color at z ∊ ℂ (where f is defined) one calculates w = f(z) and
- the color at z is computed using the map C∘f, i.e. the color at the point z is given by the C(f(z)).

Here is an example for f(z) = z².

For more flexibility in this package the color at z may also depend on z itself: color at z = CS(z, f(z)). Such a function is called *color scheme*.

This process is called domain coloring. There are many different ways to color the w-plane. Often the color is primarily determined by the angle/phase of w. In such a situation this is also called a (complex) phase portrait. A lot of properties of f can be directly seen in or deduced from such a phase portrait.

For a lot more details, see the book "Visual Complex Functions" by Elias Wegert, Birkhäuser, 2012.

## Phase colors and colormaps

Some of the ideas of Elias Wegert's PhasePlot package/Complex Function Explorer for Matlab are implemented here for Julia. Especially the following one-lettter-colorschemes are also available in ComplexPortraits: c, d, e, j, m, p, q. Hence a copy of the BSD-licensed Complex Funktion Explorer source code can be found in the based_on directory.

Additionally some other color schemes are implemented. Please see the list of examples below.

The basis for many color schemes are the colormaps for the angles/phases. There exist two typical choices (color wheels):

HSV wheel: and NIST wheel:

Of course you can define and use your own colormap.

## Installing the ComplexPortraits package

```
using Pkg
Pkg.add(PackageSpec(url="https://github.com/luchr/ComplexPortraits.jl", rev="master"))
```

To save diskspace all images are not included in this Julia-Package, but are saved in ComplexPortraitsImages.

## Using the package

By default this package does not import any names in the global namespace (no namespace pollution). There are two macros if you want to import some names (`import_normal`

and `import_huge`

):

```
using ComplexPortraits
@ComplexPortraits.import_normal
# or @ComplexPortraits.import_huge
```

## Creating, viewing and saving portraits

The main function is `portrait`

:

```
function portrait(z_upperleft, z_lowerright, f;
no_pixels=(600,600), point_color=cs_j())
```

Two complex numbers are needed: the upper left and lower right corner of the rectangular domain which should be colored. `f`

is the complex function.

The other arguments are optional: With `no_pixels`

the size of the output image (number of pixels) can be given. `point_color`

is a color scheme function. A color scheme function has the form

`mycolorscheme(z, fz) = ... # computation of color depending on z and f(z)`

It takes two scalar complex numbers (typically of type `Complex{Float64}`

) and returns a color, see ColorTypes.

Please see below for a list of predefined parameterized color schemes. All `cs_foobar(baz)`

functions in the package are function generators, i.e. they use the given parameters
to generate and return a color scheme function. For example:
`cs_e(phaseres=10)`

uses the `phaseres`

argument to create and return a color scheme
function with the given phase-resolution.

The output of `portrait`

is a matrix of color entries (a.k.a. image).

There are many ways to save such an image, e.g. using FileIO and ImageMagick:

```
using ComplexPortraits
using ImageMagick
using FileIO
img = portrait(-2.0 + 2.0im, 2.0 - 2.0im, z -> z^2)
save(File(format"PNG", "z_squared.png"), img)
```

To directly view such an image in Jupyter, Atom, etc. (or in an extra window) and/or process the image in other ways, please see and use the JuliaImages ecosystem.

## Support for Plots

Since version 0.2.0 `Plots.jl`

is supported via Plot recipes:

```
using Plots, ComplexPortraits
ComplexPortraits.phaseplot(-2.0 + 2.0im, 2.0 - 2.0im, z -> exp(z) - z)
```

Here is the full list if kwargs for `phaseplot`

:

```
phaseplot(z_upperleft, z_lowerright, f;
no_pixels=(600, 600),
point_color=cs_j(),
no_ticks=(7, 7),
ticks_sigdigits=2)
```

## Examples of color schemes

For all the examples the function `z -> (z-1)/(z*z + z + 1)`

is used. It
has two simple poles, one root and one saddle point.

### cs_grid...

For the color schemes in this section the 1-periodic "spike function"
`x -> 1 - exp(a*abs(mod(x,1)-0.5)^b)`

with two parameters a and b is
often used as a factor to turn something off (at 0.5 + ℤ):

#### cs_gridReIm

`cs_gridReIm(; reim_a=-9.0, reim_b=0.5)`

Use Nist-Colors for the phase and use the spike function to decrease the brightness periodically on a real-imag grid.

`reim_a=-9.0, reim_b=0.5`

:

`reim_a=-9.0, reim_b=0.8`

:

#### cs_gridReIm_logabs

`cs_gridReIm_logabs(; reim_a=-9.0, reim_b=0.5, abs_a=-9.0, abs_b=0.8, vcorr=(s,v) -> max(1.0 - s, v))`

Additional to `cs_gridReIm`

use another spike function to
decrease the saturation depending on the log(abs(w)).

An additional function `vcorr`

can be used to adjust the
brightness depending on the saturation to control whether the black lines
are "in front of the white lines" or the white lines are in front.

`reim_a=-9.0, reim_b=0.5, abs_a=-9.0, abs_b=0.8`

:

`reim_a=-9.0, reim_b=0.8, abs_a=-9.0, abs_b=0.9`

:

#### cs_gridReIm_abs

`cs_gridReIm_abs(; reim_a=-9.0, reim_b=0.5, abs_a=-9.0, abs_b=0.8, vcorr=(s,v) -> max(1.0 - s, v))`

Like `cs_gridReIm_logabs`

, but the low saturation lines (white lines)
depend on abs(w).

`reim_a=-9.0, reim_b=0.5, abs_a=-9.0, abs_b=0.8`

:

### cs_p, cs_j, cs_d, cs_q

The following color schemes all use a colormap for the phase, i.e. the phase
is discretized with the colors of the `colormap`

-argument.
Use `hsv_colors(color_number=600; saturation=1.0, value=1.0)`

for the HSV-colormap and `nist_colors(color_number=600; saturation=1.0, value=1.0)`

for the NIST-colormap.

#### cs_p

`cs_p(; colormap=hsv_colors())`

A vanilla phase portrait ("proper").

`cs_p():`

#### cs_j

`cs_j(; colormap=hsv_colors(), jumps=collect(LinRange(-π, π, 21))[1:20], a=0.8, b=1.0)`

This is a `cs_p`

with specific phase jumps.

`cs_j()`

:

`cs_j(jumps=[60/180*pi, 240/180*pi])`

:

#### cs_d

`cs_d(; colormap=hsv_colors())`

This is a `cs_p`

with the brightness adapted to the log(abs(fz)).

`cs_d()`

:

#### cs_q

`cs_q(; colormap=hsv_colors(20)) = cs_p(; colormap=colormap)`

Same as `cs_p`

but with a colormap with only 20 entries.

`cs_q()`

:

### cs_m, cs_e

#### cs_m

`cs_m(; colormap=hsv_colors(), logabsres=20)´

Similar to `cs_p`

with additional brightness jumps depending on log(abs(fz)).

`cs_m()`

:

`cs_m(phaseres=10)`

:

#### cs_e

`cs_e(; colormap=hsv_colors(), logabsres=20)`

This is a `cs_m`

with brightness changed w.r.t. abs(fz) [near zero and infinity].

`cs_e()`

:

`cs_e(logabsres=10)`

:

### cs_c

`cs_c(; colormap=hsv_colors(), phaseres=20)`

Phase plot with conformal polar grid.

`cs_c()`

:

`cs_e(logabsres=10)`

:

### cs_useImage

`cs_useImage(image; img_upperleft=0.0 + 1.0im, img_lowerright=1.0 + 0.0im, is_hv_periodic=(true, true)) `

Here an given `image`

is placed in the target complex plane in the rectangle given by `img_upperleft`

and `img_lowerright`

. Optionally this image can be repeated horinzontal and/or vertical. The colors of the (pixels of the) image are used to colorize portrait.

For this example the following image (`test_img`

) is used:

`cs_useImage(test_img; img_upperleft=0.0+0.2im, img_lowerright=0.8+0.0im, is_hv_periodic=(false, true))`

`cs_useImage(test_img; img_upperleft=0.0+0.2im, img_lowerright=0.8+0.0im, is_hv_periodic=(true, true))`

### Colorschemes with only a few colors

#### cs_stripes

`cs_stripes(directions, colors)`

Directions is a vector of complex numbers. For each such number d in directions the stripes are perpendicular to (real(d), imag(d)). And abs(d) is the number of stripes per unit length. This devides the complex plane in two sets Sd and ℂ\Sd.

Each z ∊ ℂ has n=length(directions) flags (z ∊ S1, z ∊ S1, ..., z ∊ Sn). This make 2^n possible flag-combinations. For every combination there need to be a color in colors.

`cs_stripes([5.0 + 0.0im], [HSV(0.0, 0.0, 1.0), HSV(0.0, 0.0, 0.0)]))`

```
cs_stripes(
[5.0 + 0.0im, 0.0 + 5.0im],
[HSV(0.0, 0.0, 1.0), HSV(0.0, 0.0, 0.0),
HSV(0.0, 1.0, 1.0), HSV(240.0, 1.0, 1.0)])
```

```
cs_stripes(
exp(-1.0im*pi/4).*[2.0 + 0.0im, 0.0 + 2.0im, 2.0 + 2.0im],
[HSV(0.0, 0.0, 1.0), HSV(0.0, 0.0, 0.0),
HSV(0.0, 1.0, 1.0), HSV(60.0, 1.0, 1.0),
HSV(120.0, 1.0, 1.0), HSV(180.0, 1.0, 1.0),
HSV(240.0, 1.0, 1.0), HSV(300.0, 1.0, 1.0)])
```

#### cs_angle_abs_stripes

`cs_angle_abs_stripes(directions, colors)`

Uses `cs_stripes`

for (angle(z), abs(z)).

`cs_angle_abs_stripes([5.0 + 0.0im], [HSV(0.0, 0.0, 1.0), HSV(0.0, 0.0, 0.0)])`

```
cs_angle_abs_stripes(
[5.0 + 0.0im, 0.0 + 5.0im],
[HSV(0.0, 0.0, 1.0), HSV(0.0, 0.0, 0.0),
HSV(0.0, 1.0, 1.0), HSV(240.0, 1.0, 1.0)])
```

```
cs_angle_abs_stripes(
exp(-1.0im*pi/4).*[2.0 + 0.0im, 0.0 + 2.0im, 2.0 + 2.0im],
[HSV(0.0, 0.0, 1.0), HSV(0.0, 0.0, 0.0),
HSV(0.0, 1.0, 1.0), HSV(60.0, 1.0, 1.0),
HSV(120.0, 1.0, 1.0), HSV(180.0, 1.0, 1.0),
HSV(240.0, 1.0, 1.0), HSV(300.0, 1.0, 1.0)]))
```