)abbrev domain GRIMAGE GraphImage
++ Author: Jim Wen
++ Date Created: 27 April 1989
++ Basic Operations:
++ Related Constructors:
++ Also See:
++ AMS Classifications:
++ Keywords:
++ References:
++ Description: TwoDimensionalGraph creates virtual two dimensional graphs
++ (to be displayed on TwoDimensionalViewports).
GraphImage () : Exports == Implementation where

  VIEW    ==> _$ViewportServer$Lisp
  sendI   ==> sockSendInt$Lisp
  sendSF  ==> sockSendFloat$Lisp
  sendSTR ==> sockSendString$Lisp
  getI    ==> sockGetInt$Lisp
  getSF   ==> sockGetFloat$Lisp

  typeGRAPH  ==> 2
  typeVIEW2D ==> 3

  makeGRAPH  ==> (-1)$SingleInteger
  makeVIEW2D ==> (-1)$SingleInteger

  I   ==> Integer
  PI  ==> PositiveInteger
  NNI ==> NonNegativeInteger
  SF  ==> DoubleFloat
  F   ==> Float
  L   ==> List
  P   ==> Point(SF)
  V   ==> Vector
  SEG ==> Segment
  RANGESF   ==> L SEG SF
  RANGEF    ==> L SEG F
  UNITSF   ==> L SF
  UNITF    ==> L F
  PAL ==> Palette
  E   ==> OutputForm
  DROP ==> DrawOption
  PP ==> PointPackage(SF)
  COORDSYS ==> CoordinateSystems(SF)

  Exports ==> SetCategory with
    graphImage      :  ()                                        -> %
      ++ graphImage() returns an empty graph with 0 point lists
      ++ of the domain \spadtype{GraphImage}.  A graph image contains
      ++ the graph data component of a two dimensional viewport.
    sendGraphImage  :  %  -> Void
      ++ sendGraphImage(gi) takes the given graph, \spad{gi} of the
      ++ domain \spadtype{GraphImage}, and sends it's data to the
      ++ viewport manager where it waits to be included in a two-dimensional
      ++ viewport window.  \spad{gi} cannot be an empty graph.
    makeGraphImage  :  (L L P)                                   -> %
      ++ makeGraphImage(llp) returns a graph of the domain
      ++ \spadtype{GraphImage} which is composed of the points and
      ++ lines from the list of lists of points, \spad{llp}, with
      ++ default point size and default point and line colours.
    makeGraphImage  :  (L L P, L PAL, L PAL, L PI)                  -> %
      ++ makeGraphImage(llp, lpal1, lpal2, lp) returns a graph of the
      ++ domain \spadtype{GraphImage} which is composed of the points
      ++ and lines from the list of lists of points, \spad{llp}, whose
      ++ point colors are indicated by the list of palette colors,
      ++ \spad{lpal1}, and whose lines are colored according to the list
      ++ of palette colors, \spad{lpal2}.  The paramater lp is a list of
      ++ integers which denote the size of the data points.
    makeGraphImage  :  (L L P, L PAL, L PAL, L PI, L DROP)           -> %
      ++ makeGraphImage(llp, lpal1, lpal2, lp, lopt) returns a graph of
      ++ the domain \spadtype{GraphImage} which is composed of the
      ++ points and lines from the list of lists of points, \spad{llp},
      ++ whose point colors are indicated by the list of palette colors,
      ++ \spad{lpal1}, and whose lines are colored according to the list
      ++ of palette colors, \spad{lpal2}.  The paramater lp is a list of
      ++ integers which denote the size of the data points, and \spad{lopt}
      ++ is the list of draw command options.
    pointLists      :  %                                         -> L L P
      ++ pointLists(gi) returns the list of lists of points which compose
      ++ the given graph, \spad{gi}, of the domain \spadtype{GraphImage}.
    key             :  %                                         -> I
      ++ key(gi) returns the process ID of the given graph, \spad{gi},
      ++ of the domain \spadtype{GraphImage}.
    ranges          :  %                                         -> RANGEF
      ++ ranges(gi) returns the list of ranges of the point components from
      ++ the indicated graph, \spad{gi}, of the domain \spadtype{GraphImage}.
    ranges          :  (%, RANGEF)                                -> RANGEF
      ++ ranges(gi, lr) modifies the list of ranges for the given graph,
      ++ \spad{gi} of the domain \spadtype{GraphImage}, to be that of the
      ++ list of range segments, \spad{lr}, and returns the new range list
      ++ for \spad{gi}.
    units           :  %                                         -> UNITF
      ++ units(gi) returns the list of unit increments for the x and y
      ++ axes of the indicated graph, \spad{gi}, of the domain
      ++ \spadtype{GraphImage}.
    units           :  (%, UNITF)                                 -> UNITF
      ++ units(gi, lu) modifies the list of unit increments for the x and y
      ++ axes of the given graph, \spad{gi} of the domain
      ++ \spadtype{GraphImage}, to be that of the list of unit increments,
      ++ \spad{lu}, and returns the new list of units for \spad{gi}.
    component       :  (%, L P, PAL, PAL, PI)                        -> Void
      ++ component(gi, lp, pal1, pal2, p) sets the components of the
      ++ graph, \spad{gi} of the domain \spadtype{GraphImage}, to the
      ++ values given.  The point list for \spad{gi} is set to the list
      ++ \spad{lp}, the color of the points in \spad{lp} is set to
      ++ the palette color \spad{pal1}, the color of the lines which
      ++ connect the points \spad{lp} is set to the palette color
      ++ \spad{pal2}, and the size of the points in \spad{lp} is given
      ++ by the integer p.
    component       :  (%, P)                                     -> Void
      ++ component(gi, pt) modifies the graph \spad{gi} of the domain
      ++ \spadtype{GraphImage} to contain one point component, \spad{pt}
      ++ whose point color, line color and point size are determined by
      ++ the default functions \spadfun{pointColorDefault},
      ++ \spadfun{lineColorDefault}, and \spadfun{pointSizeDefault}.
    component       :  (%, P, PAL, PAL, PI)                          -> Void
      ++ component(gi, pt, pal1, pal2, ps) modifies the graph \spad{gi} of
      ++ the domain \spadtype{GraphImage} to contain one point component,
      ++ \spad{pt} whose point color is set to the palette color \spad{pal1},
      ++ line color is set to the palette color \spad{pal2}, and point
      ++ size is set to the positive integer \spad{ps}.
    appendPoint     :  (%, P)                                     -> Void
      ++ appendPoint(gi, pt) appends the point \spad{pt} to the end
      ++ of the list of points component for the graph, \spad{gi}, which is
      ++ of the domain \spadtype{GraphImage}.
    point           :  (%, P, PAL)                                 -> Void
      ++ point(gi, pt, pal) modifies the graph \spad{gi} of the domain
      ++ \spadtype{GraphImage} to contain one point component, \spad{pt}
      ++ whose point color is set to be the palette color \spad{pal}, and
      ++ whose line color and point size are determined by the default
      ++ functions \spadfun{lineColorDefault} and \spadfun{pointSizeDefault}.
    coerce          :  L L P                                     -> %
      ++ coerce(llp) creates and returns a graph of the domain
      ++ \spadtype{GraphImage} which is composed of the list of list
      ++ of points given by \spad{llp}, and whose point colors, line colors
      ++ and point sizes are determined by the default functions
      ++ \spadfun{pointColorDefault}, \spadfun{lineColorDefault}, and
      ++ \spadfun{pointSizeDefault}.  The graph data is then sent to the
      ++ viewport manager where it waits to be included in a two-dimensional
      ++ viewport window.
    coerce          :  %                                         -> E
      ++ coerce(gi) returns the indicated graph, \spad{gi}, of domain
      ++ \spadtype{GraphImage} as output of the domain \spadtype{OutputForm}.
    putColorInfo    : (L L P, L PAL)                              -> L L P
      ++ putColorInfo(llp, lpal) takes a list of list of points, \spad{llp},
      ++ and returns the points with their hue and shade components
      ++ set according to the list of palette colors, \spad{lpal}.

  Implementation ==> add
    import from Color()
    import from Palette()
    import from ViewDefaultsPackage()
    import from PlotTools()
    import from DrawOptionFunctions0
    import from P
    import from PP
    import from COORDSYS

    Rep := Record(key : I, rangesField : RANGESF, unitsField : UNITSF, _
       llPoints : L L P, pointColors : L PAL, lineColors : L PAL, pointSizes : L PI, _
       optionsField : L DROP)

