--* From postmaster%watson.vnet.ibm.com@yktvmv.watson.ibm.com  Fri May 27 18:04:02 1994
--* Received: from yktvmv-ob.watson.ibm.com by asharp.watson.ibm.com (AIX 3.2/UCB 5.64/930311)
--*           id AA25894; Fri, 27 May 1994 18:04:02 -0400
--* Received: from watson.vnet.ibm.com by yktvmv.watson.ibm.com (IBM VM SMTP V2R3)
--*    with BSMTP id 8139; Fri, 27 May 94 18:03:59 EDT
--* Received: from YKTVMV by watson.vnet.ibm.com with "VAGENT.V1.0"
--*           id <A.ROOT.NOTE.VAGENT2.6809.May.27.18:03:58.-0400>
--*           for asbugs@watson; Fri, 27 May 94 18:03:59 -0400
--* Received: from YKTVMV by watson.vnet.ibm.com with "VAGENT.V1.0"
--*           id 6805; Fri, 27 May 1994 18:03:58 EDT
--* Received: from daly.watson.ibm.com by yktvmv.watson.ibm.com (IBM VM SMTP V2R3)
--*    with TCP; Fri, 27 May 94 18:03:57 EDT
--* Received: by daly.watson.ibm.com (AIX 3.2/UCB 5.64/4.03)
--*           id AA21514; Fri, 27 May 1994 18:03:15 -0500
--* Date: Fri, 27 May 1994 18:03:15 -0500
--* From: root@daly.watson.ibm.com
--* Message-Id: <9405272303.AA21514@daly.watson.ibm.com>
--* To: asbugs@watson.ibm.com
--* Subject: [3] p2.1 ... cannot find apply for Point(DoubleFloat) [drawpak.as][0.35.3]

--@ Fixed  by:  SSD   Fri Jun 24 11:08:32 EDT 1994 
--@ Tested by:  none 
--@ Summary:    Extend substitution fixes correct this bug [v35.8.3], plus lots of corrections to the example code. 

#include "axiom.as"
+++ Date Last Updated: May 21, 1994 asharp conversion by tim daly

DF ==> DoubleFloat;
import from DF;
C ==> Complex DF;
import from C;
S ==> Segment DF;
import from S;
PC ==> Record(fun:DF, arg:DF);
import from PC;
INT ==> Integer;
import from INT;
NNI ==> NonNegativeInteger;
import from NNI;
VIEW3D ==> ThreeDimensionalViewport;
import from VIEW3D;
ARRAY2 ==> TwoDimensionalArray(PC);
import from ARRAY2;
PDF    ==> Point(DF);
import from PDF;
LPDF   ==> List(PDF);
import from LPDF;
LLPDF  ==> List(LPDF);
import from LPDF;


--)abbrev package DRAWCX DrawComplex

+++ Description: \axiomType{DrawComplex} provides some facilities
+++ for drawing complex functions.

