home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / e / e032 / 3.ddi / FILES / GRAPHICS.PAK / LEGEND.M < prev    next >
Encoding:
Text File  |  1992-07-29  |  12.8 KB  |  361 lines

  1.  
  2. (* :Title: Graphics Legends *)
  3.  
  4. (* :Author: John M. Novak *)
  5.  
  6. (* :Summary: A package for placing a legend box on a graphic.
  7.     Includes numerous options to assist in making it just
  8.     right. *)
  9.  
  10. (* :Context: Graphics`Legends` *)
  11.  
  12. (* :Package Version: 1.0 *)
  13.  
  14. (* :History: Version 1.0 by John M. Novak, Feb. 91 *)
  15.  
  16. (* :Keywords: graphics, legends, key *)
  17.  
  18. (* :Mathematica Version: 2.0 *)
  19.  
  20. (* :Warning: adds rules to Plot[]. *)
  21.  
  22. (* :Warning: Uses the package Utilities`FilterOptions`. *)
  23.  
  24. (* :Limitation: Does not yet deal with scaled coordinates. *)
  25.  
  26. (* :Limitation: Automatic placing of legend boxes is not very
  27.     good; tweaking by hand is likely to be required (definitely
  28.     if there is more than one box being placed). *)
  29.  
  30. (* :Limitation: Graphics options affect the entire graphic (with
  31.     legend box emplaced). Because the boxes generally contain
  32.     graphics in rectangles, if AspectRatio is changed, unexpected
  33.     changes may occur.  This applies even more so to Legend (as
  34.     opposed to ShowLegend). *)
  35.  
  36. (* :Limitation: Error checking is somewhat limited at this time. *)
  37.  
  38. BeginPackage["Graphics`Legend`","Utilities`FilterOptions`"]
  39.  
  40. (* Usage Messages *)
  41.  
  42. ShowLegend::usage =
  43.     "ShowLegend[graphic,{legendargs}...] creates and shows a graphic
  44.     with legends (keys).  legendargs are in the same format as
  45.     a call to Legend would be (see: Legend). This routine attempts
  46.     to place the graphic in a coordinate system in which the
  47.     center of the graphic is at {0,0}, and the graphic fits
  48.     inside of a box {{-1,-1},{1,1}}. It retains its original
  49.     aspect ratio, so if this routine cannot find that aspect
  50.     ratio, the box may be oversized (requiring possible adjustments
  51.     to the PlotRange).  The position for each legend should be
  52.     determined according to this coordinate system. The routine
  53.     will attempt to place the first legend in the lower left
  54.     corner if no position is explicitly stated; all others will
  55.     be placed in the default position specified in Legend.  Be careful to
  56.     count braces with this function! You may need three in a row
  57.     for some arguments."
  58.  
  59. Legend::usage =
  60.     "There are two forms with which Legend may be called.
  61.     Legend[function,num,(mintext),(maxtext),opts] will apply
  62.     the function to numbers between 0 and 1, incremented by
  63.     the number of boxes num - 1.  This will generate a color
  64.     directive or graphic suitable for a box in the legend.
  65.     Legend[{{box,text},...},opts] is the simpler usage; it
  66.     creates the legend with the specified boxes and text.  Boxes
  67.     can be color directives (e.g., Hue) or graphics.  Text can
  68.     be a string or have FontForm[] wrapped around it.  Note:
  69.     options for ShadowBox will also be accepted and passed on
  70.     to ShadowBox."
  71.  
  72. PlotLegend::usage = 
  73. "PlotLegend is an option for Plot, which assigns text to lines in a 2D
  74. plot to create a legend for that plot. PlotLegend->{txt1,txt2...}
  75. assigns text to each line in the fashion of PlotStyle.  PlotLegend
  76. also enables Plot to accept options for Legend, which will modify the
  77. legend produced."
  78.  
  79. ShadowBox::usage =
  80. "ShadowBox[pos,size,opts] creates a box with a drop shadow,
  81. with colors specified by opts.  It is generated at position
  82. pos ({x,y}) with size specified by {x-length, y-length}."
  83.  
  84. LegendPosition::usage =
  85. "LegendPosition is an option for Legend, which specifies the exact
  86. location of a legend box (the lower-left corner).  If called from
  87. ShowLegend, the position will be in the coordinate system, with the
  88. graphic centered at {0,0} and scaled to fit inside {{-1,-1},{1,1}}."
  89.  
  90. LegendSize::usage =
  91. "LegendSize specifies the size of a Legend box.  
  92. LegendSize->Automatic means that a routine determines the size.
  93. LegendSize-Number will scale the size so that it fits in a box of
  94. width the longest side of the length given.  LegendSize->{number,
  95. number} uses {x-length, y-length} to determine size."
  96.  
  97. LegendShadow::usage =
  98. "LegendShadow is an option for Legend.  LegendShadow->None specifies
  99. no shadow and a transparent backdrop to the legend box.
  100. LegendShadow->Automatic specifies that the shadow is placed by
  101. routine.  LegendShadow->{x-offset, y-offset} specifies offset of
  102. shadow from the box."
  103.  
  104. LegendTextSpace::usage =
  105. "LegendTextSpace specifies the space in the legend box for text.
  106. Arguments can be a number corresponding to the ratio of the text space
  107. to the size of a key box, or Automatic."
  108.  
  109. LegendTextDirection::usage =
  110. "LegendTextDirection is an option for Legend, which specifies the
  111. direction of text next to the key box.  Argument can be standard
  112. Text[], number pair, or Automatic."
  113.  
  114. LegendTextOffset::usage =
  115. "LegendTextOffset is an option for Legend, which specifies the offset
  116. of text next to the key box.  Arguments can be standard Text[], number
  117. pair, or Automatic."
  118.  
  119. LegendLabel::usage =
  120. "LegendLabel is an option for Legend, which specifies the text to be
  121. used as the label for the legend box.  Arguments can be String,
  122. FontForm, or None."
  123.  
  124. LegendLabelSpace::usage =
  125. "LegendLabelSpace is an option for Legend, which sets the space for
  126. LegendLabel.  It can be expressed as a ratio of a keybox size (see
  127. LegendTextSpace) or be Automatic."
  128.  
  129. LegendOrientation::usage =
  130. "LegendOrientation is an option for Legend, which specifies the
  131. direction in which key boxes are laid out.  Settings can be Horizontal
  132. (left to right) or Vertical (top to bottom)."
  133.  
  134. LegendSpacing::usage =
  135. "LegendSpacing is an option for Legend, which can be set to a number or
  136. Automatic.  Determines the amount of space around each key box on a
  137. scale where the box is 1.  For the boxes to be adjacent (a continuous
  138. line for instance) LegendSpacing should be set to 0."
  139.  
  140. LegendBorderSpace::usage =
  141. "LegendBorderSpace is an option for Legend which sets the space around
  142. the entire set of key boxes and text in the legend."
  143.  
  144. LegendBorder::usage =
  145. "LegendBorder is an option for Legend, which specifies the style of the
  146. line surrounding key boxes and text in a legend."
  147.  
  148. LegendBackground::usage =
  149. "LegendBackground is an option for Legend, which specifies the style of
  150. background to use with a legend.  LegendBackground sets
  151. ShadowForeground and will override any value passed to
  152. ShadowForeground."
  153.  
  154. ShadowBorder::usage =
  155. "ShadowBorder is an option for ShadowBox or Legend, which sets a border
  156. around the rectangle above a shadow.  Styles are same as those for a
  157. Line[] primitive."
  158.  
  159. ShadowForeground::usage =
  160. "ShadowForeground is an option for ShadowBox or Legend, which
  161. specifies a style for the foreground of a shadow box.  If used from
  162. Legend, this will be the style behind the keys/text.  The style should
  163. be a color primitive."
  164.  
  165. ShadowBackground::usage =
  166. "ShadowBackground is an option for Shadowbox or Legend, which sets the
  167. style for a drop shadow.  The style should be a color primitive.  The
  168. default is GrayLevel[0]."
  169.  
  170. ShadowOffset::usage =
  171. "ShadowOffset is an option for ShadowBox or Legend, which sets the
  172. Offset of a shadow from the rest of the box.  It is expresses as
  173. {x-distance,y-distance}.  When using from Legend, set ShadowOffset to
  174. {0,0} to get a background with no shadow."
  175.  
  176. Horizontal::usage =
  177.     "Value for option LegendOrientation."
  178.  
  179. Vertical::usage =
  180.     "Value for option LegendOrientation."
  181.  
  182. Begin["`Private`"]
  183.  
  184. Options[ShadowBox] =
  185.     {ShadowBorder->{Thickness[.001],GrayLevel[0]},
  186.     ShadowForeground->GrayLevel[1],
  187.     ShadowBackground->GrayLevel[0],
  188.     ShadowOffset->{.1,-.1}};
  189.  
  190. ShadowBox[pos:{_,_},size:{_,_},opts___] :=
  191.     Module[{bordsty,foresty,backsty,offset,forebox,
  192.                 backbox,border},
  193.         {bordsty,foresty,backsty,offset} =
  194.             {ShadowBorder,ShadowForeground,ShadowBackground,ShadowOffset}/.
  195.             {opts}/.Options[ShadowBox];
  196.         If[foresty === Automatic, foresty = GrayLevel[1]];
  197.         If[bordsty === Automatic, bordsty = {Thickness[.001],
  198.                     GrayLevel[0]}];
  199.         If[backsty === Automatic, backsty = GrayLevel[0]];
  200.         forebox = Rectangle[pos,pos + size];
  201.         backbox = Rectangle[pos + offset,pos + size + offset];
  202.         border = Line[{pos,pos + {First[size],0},
  203.             pos + size,pos + {0,Last[size]},pos}];
  204.         Flatten[{backsty,backbox,foresty,forebox,
  205.                     bordsty,border}]]
  206.  
  207. Options[Legend] =
  208.     {LegendPosition->{-1,-1},LegendSize->Automatic,
  209.     LegendShadow->Automatic,LegendTextSpace->Automatic,
  210.     LegendTextDirection->Automatic,LegendTextOffset->Automatic,
  211.     LegendLabel->None,LegendLabelSpace->Automatic,
  212.     LegendOrientation->Vertical, LegendSpacing->Automatic,
  213.     LegendBorder->Automatic,
  214.     LegendBorderSpace->Automatic,LegendBackground->Automatic};
  215.  
  216. Legend[fn:(_Function | _Symbol),boxes_?NumberQ,
  217.         minstr_String:"",maxstr_String:"",opts___] :=
  218.     Module[{its,strs},
  219.         its = Map[fn,Range[0,1,1/(boxes - 1)]];
  220.         strs = Table["",{Length[its]}];
  221.         strs[[1]] = minstr;strs[[Length[strs]]] = maxstr;
  222.         Legend[Transpose[{its,strs}],opts,
  223.             LegendSpacing->0]]
  224.  
  225. Legend[items:{{_,_}..},opts___] :=
  226.     Module[{ln = Length[items],boxes,lb,n,inc,rn,as,gr,sbox,
  227.             pos,size,shadow,tspace,lspace,bspace,tdir,toff,
  228.             label,orient,space,back,bord},
  229.         {pos,size,shadow,tspace,tdir,label,lspace,
  230.             orient,space,bspace,toff,back,bord} =
  231.             {LegendPosition,LegendSize,LegendShadow,
  232.             LegendTextSpace,LegendTextDirection,LegendLabel,
  233.             LegendLabelSpace,LegendOrientation,
  234.             LegendSpacing,LegendBorderSpace,
  235.             LegendTextOffset,LegendBackground,
  236.             LegendBorder}/.{opts}/.Options[Legend];
  237.         If[Not[NumberQ[space]], inc = .08,inc = space];
  238.         If[tspace === Automatic,
  239.             If[Count[Transpose[items][[2]],""] == ln,
  240.                 tspace = 0,
  241.                 If[orient === Vertical,
  242.                     tspace = 2,
  243.                     tspace = 1]]];
  244.         If[lspace === Automatic,
  245.             If[(label =!= None) && (label =!= ""),
  246.                 lspace = 1,
  247.                 lspace = 0]];
  248.         If[bspace === Automatic,
  249.             bspace = .1];
  250.         If[toff === Automatic,
  251.             If[orient === Vertical,toff = {-1,0},
  252.                 toff = {0,-1}]];
  253.         If[tdir === Automatic,
  254.             tdir = {1,0}];
  255.         boxes =
  256.             If[orient === Vertical,
  257.             Table[pt = {inc,inc (2 n - 1) + (n - 1)};
  258.                 {rec[pt,{1,1},items[[ln - n + 1,1]]],
  259.                 Text[items[[ln - n + 1,2]],
  260.                     pt + {1 + inc + .05,1/2},toff,tdir]},
  261.                 {n,ln}],
  262.             Table[pt = {inc (2 n - 1) + (n - 1),inc};
  263.                 {rec[pt,{1,1},items[[n,1]]],
  264.                 Text[items[[n,2]],
  265.                     pt + {1/2, 1 + inc},toff,tdir]},
  266.                 {n,ln}]];
  267.         lb = If[lspace != 0,
  268.             Text[label,
  269.                 If[orient === Vertical,
  270.                     {(2 inc + 1 + tspace)/2,
  271.                         (2 inc + 1) ln + lspace/2},
  272.                     {(2 inc + 1) ln /2,
  273.                         2 inc + 1 + tspace + lspace/2}],
  274.                 {0,0}],
  275.             {}];
  276.         rn = If[orient === Vertical,
  277.             {{-bspace,2 inc + 1 + tspace + bspace},
  278.                 {-bspace,(2 inc + 1) ln + lspace + bspace}},
  279.             {{-bspace,(2 inc + 1) ln + bspace},
  280.                 {-bspace,2 inc + 1 + tspace + lspace + bspace}}];
  281.         If[Not[Head[size] === List],
  282.             If[Not[NumberQ[size]], size = .8];
  283.             tmp = Map[#[[2]] - #[[1]] &,rn];
  284.             size = tmp (size/Max[tmp])];
  285.         as = size[[2]]/size[[1]];
  286.         gr = Graphics[{boxes,lb},AspectRatio->as,PlotRange->rn];
  287.         If[shadow =!= None,
  288.             If[shadow === Automatic, shadow = {.05,-.05}];
  289.             sbox = ShadowBox[pos,size,ShadowForeground->back,
  290.                 ShadowBorder->bord,ShadowOffset->shadow,opts],
  291.             sbox = {}];
  292.         Flatten[{sbox,Rectangle[pos,pos + size,gr]}]]
  293.             
  294.  
  295. rec[start:{_,_},size:{_,_},style_] :=
  296.     Module[{nrec},
  297.         nrec = Rectangle[start, start + size];
  298.         If[MemberQ[{RGBColor,Hue,CMYKColor,GrayLevel},
  299.                 Head[style]],
  300.             {style,nrec},
  301.             Append[nrec,style]]]
  302.  
  303.  
  304. ShowLegend[agr_,largs:({__}..),opts:(_Rule | _RuleDelayed)...] :=
  305.     Module[{as,ls={largs},rec,ap},
  306.         as = FullOptions[agr,AspectRatio];
  307.         bk = FullOptions[agr,Background];
  308.         If[!NumberQ[as], as = 1];
  309.         If[as > 1,
  310.             rec = Rectangle[{-1/as,-1},{1/as,1},agr];
  311.                 ap = {-1/as - .2,-1.2},
  312.             rec = Rectangle[{-1,-as},{1,as},agr];
  313.                 ap = {-1.2,-as - .2}];
  314.         ls = MapAt[Append[#,LegendPosition->ap]&,ls,1];
  315.         ls = Apply[Legend,ls,{1}];
  316.         Show[Graphics[{rec,ls},FilterOptions[Graphics,opts],
  317.             Background->bk,AspectRatio->Automatic,PlotRange->All]]]
  318.  
  319.  
  320. Unprotect[Plot];
  321.  
  322. Plot/: Plot[fn_,r_,o1___,PlotLegend->None,o2___] :=
  323.     Plot[fn,r,Evaluate[FilterOptions[Plot,o1,o2]]]
  324.  
  325. Plot/: Plot[fn_,r_,o1___,PlotLegend->lg_,o2___] :=
  326.     Module[{txt = lg,sopts,gopts,lopts,ps,disp,ln,gr,tb},
  327.         gopts = FilterOptions[Plot,o1,o2];
  328.         sopts = FilterOptions[ShadowBox,o1,o2];
  329.         lopts = FilterOptions[Legend,o1,o2];
  330.         {ps,disp} = {PlotStyle,DisplayFunction}/.{gopts}/.
  331.                 Options[Plot];
  332.  
  333.         ln = If[Head[fn] === List, Length[fn],1];
  334.  
  335.         If[Head[txt] =!= List, txt = {txt},
  336.         If[Length[txt] == 0, txt = {""}]];
  337.         While[Length[txt] < ln,txt = Join[txt,txt]];
  338.         txt = Take[txt,ln];
  339.             
  340.         If[ps === Automatic,ps = {}];
  341.         If[Head[ps] =!= List, ps = {ps},
  342.         If[Length[ps] == 0, ps = {{}}]];
  343.         While[Length[ps] < ln,ps = Join[ps,ps]];
  344.         ps = Take[ps,ln];
  345.         ps = ps/.Dashing[x_] -> Dashing[2/.3 x]; (* scale dashes *)
  346.  
  347.         tb = Table[{Graphics[Flatten[{ps[[n]],
  348.             Line[{{0,0},{1,0}}]}]],txt[[n]]},{n,ln}];
  349.  
  350.         gr = Insert[
  351.             Plot[fn,r,DisplayFunction->Identity,Evaluate[gopts]],
  352.             DisplayFunction->disp,2];
  353.  
  354.         ShowLegend[gr,{tb,sopts,lopts}]]
  355.  
  356. Protect[Plot];
  357.  
  358. End[]
  359.  
  360. EndPackage[]
  361.