--%Internal Functions

    graph       : RANGEF                          -> %
    scaleStep   : SEG SF -> SF


    numberCheck(nums : Point SF) : Void ==
      for i in minIndex(nums)..maxIndex(nums) repeat
        COMPLEXP(nums.(i::PositiveInteger))$Lisp =>
          error "An unexpected complex number was encountered in the calculations."


    doOptions(g : Rep) : Void ==
      lr : RANGEF := ranges(g.optionsField, ranges g)
      if (#lr > 1$I) then
        g.rangesField := [segment(convert(low(lr.1))@SF,
                                  convert(high(lr.1))@SF)$(Segment(SF)),
                          segment(convert(low(lr.2))@SF,
                                  convert(high(lr.2))@SF)$(Segment(SF))]
      else
        g.rangesField := []
      lu : UNITF := units(g.optionsField, units g)
      if (#lu > 1$I) then
        g.unitsField := [convert(lu.1)@SF, convert(lu.2)@SF]
      else
        g.unitsField := []
    -- etc - graphimage specific stuff...

    putColorInfo(llp, listOfPalettes) ==
      llp2 : L L P := []
      for lp in llp for pal in listOfPalettes repeat
        lp2 : L P := []
        daHue   := (hue(hue pal))::SF
        daShade := (shade pal)::SF
        for p in lp repeat
          if (d := dimension p) < 3 then
            p := extend(p, [daHue, daShade])
          else
            p.3 := daHue
            d < 4 => p := extend(p, [daShade])
            p.4 := daShade
          lp2 := cons(p, lp2)
        llp2 := cons(reverse! lp2, llp2)
      reverse! llp2

    graph demRanges ==
      empty?(demRanges) =>  [ 0, [], [], [], [], [], [], [] ]
      demRangesSF : RANGESF := _
        [segment(convert(low(demRanges.1))@SF,
                 convert(high(demRanges.1))@SF)$(Segment(SF)),
         segment(convert(low(demRanges.1))@SF,
                 convert(high(demRanges.1))@SF)$(Segment(SF))]
      [ 0, demRangesSF, [], [], [], [], [], [] ]

    scaleStep(range_seg) ==                        -- MGR
      range := high(range_seg) - low(range_seg)

      adjust : NNI
      tryStep : SF
      scaleDown : SF
      numerals : String
      adjust := 0
      while range < 100.0::SF repeat
        adjust := adjust + 1
        range := range * 10.0::SF -- might as well take big steps
      tryStep := range/10.0::SF
      numerals := string(((retract(ceiling(tryStep)$SF)$SF)@I))$String
      scaleDown := (10@I ^$I (((#(numerals)@I) - 1$I) pretend PI))::SF
      scaleDown*ceiling(tryStep/scaleDown - 0.5::SF)/((10 ^$I adjust)::SF)

    figureUnits(ranges : RANGESF) : UNITSF ==
        [scaleStep(first(ranges)), scaleStep(second(ranges))]

    roundRange(range : SEG SF) : SEG SF ==
        single_eps := (2.0::SF)^(-19)
        low := low(range)
        low :=
            abs(low) < 1 => low - single_eps
            low < 0 => (1.0::SF + single_eps)*low
            (1.0::SF - single_eps)*low
        high := high(range)
        high :=
            abs(high) < 1 =>  high + single_eps
            high < 0 => (1.0::SF - single_eps)*high
            (1.0::SF + single_eps)*high
        low..high

    roundRanges(ranges : RANGESF) : RANGESF ==
        [roundRange(first(ranges)), roundRange(second(ranges))]

    plotLists(graf : Rep, listOfListsOfPoints : L L P, listOfPointColors : L PAL, listOfLineColors : L PAL, listOfPointSizes : L PI) : % ==
      givenLen := #listOfListsOfPoints
        -- take out point lists that are actually empty
      listOfListsOfPoints := [l for l in listOfListsOfPoints | not empty?(l)]
      if empty?(listOfListsOfPoints) then
        error "GraphImage was given a list that contained no valid point lists"
      if ((len := #listOfListsOfPoints) ~= givenLen) then
          say("   Warning: Ignoring pointless point list")$DisplayPackage
      graf.llPoints := listOfListsOfPoints
        -- do point colors
      if ((givenLen := #listOfPointColors) > len) then
         -- pad or discard elements if given list has length different from the point list
        graf.pointColors := concat(listOfPointColors,
            new((len - givenLen)::NonNegativeInteger + 1, pointColorDefault()))
      else graf.pointColors := first(listOfPointColors, len)
        -- do line colors
      if ((givenLen := #listOfLineColors) > len) then
        graf.lineColors := concat(listOfLineColors,
             new((len - givenLen)::NonNegativeInteger + 1, lineColorDefault()))
      else graf.lineColors := first(listOfLineColors, len)
        -- do point sizes
      if ((givenLen := #listOfPointSizes) > len) then
        graf.pointSizes := concat(listOfPointSizes,
             new((len - givenLen)::NonNegativeInteger + 1, pointSizeDefault()))
      else graf.pointSizes := first(listOfPointSizes, len)
      graf

    sendGraphImage graf ==
      doOptions(graf)
      (s := #(graf.llPoints)) = 0 =>
        error "You are trying to make a graph with no points"
      key graf ~= 0 =>
        error "You are trying to draw over an existing graph"
      transform := coord(graf.optionsField, cartesian$COORDSYS)$DrawOptionFunctions0
      graf.llPoints := putColorInfo(graf.llPoints, graf.pointColors)
      if empty?(ranges graf) then  -- figure out best ranges for points
        graf.rangesField := calcRanges(graf.llPoints)  --::V SEG SF
      graf.rangesField := roundRanges(graf.rangesField)
      if empty?(units graf) then  -- figure out best ranges for points
        graf.unitsField := figureUnits(graf.rangesField)  --::V SEG SF
      say(
        "   Graph data being transmitted to the viewport manager..."
                  )$DisplayPackage
      sendI(VIEW, typeGRAPH)
      sendI(VIEW, makeGRAPH)
      tonto := (graf.rangesField)::RANGESF
      sendSF(VIEW, low(first(tonto)))
      sendSF(VIEW, high(first(tonto)))
      sendSF(VIEW, low(second(tonto)))
      sendSF(VIEW, high(second(tonto)))
      sendSF(VIEW, first (graf.unitsField))
      sendSF(VIEW, second (graf.unitsField))
      sendI(VIEW, s)    -- how many lists of points are being sent
      for aList in graf.llPoints for pColor in graf.pointColors for lColor in graf.lineColors for s in graf.pointSizes repeat
        sendI(VIEW, #aList)   -- how many points in this list
        for p in aList repeat
          aPoint := transform p
          sendSF(VIEW, xCoord aPoint)
          sendSF(VIEW, yCoord aPoint)
          sendSF(VIEW, hue(p)$PP)  -- ?use aPoint as well...?
          sendSF(VIEW, shade(p)$PP)
        hueShade := hue hue pColor + shade pColor * numberOfHues()
        sendI(VIEW, hueShade)
        hueShade := (hue hue lColor -1)*5 + shade lColor
        sendI(VIEW, hueShade)
        sendI(VIEW, s)
      graf.key := getI(VIEW)


--%Exported Functions
    key graf                  == graf.key
    pointLists graf           == graf.llPoints
    ranges graf                ==
      empty?(graf.rangesField) => []
      [segment(convert(low(graf.rangesField.1))@F,
               convert(high(graf.rangesField.1))@F),
       segment(convert(low(graf.rangesField.2))@F,
               convert(high(graf.rangesField.2))@F)]
    ranges(graf, rangesList)     ==
      graf.rangesField :=
        [segment(convert(low(rangesList.1))@SF,
                 convert(high(rangesList.1))@SF),
         segment(convert(low(rangesList.2))@SF,
                 convert(high(rangesList.2))@SF)]
      rangesList
    units graf                ==
      empty?(graf.unitsField) => []
      [convert(graf.unitsField.1)@F, convert(graf.unitsField.2)@F]
    units (graf, unitsToBe)    ==
      graf.unitsField := [convert(unitsToBe.1)@SF, convert(unitsToBe.2)@SF]
      unitsToBe
    graphImage                == graph []

    makeGraphImage(llp) ==
      makeGraphImage(llp,
        [pointColorDefault() for i in 1..(l := #llp)],
         [lineColorDefault() for i in 1..l],
          [pointSizeDefault() for i in 1..l])

    makeGraphImage(llp, lpc, llc, lps) ==
      makeGraphImage(llp, lpc, llc, lps, [])

    makeGraphImage(llp, lpc, llc, lps, opts) ==
      graf := graph(ranges(opts, []))
      graf.optionsField := opts
      graf := plotLists(graf, llp, lpc, llc, lps)
      transform := coord(graf.optionsField, cartesian$COORDSYS)$DrawOptionFunctions0
      for aList in graf.llPoints repeat
        for p in aList repeat
          aPoint := transform p
          numberCheck aPoint
      graf

    component (graf : %, ListOfPoints : L P, PointColor : PAL, LineColor : PAL, PointSize : PI) ==
      graf.llPoints    := append(graf.llPoints, [ListOfPoints])
      graf.pointColors := append(graf.pointColors, [PointColor])
      graf.lineColors  := append(graf.lineColors, [LineColor])
      graf.pointSizes  := append(graf.pointSizes, [PointSize])

    component (graf, aPoint) ==
      component(graf, aPoint, pointColorDefault(), lineColorDefault(), pointSizeDefault())

    component (graf : %, aPoint : P, PointColor : PAL, LineColor : PAL, PointSize : PI) ==
      component (graf, [aPoint], PointColor, LineColor, PointSize)

    appendPoint (graf, aPoint) ==
      num : I  := #(graf.llPoints) - 1
      num < 0 => error "No point lists to append to!"
      (graf.llPoints.num) := append((graf.llPoints.num), [aPoint])

    point (graf, aPoint, PointColor) ==
      component(graf, aPoint, PointColor, lineColorDefault(), pointSizeDefault())

    coerce (llp : L L P) : % ==
      makeGraphImage(llp,
          [pointColorDefault() for i in 1..(l := #llp)],
                [lineColorDefault() for i in 1..l],
                     [pointSizeDefault() for i in 1..l])

    coerce (graf : %) : E ==
        hconcat([message("Graph with "), (p := # pointLists graf)::E,
            message(p = 1 => " point list"; " point lists")])


)abbrev domain VIEW2D TwoDimensionalViewport
++ Author: Jim Wen
++ Date Created: 28 April 1989
++ Basic Operations:
++ Related Constructors:
++ Also See:
++ AMS Classifications:
++ Keywords:
++ References:
++ Description: TwoDimensionalViewport creates viewports to display graphs.
TwoDimensionalViewport () : Exports == Implementation where

  VIEW    ==> _$ViewportServer$Lisp
  sendI   ==> sockSendInt$Lisp
  sendSF  ==> sockSendFloat$Lisp
  sendSTR ==> sockSendString$Lisp
  getI    ==> sockGetInt$Lisp
  getSF   ==> sockGetFloat$Lisp

  typeGRAPH  ==> 2
  typeVIEW2D ==> 3

  makeGRAPH  ==> (-1)$SingleInteger
  makeVIEW2D ==> (-1)$SingleInteger

  I    ==> Integer
  PI   ==> PositiveInteger
  NNI  ==> NonNegativeInteger
  XY   ==> Record( X : I, Y : I )
  XYP  ==> Record( X : PI, Y : PI )
  XYNN ==> Record( X : NNI, Y : NNI )
  F    ==> Float
  SF   ==> DoubleFloat
  STR  ==> String
  L    ==> List
  V    ==> Vector
  E    ==> OutputForm
  FLAG ==> Record( showCP : I )
  PAL  ==> Palette()
  B    ==> Boolean
  G    ==> GraphImage
  GS   ==> Record( scaleX : SF, scaleY : SF, deltaX : SF, deltaY : SF, _
                  points : I, connect : I, spline : I, _
                  axes : I, axesColor : PAL, units : I, unitsColor : PAL, _
                  showing : I)
  GU   ==> Union(G,"undefined")
  DROP ==> DrawOption
  POINT ==> Point(SF)

  TRANSLATE2D     ==> 0$I
  SCALE2D         ==> 1$I
  pointsOnOff     ==> 2
  connectOnOff    ==> 3
  spline2D        ==> 4   -- used for controlling regions, now
  reset2D         ==> 5
  hideControl2D   ==> 6
  closeAll2D      ==> 7
  axesOnOff2D     ==> 8
  unitsOnOff2D    ==> 9

  SPADBUTTONPRESS ==> 100
  MOVE            ==> 102
  RESIZE          ==> 103
  TITLE           ==> 104
  showing2D       ==> 105  -- as defined in include/actions.h
  putGraph2D      ==> 106
  writeView       ==> 110
  axesColor2D     ==> 112
  unitsColor2D    ==> 113
  getPickedPTS    ==> 119

  graphStart      ==> 13   -- as defined in include/actions.h

  noControl ==> 0$I

  yes       ==> 1$I
  no        ==> 0$I

  maxGRAPHS ==> 9::I   -- should be the same as maxGraphs in include/view2D.h

  fileTypeDefs ==> ["PIXMAP"]    -- see include/write.h for things to include

  Exports ==> SetCategory with
    getPickedPoints : % -> L POINT
      ++ getPickedPoints(x)
      ++ returns a list of small floats for the points the
      ++ user interactively picked on the viewport
      ++ for full integration into the system, some design
      ++ issues need to be addressed: e.g. how to go through
      ++ the GraphImage interface, how to default to graphs, etc.
    viewport2D     : ()                                     -> %
      ++ viewport2D() returns an undefined two-dimensional viewport
      ++ of the domain \spadtype{TwoDimensionalViewport} whose
      ++ contents are empty.
    makeViewport2D : %                                      -> %
      ++ makeViewport2D(v) takes the given two-dimensional viewport,
      ++ v, of the domain \spadtype{TwoDimensionalViewport} and
      ++ displays a viewport window on the screen which contains
      ++ the contents of v.
    options        : %                                      -> L DROP
      ++ options(v) takes the given two-dimensional viewport, v, of the
      ++ domain \spadtype{TwoDimensionalViewport} and returns a list
      ++ containing the draw options from the domain \spadtype{DrawOption}
      ++ for v.
    options        : (%, L DROP)                             -> %
      ++ options(v, lopt) takes the given two-dimensional viewport, v,
      ++ of the domain \spadtype{TwoDimensionalViewport} and returns
      ++ v with it's draw options modified to be those which are indicated
      ++ in the given list, \spad{lopt} of domain \spadtype{DrawOption}.
    makeViewport2D : (G, L DROP)                             -> %
      ++ makeViewport2D(gi, lopt) creates and displays a viewport window
      ++ of the domain \spadtype{TwoDimensionalViewport} whose graph
      ++ field is assigned to be the given graph, \spad{gi}, of domain
      ++ \spadtype{GraphImage}, and whose options field is set to be
      ++ the list of options, \spad{lopt} of domain \spadtype{DrawOption}.
    graphState     : (%, PI, SF, SF, SF, SF, I, I, I, I, PAL, I, PAL, I) -> Void
      ++ graphState(v, num, sX, sY, dX, dY, pts, lns, box, axes, axesC, un, unC, cP)
      ++ sets the state of the characteristics for the graph indicated
      ++ by \spad{num} in the given two-dimensional viewport v, of domain
      ++ \spadtype{TwoDimensionalViewport}, to the values given as
      ++ parameters.  The scaling of the graph in the x and y component
      ++ directions is set to be \spad{sX} and \spad{sY}; the window
      ++ translation in the x and y component directions is set to be
      ++ \spad{dX} and \spad{dY}; The graph points, lines, bounding box,
      ++ axes, or units will be shown in the viewport if their given
      ++ parameters \spad{pts}, \spad{lns}, \spad{box}, \spad{axes} or
      ++ \spad{un} are set to be \spad{1}, but will not be shown if they
      ++ are set to \spad{0}.  The color of the axes and the color of the
      ++ units are indicated by the palette colors \spad{axesC} and
      ++ \spad{unC} respectively.  To display the control panel when
      ++ the viewport window is displayed, set \spad{cP} to \spad{1},
      ++ otherwise set it to \spad{0}.
    graphStates    : %                                      -> V GS
      ++ graphStates(v) returns and shows a listing of a record containing
      ++ the current state of the characteristics of each of the ten graph
      ++ records in the given two-dimensional viewport, v, which is of
      ++ domain \spadtype{TwoDimensionalViewport}.
    graphs         : %                                      -> V GU
      ++ graphs(v) returns a vector, or list, which is a union of all
      ++ the graphs, of the domain \spadtype{GraphImage}, which are
      ++ allocated for the two-dimensional viewport, v, of domain
      ++ \spadtype{TwoDimensionalViewport}.  Those graphs which have
      ++ no data are labeled "undefined", otherwise their contents
      ++ are shown.
    title          : (%, STR)                                -> Void
      ++ title(v, s) changes the title which is shown in the two-dimensional
      ++ viewport window, v of domain \spadtype{TwoDimensionalViewport}.
    putGraph       : (%, G, PI)                               -> Void
      ++ putGraph(v, gi, n) sets the graph field indicated by n, of the
      ++ indicated two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, to be the graph, \spad{gi}
      ++ of domain \spadtype{GraphImage}.  The contents of viewport, v,
      ++ will contain \spad{gi} when the function \spadfun{makeViewport2D}
      ++ is called to create the an updated viewport v.
    getGraph       : (%, PI)                                 -> G
      ++ getGraph(v, n) returns the graph which is of the domain
      ++ \spadtype{GraphImage} which is located in graph field n
      ++ of the given two-dimensional viewport, v, which is of the
      ++ domain \spadtype{TwoDimensionalViewport}.
    axes           : (%, PI, STR)                             -> Void
      ++ axes(v, n, s) displays the axes of the graph in field n of
      ++ the given two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, if s is "on", or does
      ++ not display the axes if s is "off".
    axes           : (%, PI, PAL)                             -> Void
      ++ axes(v, n, c) displays the axes of the graph in field n of
      ++ the given two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, with the axes color set to
      ++ the given palette color c.
    units          : (%, PI, STR)                             -> Void
      ++ units(v, n, s) displays the units of the graph in field n of
      ++ the given two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, if s is "on", or does
      ++ not display the units if s is "off".
    units          : (%, PI, PAL)                             -> Void
      ++ units(v, n, c) displays the units of the graph in field n of
      ++ the given two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, with the units color set to
      ++ the given palette color c.
    points         : (%, PI, STR)                             -> Void
      ++ points(v, n, s) displays the points of the graph in field n of
      ++ the given two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, if s is "on", or does
      ++ not display the points if s is "off".
    region         : (%, PI, STR)                             -> Void
      ++ region(v, n, s) displays the bounding box of the graph in
      ++ field n of the given two-dimensional viewport, v, which is
      ++ of domain \spadtype{TwoDimensionalViewport}, if s is "on",
      ++ or does not display the bounding box if s is "off".
    connect        : (%, PI, STR)                             -> Void
      ++ connect(v, n, s) displays the lines connecting the graph
      ++ points in field n of the given two-dimensional viewport, v,
      ++ which is of domain \spadtype{TwoDimensionalViewport}, if s
      ++ is "on", or does not display the lines if s is "off".
    controlPanel   : (%, STR)                                -> Void
      ++ controlPanel(v, s) displays the control panel of the given
      ++ two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, if s is "on", or hides
      ++ the control panel if s is "off".
    close          : %                                      -> Void
      ++ close(v) closes the viewport window of the given
      ++ two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, and terminates the
      ++ corresponding process ID.
    dimensions     : (%, NNI, NNI, PI, PI)                      -> Void
      ++ dimensions(v, x, y, width, height) sets the position of the
      ++ upper left-hand corner of the two-dimensional viewport, v,
      ++ which is of domain \spadtype{TwoDimensionalViewport}, to
      ++ the window coordinate x, y, and sets the dimensions of the
      ++ window to that of \spad{width}, \spad{height}.  The new
      ++ dimensions are not displayed until the function
      ++ \spadfun{makeViewport2D} is executed again for v.
    scale          : (%, PI, F, F)                             -> Void
      ++ scale(v, n, sx, sy) displays the graph in field n of the given
      ++ two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, scaled by the factor \spad{sx}
      ++ in the x-coordinate direction and by the factor \spad{sy} in
      ++ the y-coordinate direction.
    translate      : (%, PI, F, F)                             -> Void
      ++ translate(v, n, dx, dy) displays the graph in field n of the given
      ++ two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, translated by \spad{dx} in
      ++ the x-coordinate direction from the center of the viewport, and
      ++ by \spad{dy} in the y-coordinate direction from the center.
      ++ Setting \spad{dx} and \spad{dy} to \spad{0} places the center
      ++ of the graph at the center of the viewport.
    show           : (%, PI, STR)                             -> Void
      ++ show(v, n, s) displays the graph in field n of the given
      ++ two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, if s is "on", or does not
      ++ display the graph if s is "off".
    move           : (%, NNI, NNI)                            -> Void
      ++ move(v, x, y) displays the two-dimensional viewport, v, which
      ++ is of domain \spadtype{TwoDimensionalViewport}, with the upper
      ++ left-hand corner of the viewport window at the screen
      ++ coordinate position x, y.
    update         : (%, G, PI)                               -> Void
      ++ update(v, gr, n) drops the graph \spad{gr} in slot \spad{n}
      ++ of viewport \spad{v}. The graph gr must have been
      ++ transmitted already and acquired an integer key.
    resize         : (%, PI, PI)                              -> Void
      ++ resize(v, w, h) displays the two-dimensional viewport, v, which
      ++ is of domain \spadtype{TwoDimensionalViewport}, with a width
      ++ of w and a height of h, keeping the upper left-hand corner
      ++ position unchanged.
    write          : (%, STR)                                -> STR
      ++ write(v, s) takes the given two-dimensional viewport, v, which
      ++ is of domain \spadtype{TwoDimensionalViewport}, and creates
      ++ a directory indicated by s, which contains the graph data
      ++ files for v.
    write          : (%, STR, STR)                            -> STR
      ++ write(v, s, f) takes the given two-dimensional viewport, v, which
      ++ is of domain \spadtype{TwoDimensionalViewport}, and creates
      ++ a directory indicated by s, which contains the graph data
      ++ files for v and an optional file type f.
    write          : (%, STR, L STR)                          -> STR
      ++ write(v, s, lf) takes the given two-dimensional viewport, v, which
      ++ is of domain \spadtype{TwoDimensionalViewport}, and creates
      ++ a directory indicated by s, which contains the graph data
      ++ files for v and the optional file types indicated by the list lf.
    reset          :  %                                     -> Void
      ++ reset(v) sets the current state of the graph characteristics
      ++ of the given two-dimensional viewport, v, which is of domain
      ++ \spadtype{TwoDimensionalViewport}, back to their initial settings.
    key            :  %                                     -> I
      ++ key(v) returns the process ID number of the given two-dimensional
      ++ viewport, v, which is of domain \spadtype{TwoDimensionalViewport}.
    coerce         :  %                                     -> E
      ++ coerce(v) returns the given two-dimensional viewport, v, which
      ++ is of domain \spadtype{TwoDimensionalViewport} as output of
      ++ the domain \spadtype{OutputForm}.

  Implementation ==> add

    import from GraphImage()
    import from Color()
    import from Palette()
    import from ViewDefaultsPackage()
    import from DrawOptionFunctions0
    import from POINT

    Rep := Record (key : I, graphsField : V GU, graphStatesField : V GS, _
                   title : STR, moveTo : XYNN, size : XYP, flags : FLAG, optionsField : L DROP)

    import from FLAG

    defaultGS : GS := [convert(0.9)@SF, convert(0.9)@SF, 0$SF, 0$SF, _
                      yes, yes, no, _
                      yes, axesColorDefault(), no, unitsColorDefault(), _
                      yes]


     --% Local Functions
    checkViewport (viewport : %) : B ==
        -- checks to see if this viewport still exists
        -- by sending the key to the viewport manager and
        -- waiting for its reply after it checks it against
        -- the viewports in its list. a -1 means it doesn't
        -- exist.
      sendI(VIEW, viewport.key)
      i : Integer := getI(VIEW)
      (i < 0$I) =>
        viewport.key := 0$I
        error "This viewport has already been closed!"
      true

    doOptions(v : Rep) : Void ==
      v.title := title(v.optionsField,"FriCAS2D")
      -- etc - 2D specific stuff...

     --% Exported Functions

    options viewport ==
      viewport.optionsField

    options(viewport, opts) ==
      viewport.optionsField := opts
      viewport

    putGraph (viewport, aGraph, which) ==
      if ((which > maxGRAPHS) or (which < 1)) then
        error "Trying to put a graph with a negative index or too big an index"
      viewport.graphsField.which := aGraph

    getGraph (viewport, which) ==
      if ((which > maxGRAPHS) or (which < 1)) then
        error "Trying to get a graph with a negative index or too big an index"
      viewport.graphsField.which case "undefined" =>
        error "Graph is undefined!"
      viewport.graphsField.which::GraphImage


    graphStates viewport  == viewport.graphStatesField
    graphs viewport       == viewport.graphsField
    key viewport          == viewport.key

    dimensions(viewport, ViewX, ViewY, ViewWidth, ViewHeight) ==
      viewport.moveTo := [ViewX, ViewY]
      viewport.size   := [ViewWidth, ViewHeight]

    move(viewport, xLoc, yLoc) ==
      viewport.moveTo := [xLoc, yLoc]
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, MOVE)
        checkViewport viewport =>
          sendI(VIEW, xLoc)
          sendI(VIEW, yLoc)
          getI(VIEW)          -- acknowledge

    update(viewport, graph, slot) ==
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, putGraph2D)
        checkViewport viewport =>
          sendI(VIEW, key graph)
          sendI(VIEW, slot)
          getI(VIEW) -- acknowledge
      void()

    resize(viewport, xSize, ySize) ==
      viewport.size := [xSize, ySize]
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, RESIZE)
        checkViewport viewport =>
          sendI(VIEW, xSize)
          sendI(VIEW, ySize)
          getI(VIEW)          -- acknowledge

    translate(viewport, graphIndex, xTranslateF, yTranslateF) ==
      xTranslate := convert(xTranslateF)@SF
      yTranslate := convert(yTranslateF)@SF
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      viewport.graphStatesField.graphIndex.deltaX := xTranslate
      viewport.graphStatesField.graphIndex.deltaY := yTranslate
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, TRANSLATE2D)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          sendSF(VIEW, xTranslate)
          sendSF(VIEW, yTranslate)
          getI(VIEW)          -- acknowledge

    scale(viewport, graphIndex, xScaleF, yScaleF) ==
      xScale := convert(xScaleF)@SF
      yScale := convert(yScaleF)@SF
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      viewport.graphStatesField.graphIndex.scaleX := xScale  -- check union (undefined?)
      viewport.graphStatesField.graphIndex.scaleY := yScale  -- check union (undefined?)
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, SCALE2D)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          sendSF(VIEW, xScale)
          sendSF(VIEW, yScale)
          getI(VIEW)          -- acknowledge

    viewport2D ==
      [0,new(maxGRAPHS,"undefined"), _
       new(maxGRAPHS,copy defaultGS),"FriCAS2D", _
        [viewPosDefault().1, viewPosDefault().2], [viewSizeDefault().1, viewSizeDefault().2], _
         [noControl], [] ]

    makeViewport2D0 : % -> %

    makeViewport2D(g : G, opts : L DROP) ==
      viewport               := viewport2D()
      viewport.graphsField.1 := g
      viewport.optionsField := opts
      makeViewport2D0 viewport

    makeViewport2D viewport == makeViewport2D0 copy(viewport::Rep)

    send_graphs(vg : V GU) : Void ==
        for i in 1..#vg repeat
            (gu := vg(i)) case "undefined" => "iterate"
            g := gu::G
            if key(g) = 0 then sendGraphImage(g)

    makeViewport2D0 viewportDollar ==
      viewport := viewportDollar::Rep
      send_graphs(viewport.graphsField)
      doOptions viewport --local function to extract and assign optional arguments for 2D viewports
      say("   FriCAS2D data being transmitted to the viewport manager..."
         )$DisplayPackage
      sendI(VIEW, typeVIEW2D)
      sendI(VIEW, makeVIEW2D)
      sendSTR(VIEW, viewport.title)
      sendI(VIEW, viewport.moveTo.X)
      sendI(VIEW, viewport.moveTo.Y)
      sendI(VIEW, viewport.size.X)
      sendI(VIEW, viewport.size.Y)
      sendI(VIEW, viewport.flags.showCP)
      for i in 1..maxGRAPHS repeat
        g := (graphs viewport).i
        if g case "undefined" then
          sendI(VIEW, 0$I)
        else
          sendI(VIEW, key(g::G))
          gs := (graphStates viewport).i
          sendSF(VIEW, gs.scaleX)
          sendSF(VIEW, gs.scaleY)
          sendSF(VIEW, gs.deltaX)
          sendSF(VIEW, gs.deltaY)
          sendI(VIEW, gs.points)
          sendI(VIEW, gs.connect)
          sendI(VIEW, gs.spline)
          sendI(VIEW, gs.axes)
          hueShade := hue hue gs.axesColor + shade gs.axesColor * numberOfHues()
          sendI(VIEW, hueShade)
          sendI(VIEW, gs.units)
          hueShade := hue hue gs.unitsColor + shade gs.unitsColor * numberOfHues()
          sendI(VIEW, hueShade)
          sendI(VIEW, gs.showing)
      viewport.key := getI(VIEW)
      viewport

    graphState(viewport, num, sX, sY, dX, dY, Points, Lines, Spline, _
               Axes, AxesColor, Units, UnitsColor, Showing) ==
      viewport.graphStatesField.num := [sX, sY, dX, dY, Points, Lines, Spline, _
                                        Axes, AxesColor, Units, UnitsColor, Showing]

    title(viewport, Title) ==
      viewport.title := Title
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, TITLE)
        checkViewport viewport =>
          sendSTR(VIEW, Title)
          getI(VIEW)          -- acknowledge

    reset viewport ==
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, SPADBUTTONPRESS)
        checkViewport viewport =>
          sendI(VIEW, reset2D)
          getI(VIEW)          -- acknowledge
      void()

    axes (viewport : %, graphIndex : PI, onOff : STR) : Void ==
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      if onOff = "on" then
        status := yes
      else
        status := no
      viewport.graphStatesField.graphIndex.axes := status  -- check union (undefined?)
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, axesOnOff2D)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          sendI(VIEW, status)
          getI(VIEW)          -- acknowledge

    axes (viewport : %, graphIndex : PI, color : PAL) : Void ==
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      viewport.graphStatesField.graphIndex.axesColor := color
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, axesColor2D)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          hueShade := hue hue color + shade color * numberOfHues()
          sendI(VIEW, hueShade)
          getI(VIEW)          -- acknowledge

    units (viewport : %, graphIndex : PI, onOff : STR) : Void ==
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      if onOff = "on" then
        status := yes
      else
        status := no
      viewport.graphStatesField.graphIndex.units := status  -- check union (undefined?)
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, unitsOnOff2D)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          sendI(VIEW, status)
          getI(VIEW)          -- acknowledge

    units (viewport : %, graphIndex : PI, color : PAL) : Void ==
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      viewport.graphStatesField.graphIndex.unitsColor := color
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, unitsColor2D)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          hueShade := hue hue color + shade color * numberOfHues()
          sendI(VIEW, hueShade)
          getI(VIEW)          -- acknowledge

    connect (viewport : %, graphIndex : PI, onOff : STR) : Void ==
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      if onOff = "on" then
        status := 1$I
      else
        status := 0$I
      viewport.graphStatesField.graphIndex.connect := status  -- check union (undefined?)
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, connectOnOff)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          sendI(VIEW, status)
          getI(VIEW)          -- acknowledge

    points (viewport : %, graphIndex : PI, onOff : STR) : Void ==
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      if onOff = "on" then
        status := 1$I
      else
        status := 0$I
      viewport.graphStatesField.graphIndex.points := status  -- check union (undefined?)
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, pointsOnOff)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          sendI(VIEW, status)
          getI(VIEW)          -- acknowledge

    region (viewport : %, graphIndex : PI, onOff : STR) : Void ==
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      if onOff = "on" then
        status := 1$I
      else
        status := 0$I
      viewport.graphStatesField.graphIndex.spline := status  -- check union (undefined?)
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, spline2D)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          sendI(VIEW, status)
          getI(VIEW)          -- acknowledge

    show (viewport, graphIndex, onOff) ==
      if (graphIndex > maxGRAPHS) then
        error "Referring to a graph with too big an index"
      if onOff = "on" then
        status := 1$I
      else
        status := 0$I
      viewport.graphStatesField.graphIndex.showing := status  -- check union (undefined?)
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, showing2D)
        checkViewport viewport =>
          sendI(VIEW, graphIndex)
          sendI(VIEW, status)
          getI(VIEW)          -- acknowledge

    controlPanel (viewport, onOff) ==
      if onOff = "on" then viewport.flags.showCP := yes
      else viewport.flags.showCP := no
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, hideControl2D)
        checkViewport viewport =>
          sendI(VIEW, viewport.flags.showCP)
          getI(VIEW)          -- acknowledge

    close viewport ==
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, closeAll2D)
        checkViewport viewport =>
          getI(VIEW)          -- acknowledge
          viewport.key := 0$I
      void()

    coerce viewport ==
      (key(viewport) = 0$I) =>
        hconcat [message("Closed or Undefined TwoDimensionalViewport: "),
                  (viewport.title)::E]
      hconcat [message("TwoDimensionalViewport: "), (viewport.title)::E]

    write(viewport : %, Filename : STR, aThingToWrite : STR) ==
      write(viewport, Filename, [aThingToWrite])

    write(viewport, Filename) ==
      write(viewport, Filename, viewWriteDefault())

    write(viewport : %, Filename : STR, thingsToWrite : L STR) ==
      stringToSend : STR := ""
      (key(viewport) ~= 0$I) =>
        sendI(VIEW, typeVIEW2D)
        sendI(VIEW, writeView)
        checkViewport viewport =>
          sendSTR(VIEW, Filename)
          m := minIndex(avail := viewWriteAvailable())
          for aTypeOfFile in thingsToWrite repeat
            if (writeTypeInt := position(upperCase aTypeOfFile, avail)-m) < 0 then
              say(["  > ", aTypeOfFile,
                " is not a valid file type for writing a 2D viewport"
                          ])$DisplayPackage
            else
              sendI(VIEW, writeTypeInt + 1$I)
                  --              stringToSend := concat [stringToSend,"%",aTypeOfFile]
                  --              sendSTR(VIEW, stringToSend)
          sendI(VIEW, 0$I)     -- no more types of things to write
          getI(VIEW)          -- acknowledge
          Filename


--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