DrawComplex(): with
   {drawComplex: (C -> C,S,S,Boolean) -> VIEW3D;
      ++ drawComplex(f,rRange,iRange,arrows?)
      ++ draws a complex function as a height field.
      ++ It uses the complex norm as the height
      ++ and the complex argument as the color.
      ++ It will optionally draw arrows on the surface indicating the direction
      ++ of the complex value.\newline
      ++ Sample call:
      ++   \spad{f z == exp(1/z)}
      ++   \spad{drawComplex(f, 0.3..3, 0..2*%pi, false)}
      ++ Parameter descriptions:
      ++   f:  the function to draw
      ++   rRange : the range of the real values
      ++   iRange : the range of imaginary values
      ++   arrows? : a flag indicating whether to draw the phase arrows for f
      ++ Call the functions \axiomFunFrom{setRealSteps}{DrawComplex} and
      ++ \axiomFunFrom{setImagSteps}{DrawComplex} to change the
      ++ number of steps used in each direction.
    drawComplexVectorField: (C -> C,S,S) -> VIEW3D;
      ++ drawComplexVectorField(f,rRange,iRange)
      ++ draws a complex vector field using arrows on the \spad{x--y} plane.
      ++ These vector fields should be viewed from the top by pressing the
      ++ "XY" translate button on the 3-d viewport control panel.\newline
      ++ Sample call:
      ++    \spad{f z == sin z}
      ++    \spad{drawComplexVectorField(f, -2..2, -2..2)}
      ++ Parameter descriptions:
      ++   f : the function to draw
      ++   rRange : the range of the real values
      ++   iRange : the range of the imaginary values
      ++ Call the functions \axiomFunFrom{setRealSteps}{DrawComplex} and
      ++ \axiomFunFrom{setImagSteps}{DrawComplex} to change the
      ++ number of steps used in each direction.
    setRealSteps: INT -> INT;
      ++ setRealSteps(i)
      ++ sets to i the number of steps to use in the real direction
      ++ when drawing complex functions. Returns i.
    setImagSteps: INT -> INT;
      ++ setImagSteps(i)
      ++ sets to i  the number of steps to use in the imaginary direction
      ++ when drawing complex functions. Returns i.
    setClipValue: DF-> DF;
      ++ setClipValue(x)
      ++ sets to x the maximum value to plot when drawing complex functions.
      ++ Returns x.
  }
 == add
   {-- relative size of the arrow head compared to the length of the arrow
    arrowScale: DF := (0.125)::DF;
    arrowAngle: DF := pi()-pi()/(20::DF);    -- angle of the arrow head
    realSteps: INT  := 11;   -- the number of steps in the real direction
    imagSteps: INT  := 11;   -- the number of steps in the imaginary direction
    clipValue: DF  := 10::DF; -- the maximum length of a vector to draw


    -- Add an arrow head to a line segment, which starts at 'p1', ends at 'p2',
    -- has length 'len', and and angle 'arg'.  We pass 'len' and 'arg' as
    -- arguments since thet were already computed by the calling program
    makeArrow(p1:PDF, p2:PDF, len: DF, arg:DF):List List Point DF ==
     {c1:DF := cos(arg + arrowAngle);
      s1:DF := sin(arg + arrowAngle);
      c2:DF := cos(arg - arrowAngle);
      s2:DF := sin(arg - arrowAngle);
      p3:PDF := point [((p2@PDF).1)@DF + c1*arrowScale*len,
                       ((p2@PDF).2)@DF + s1*arrowScale*len,
                        p2.3, p2.4];
      p4:PDF := point [p2.1 + c2*arrowScale*len, p2.2 + s2*arrowScale*len,
                   p2.3, p2.4];
      [[p1, p2, p3], [p2, p4]]}

    -- clip a value in the interval (-clip...clip)
    clipFun(x:DF):DF ==
      {min(max(x, -clipValue), clipValue)}

    drawComplex(f:(C->C), realRange:S, imagRange:S, arrows?:Boolean):VIEW3D ==
     {delReal:DF := (hi(realRange)@DF - lo(realRange)@DF)/realSteps::DF;
      delImag:DF := (hi(imagRange)@DF - lo(imagRange)@DF)/imagSteps::DF;
      funTable: ARRAY2(PC) :=
         new((realSteps::NNI)+1, (imagSteps::NNI)+1, [0,0]$PC)$ARRAY2(PC);
      real := lo(realRange);
      for i:DF in 1..realSteps+1 repeat
       {imag := lo(imagRange);
        for j in 1..imagSteps+1 repeat
         {z:C := f complex(real, imag);
          funTable(i,j) := [clipFun(sqrt norm z), argument(z)]$PC;
          imag:DF := imag + delImag}
        real:DF := real + delReal}
      llp := empty()$(List List Point DF);
      real := lo(realRange);
      for i:DF in 1..realSteps+1 repeat
       {imag := lo(imagRange);
        lp := empty()$(List Point DF);
        for j:DF in 1..imagSteps+1 repeat
         {p := point [real, imag, funTable(i,j).rr, funTable(i,j).th];
          lp := cons(p, lp);
          imag:DF := imag + delImag}
        real:DF := real + delReal;
        llp:LLPDF := cons(lp:LPDF, llp:LPDF)}
      space := mesh(llp)$(ThreeSpace DF);
      if arrows? then
       {real := lo(realRange);
        for i:DF in 1..realSteps+1 repeat
         {imag := lo(imagRange);
          for j:DF in 1..imagSteps+1 repeat
           {arg:PC := funTable(i,j).th;
            p1 := point [real,imag, funTable(i,j).rr, arg];
            len:DF := delReal*2.0::DF;
            p2 := point [p1.1 + len*cos(arg), p1.2 + len*sin(arg), p1.3, p1.4];
            arrow:LLPDF := makeArrow(p1, p2, len, arg);
            for a in arrow repeat curve(space, a)$(ThreeSpace DF);
            imag:DF := imag + delImag}
          real:DF := real + delReal}}
      makeViewport3D(space, "Complex Function")$VIEW3D}

    drawComplexVectorField(f:(C->C),realRange:S,imagRange:S): VIEW3D ==
     {-- compute the steps size of the grid
      delReal := (hi(realRange)@DF - lo(realRange)@DF)/realSteps::DF;
      delImag := (hi(imagRange)@DF - lo(imagRange)@DF)/imagSteps::DF;
      -- create the space to hold the arrows
      space := create3Space()$(ThreeSpace DF);
      real := lo(realRange);
      for i:DF in 1..realSteps+1 repeat
       {imag := lo(imagRange);
        for j:DF in 1..imagSteps+1 repeat
         {-- compute the function
          z:C := f complex(real, imag);
          -- get the direction of the arrow
          arg:DF := argument z;
          -- get the length of the arrow
          len:DF := clipFun(sqrt norm z);
          -- create point at the base of the arrow
          p1:PDF :=  point [real, imag, 0::DF, arg];
          -- scale the arrow length so it isn't too long
          scaleLen:DF := delReal * len;
          -- create the point at the top of the arrow
          p2:PDF := point [p1.1 + scaleLen*cos(arg), p1.2 + scaleLen*sin(arg),
                       0::DF, arg];
          -- make the pointer at the top of the arrow
          arrow:LLPDF := makeArrow(p1, p2, scaleLen, arg);
          -- add the line segments in the arrow to the space
          for a:LPDF in arrow repeat curve(space, a)$(ThreeSpace DF);
          imag:DF := imag + delImag}
        real:DF := real + delReal}
      -- draw the vector field
      makeViewport3D(space, "Complex Vector Field")$VIEW3D}

    -- set the number of steps to use in the real direction
    setRealSteps(n:INT):INT ==
     {free realSteps := n}

    -- set the number of steps to use in the imaginary direction
    setImagSteps(n:INT):INT ==
     {free imagSteps := n}

    -- set the maximum value to plot
    setClipValue(clip:DF):DF ==
     {free clipValue := clip}
} -- DrawComplex

 
