{mbrot←0}(cxfn ##.cxdraw) zoom              ⍝ Complex function drawing.

A tool for visual exploration of complex function [cxfn]. For example:

    *∘0.5 cxdraw 2      ⍝ explore complex square root.

NB:  The  operand function is called with a single complex point as argument and
must return a <single> complex point as result; a multiple-valued function, such
as '⍵+2 3', won't work.

Right argument [zoom] determines the part of the complex plane that  is  visible
in the GUI window.  For example, ...cxdraw 8 shows a square of "radius" 8,  with
corners at ¯8j8 and 8j¯8. Real and imaginary axes and the unit circle are marked
on the plane.

Drawing:

    Mark:  Double-click to draw domain (blue) and range (red) markers.
    Draw:  Click and hold Left button to draw.
    Clear: Click Right button to clear.
    Quit:  Press <Esc> or close window to quit.

As  you  hold down the left mouse button and draw, the function's domain path is
tracked in blue and its corresponding range path is calculated and drawn in red.
It is informative  to draw small circles crossing any branch cuts of the operand
function.

Notice that, while drawing, the GUI form's caption displays the complex coordin-
ates of current domain and range.

Continuity
----------
Domain  points that are close to,  but which straddle a branch cut, map to (red)
range points that are typically far apart.  It is less distracting if the funct-
ion  refrains  from connecting range points on either side of such a discontinu-
ity.  An  example  might be the square-root function ⍵*÷2: as the domain crosses
the negative real axis, the range flips to its conjugate (mirror-image) point on
the other side of the real axis.

We  avoid this effect by looking at the _differential_ of the blue and red lines
and  by  refusing to draw segments of the red line that correspond to a slope of
more than an arbitrary limit (say 100). We sample the slope at a number (100) of
points along the blue and red lines.

Ideally, we should compare the ratio of the _lengths_ of these "delta" sections:

        length←{+⌿under(*∘2)2-/zg ⍵}    ⍝ length of line segment at ⍵.
where:
        under←{⍵⍵⍣¯1 ⍺⍺ ⍵⍵ ⍵}           ⍝ aka "dual"

but for this exercise, the distance around two sides of the bounding rectangle

        +/|2-/[1]↑rng dom

is considerably quicker to calculate and is accurate enough.

        ┌──────────∘z+∆z
        │        ./│|
        │      ./  │|
        │    ./ ←──│|─── real length of line segment [z, z+∆z].
        │  ./      │|
        │./        │|─┐
       z∘──────────┘/ │
         ¯¯¯¯¯¯¯¯¯¯¯  │
                  └───┴─ "good-enough" length of line segment [z, z+∆z].

Mandelbrot mode
---------------
If optional left argument [mbrot], default 0, is set, only mouse  movements  are
detected and button clicks are ignored.  In this mode, for each MouseMove event,
we:

    Set the value of ⍺ to the new position of the mouse cursor.
    Set temp variable z to 0.
    Apply expression (z←⍺ cxfn z) a large (10) number of times. Then,
    If z is still within the square ¯2J2..2J¯2
        draw a dot at ⍺; otherwise, don't.

Using [cxfn] +∘(×⍨) reveals an approximation to the Mandelbrot  set:  by  moving
the mouse, shade the region around the unit circle as if  shading  with a pencil
on a piece of grease-spotted  paper.  In particular, by shading more thoroughly,
discover the _boundary_ of the revealed figure.

    http://en.wikipedia.org/wiki/Mandelbrot_set

Technical note: the above iteration is coded using the power operator ⍣:

        to←,map⍣10⍨zg↑,↓y x                 ⍝ after 10 iterations.

where:
    y and x are the GUI window coordinates,
    zg translates these coordinates to complex number (z) and
    map⍣10 applies cxfn ten times.

Notice that monadic commute ⍨ starts the iteration with z←⍺, rather than at z←0,
but, in the case of the mandelbrot expression, this just saves one iteration.

(Perhaps  this technique might be extended to investigate the more general Julia
 and Fatou sets http://en.wikipedia.org/wiki/Julia_set).

Technical note:

Notice, with the function that maps a GUI coordinate to a complex number, rather
than using an easier-to-read dfn:

    zg←(⍺÷100÷2 ¯2)∘{↑⍺×↓⊖⍉⍵-50}            ⍝ scale: complex from gui coords.

it is coded instead as a more obscure derived function:

    zg←↑∘((⍺÷100÷2 ¯2)∘×)∘↓∘(-∘50)∘⊖∘⍉      ⍝ scale: complex from gui coords.

This is so that we can _generate_ zg's inverse directly (as zg⍣¯1),  rather than
having to  code a separate explicit inverse function.  In fact, it is convenient
to supply an "under" operator, which applies its left operand _under_ the effect
of its right operand.

    under←{⍵⍵⍣¯1 ⍺⍺ ⍵⍵ ⍵}                   ⍝ aka "dual".

Thanks to Giangluigi Quario for help with the GUI.

Examples:

    + cxdraw 2          ⍝ complex conjugate (horizontal mirror-image).

    *∘0.5 cxdraw 2      ⍝ complex square root (investigate branch cut).

    8∘○ cxdraw 2        ⍝ Pythagorian function (investigate both branch cuts).

    * cxdraw 8          ⍝ complex exponential (find 4 "fixpoints", where z≡*z).

    *∘○ cxdraw 2        ⍝ see that *○0j1 → ¯1 (Euler).

    {+/×⍨1 2○⍵}cxdraw 2 ⍝ sin-squared + cos-squared (watch form Caption).

    0j1∘* cxdraw 2      ⍝ see that 0j1*0j1 is a real(!) number.

    ÷ cxdraw 2          ⍝ complex reciprocal (maps until circle inside out).

    1 +∘(×⍨)cxdraw 2    ⍝ mandelbrot mode: try shading in the region 2>|⍵.

Back to: contents

Back to: Workspaces