Combining Plots in a Natural Manner
- To: mathgroup at smc.vnet.net
- Subject: [mg29786] Combining Plots in a Natural Manner
- From: "David Park" <djmp at earthlink.net>
- Date: Tue, 10 Jul 2001 20:25:26 -0400 (EDT)
- Sender: owner-wri-mathgroup at wolfram.com
Dear MathGroup, I have, perhaps too often, mentioned my DrawingPaper package on MathGroup. Many people, naturally enough, are reluctant to depend too heavily on third party, and non WRI Mathematica software. However, there are two simple routines which easily implement the core idea. I thought I would present and illustrate these routines because they provide an easy alternative to the standard methods of combining plots, and can be used without DrawingPaper. The core idea is to reduce plots to primitive graphics, which can then be easily intermixed with other graphics primitives and directives in one statement. I will present the routines and two examples first, and then give some discussion. Needs["Graphics`Colors`"] Needs["Graphics`ImplicitPlot`"] Needs["Graphics`FilledPlot`"] Needs["Graphics`Graphics`"] The first routine serves to extract the primitive graphics and is so simple I am almost embarrassed to present it. Attributes[DrawGraphics] = {HoldAll}; DrawGraphics[plot_] := Block[{$DisplayFunction = Identity}, First[Graphics[plot]]] The second routine is equally trivial but serves to get rid of one level of unnecessary complexity and confusion. Draw2D[primitives_List, opts___?OptionQ] := Show[Graphics[primitives], opts] But look how we can use them. Here I combine a ContourPlot, a Disk, a Circle, a DensityPlot, a regular Plot, a ParametricPlot, an ImplicitPlot, the points Mathematica used in generating the ImplicitPlot, some Text and color directives, all in one statement, which contains only a list of the graphical elements and the overall plot options. We just wrap DrawGraphics around each of the plot statements. Draw2D[ {ContourPlot[Sin[x y], {x, -Pi/2, Pi/2}, {y, -1, 1}] // DrawGraphics, Orchid, Disk[{0, 0}, Sqrt[2]/4], Black, Circle[{0, 0}, Sqrt[2]/4], DensityPlot[Sin[x y], {x, -1/4, 1/4}, {y, -1/4, 1/4}, ColorFunction -> Hue, PlotPoints -> 8] // DrawGraphics, Peacock, Plot[Sin[x], {x, -Pi/2, Pi/2}] // DrawGraphics, Blue, ParametricPlot[{Cos[t], Sin[t]}, {t, 0, 2Pi}] // DrawGraphics, Red, ellipse = ImplicitPlot[x^2 + 3y^2 == 1, {x, -1, 1}] // DrawGraphics, LightBlue, PointSize[0.01], ellipse /. Line[pts_] :> Point /@ pts, Gold, Text["Draw It!", Scaled[{0.95, 0.1}], {1, -1}]}, Frame -> True, AspectRatio -> Automatic, Background -> Linen, ImageSize -> 500, PlotLabel -> "Combining Plots, Graphics Primitives\n\t and Graphics Directives"]; It is very useful to have the primitive graphics from a plot because then we can do further manipulations on them. In the example above, we could immediately convert a line from one of the plots into points. In the second example below, I color the area inside a cardioid but outside a unit circle. (I imagine there is also a way to do this with InequalityPlot.) It is difficult to use FilledPlot directly because of the shape of the region. But we can use FilledPlot in the theta-r plane and then transform to the xy plane. So here I draw the filled area and the polar plots for the cardioid and circle in one statement. Draw2D[ {(FilledPlot[{1, 1 + Cos[t]}, {t, -Pi/2, Pi/2}, Fills -> LightSteelBlue, Curves -> None, PlotPoints -> 31] // DrawGraphics) /. {t_?NumberQ, r_?NumberQ} -> {r Cos[t], r Sin[t]}, PolarPlot[1 + Cos[t], {t, 0, 2Pi}, PlotPoints -> 31] // DrawGraphics, PolarPlot[1, {t, 0, 2Pi}] // DrawGraphics}, AspectRatio -> Automatic, Background -> Linen, ImageSize -> 500, PlotLabel -> "Primitive Graphics Can\n Be Easily Transformed"]; The advantages of this approach are: 1) There is no need to produce side plots, or to turn the display on and off. 2) All graphical elements are on the same level, whether they are ordinary graphics primitives or primitives generated by some plot command. 3) Graphical elements can be drawn one after the other, and mixed with graphics directives, just in the order you wish. (And in 2D graphics, the order matters.) This, after all, is the natural way that one would draw a picture. 4) Graphical elements are immediately available for manipulation. 5) The overall plot options, such as Axes, Frame, FrameTicks, PlotLabel, etc., are specified in the Draw2D (or Show) statement itself, where they logically belong, and not in some plot command which produced only one element of the overall graphics. 6) It is not necessary to use Prolog or Epilog, or wonder, and be surprised by which options are actually picked up when you use the regular Mathematica paradigm for combining plots in a Show statement or with DisplayTogether. (Show and DisplayTogether pick up options only from the first plot listed. So Epilog items which might naturally be associated with other plots must be shifted to the first plot or moved to the Show or DisplayTogether statement. That is not only not the natural way of doing things, but it loses precise ordering control for laying down all the elements.) By adhering to the paradigm that "Everything is an Expression" the designers of Mathematica made a system which is both powerful and easy to use. While plots are also expressions, they are not easy to use. For graphical programming, I would have liked to have seen the subordinate paradigm, "Everything is Primitive Graphics", implemented. The designers, rather, went for set-piece plots, and then seem to have worked out methods of combining them as an afterthought. The DrawGraphics approach goes a long way toward implementing the "Everything is Primitive Graphics" paradigm. David Park djmp at earthlink.net http://home.earthlink.net/~djmp/