How to annotate multiple datasets in ListPlots - wolfram-mathematica

Frequently I have to visualize multiple datasets simultaneously, usually in ListPlot or its Log-companions. Since the number of datasets is usually larger than the number of easily distinguishable line styles and creating large plot legends is still somewhat unintuitiv I am still searching for a good way to annotate the different lines/sets in my plots. Tooltip is nice when working on screen, but they don't help if I need to pritn the plot.
Recently, I played around with the Mesh option to enumerate my datasets and found some weird stuff
GraphicsGrid[Partition[Table[ListPlot[
Transpose#
Table[{Sin[x], Cos[x], Tan[x], Cot[x]}, {x, 0.01, 10, 0.1}],
PlotMarkers -> {"1", "2", "3", "4"}, Mesh -> i, Joined -> True,
PlotLabel -> "Mesh\[Rule]" <> ToString[i], ImageSize -> 180], {i,
1, 30}], 4]]
The result looks like this on my machine (Windows 7 x64, Mathematica 8.0.1):
Funnily, for Mesh->2, 8 , and 10 the result looks like I expected it, the rest does not. Either I don't understand the Mesh option, or it doesn't understand me.
Here are my questions:
is Mesh in ListPLot bugged or do I use it wrongly?
how could I x-shift the mesh points of successive sets to avoid overprinting?
do you have any other suggestions how to annotate/enumerate multiple datasets in a plot?

You could try something along these lines. Make each line into a button which, when clicked, identifies itself.
plot=Plot[{Sin[x],Cos[x]},{x,0,2*Pi}];
sinline=plot[[1,1,3,2]];
cosline=plot[[1,1,4,2]];
message="";
altplot=Append[plot,PlotLabel->Dynamic[message]];
altplot[[1,1,3,2]]=Button[sinline,message="Clicked on the Sin line"];
altplot[[1,1,4,2]]=Button[cosline,message="Clicked on the Cos line"];
altplot
If you add an EventHandler you can get the location where you clicked and add an Inset with the relevant positioned label to the plot. Wrap the plot in a Dynamic so it updates itself after each button click. It works fine.
In response to comments, here is a fuller version:
plot = Plot[{Sin[x], Cos[x]}, {x, 0, 2*Pi}];
sinline = plot[[1, 1, 3, 2]];
cosline = plot[[1, 1, 4, 2]];
AddLabel[label_] := (AppendTo[plot[[1]],
Inset[Framed[label, Background -> White], pt]];
(* Remove buttons for final plot *)
plainplot = plot;
plainplot[[1, 1, 3, 2]] = plainplot[[1, 1, 3, 2, 1]];
plainplot[[1, 1, 4, 2]] = plainplot[[1, 1, 4, 2, 1]]);
plot[[1, 1, 3, 2]] = Button[sinline, AddLabel["Sin"]];
plot[[1, 1, 4, 2]] = Button[cosline, AddLabel["Cos"]];
Dynamic[EventHandler[plot,
"MouseDown" :> (pt = MousePosition["Graphics"])]]
To add a label click on the line. The final annotated chart, set to 'plainplot', is printable and copyable, and contains no dynamic elements.
[Later in the day] Another version, this time generic, and based on the initial chart. (With parts of Mark McClure's solution used.) For different plots 'ff' and 'spec' can be edited as desired.
ff = {Sin, Cos, Tan, Cot};
spec = Range[0.1, 10, 0.1];
(* Plot functions separately to obtain line counts *)
plots = Array[ListLinePlot[ff[[#]] /# spec] &, Length#ff];
plots = DeleteCases[plots, Line[_?(Length[#] < 3 &)], Infinity];
numlines = Array[Length#Cases[plots[[#]], Line[_], Infinity] &,
Length#ff];
(* Plot functions together for annotation plot *)
plot = ListLinePlot[##spec & /# ff];
plot = DeleteCases[plot, Line[_?(Length[#] < 3 &)], Infinity];
lbl = Flatten#Array[ConstantArray[ToString#ff[[#]],
numlines[[#]]] &, Length#ff];
(* Line positions to substitute with buttons *)
linepos = Position[plot, Line, Infinity];
Clear[line];
(* Copy all the lines to line[n] *)
Array[(line[#] = plot[[Sequence ## Most#linepos[[#]]]]) &,
Total#numlines];
(* Button function *)
AddLabel[label_] := (AppendTo[plot[[1]],
Inset[Framed[label, Background -> White], pt]];
(* Remove buttons for final plain plot *)
plainplot = plot;
bpos = Position[plainplot, Button, Infinity];
Array[(plainplot[[Sequence ## Most#bpos[[#]]]] =
plainplot[[Sequence ## Append[Most#bpos[[#]], 1]]]) &,
Length#bpos]);
(* Substitute all the lines with line buttons *)
Array[(plot[[Sequence ## Most#linepos[[#]]]] = Button[line[#],
AddLabel[lbl[[#]]]]) &, Total#numlines];
Dynamic[EventHandler[plot,
"MouseDown" :> (pt = MousePosition["Graphics"])]]
Here's how it looks. After annotation the plain graphics object can be found set to the 'plainplot' variable.

One approach is to generate the plots separately and then show them together. This yields code that is more like yours than the other post, since PlotMarkers seems to play the way we expect when dealing with one data set. We can get the same coloring using ColorData with PlotStyle. Here's the result:
ff = {Sin, Cos, Tan, Cot};
plots = Table[ListLinePlot[ff[[i]] /# Range[0.1, 10, 0.1],
PlotStyle -> {ColorData[1, i]},
PlotMarkers -> i, Mesh -> 22], {i, 1, Length[ff]}];
(* Delete the spurious asymptote looking thingies. *)
plots = DeleteCases[plots, Line[ll_?(Length[#] < 4 &)], Infinity];
Show[plots, PlotRange -> {-4, 4}]

Are you going to be plotting computable curves or actual data?
If it's computable curves, then it's common to use a plot legend (key).
You can use different dashings and thicknesses to differentiate between the lines on a grayscale printer. There are many examples in the PlotLegends documentation.
If it's real data, then normally the data is sparse enough that you can use PlotMarkers for the actual data points (i.e. don't specify Mesh). You can use automatic PlotMarkers, or you can use custom PlotMarkers including BoxWhisker markers to indicate the various uncertainties.

Related

Mathematica: Extract Co-ordinates of a Plot for which I do not know the Equation of

GIven a 2D plot in Mathematica, if you keep clicking the graph, a sequence of co-ordinates of that graph are shown. I'd like to extract the x and y co-ordinates of ALL of these points, WITHOUT using the "Get Coordinates" tool (which only extracts one co-ordinate at a time, which is both inaccurate and laborious). An additional constraint is that the equation of the plot is UNKNOWN (I found a graph produced by Wolfram Alpha, the equation of which is unknown to me. If I can simply extract the co-ordinates, I can fit a spline through those co-ordinates, thereby getting the equation of the graph). Any ideas?
Cheers!
This is how it can be done in Mathematica 9.
First obtain the chart.
chart = WolframAlpha["density vs altitude of heterosphere",
{{"EntrainedDensityPlot:AtmosphericLayers", 1}, "Content"}]
Extract the data section. The x-axis is scaled according to the tick specification.
data = chart[[1, 1, 1, 1, 1, 1, 3, 2, 1]];
ListLinePlot[data, PlotRange -> All]
This is the content of the tick specification :-
ticksposition = Position[chart, Ticks];
ticks = Last#chart[[Sequence ## Most[First#ticksposition]]];
Take[First#ticks, 5][[All, 1]]
{-25.328436022934504, -18.420680743952367, -11.512925464970229`,
-4.605170185988091, 2.302585092994046}
The numbers above relate to the following tick labels :-
{10^-11, 10^-8, 10^-5, 0.01, 10};
The line data is show below. The x values can be rescaled according to the ticks.
data
{{7.56584506772668,-5.},{7.522454313212941,-4.5},{7.4785653196771396,-4.},{7.4342573821331355,-3.5},{7.38950218524746,-3.},{7.344266755495627,-2.5},{7.2985804103507865,-2.},{7.25233739856673,-1.5},{7.205635176410364,-1.},{7.158436173289435,-0.5},{7.110696122978827,0.},{7.062448668658617,0.5},{7.0136456542395695,1.},{6.964230125910116,1.5},{6.91433359434226,2.},{6.863751143484082,2.5},{6.812620083867098,3.},{6.760878083121377,3.5},{6.708511342992233,4.},{6.655491829094075,4.5},{6.601814187258075,5.},{6.547459502017843,5.5},{6.4924064877997925,6.},{6.436647039879506,6.5},{6.380156434630315,7.},{6.32290629486736,7.5},{6.264901893476659,8.},{6.206091938653852,8.5},{6.1464577290734805,9.},{6.086001700931971,9.5},{6.0246816979681785,10.},{5.962473333757384,10.5},{5.899349258200177,11.},{5.821358081393286,11.5},{5.7428108616236795,12.},{5.664279054878501,12.5},{5.585749407744609,13.},{5.507199708509977,13.5},{5.42873140526997,14.},{5.350245459408396,14.5},{5.2717680313145,15.},{5.114815113005919,16.},{4.957937505095806,17.},{4.801148069229532,18.},{4.6443908991413725,19.},{4.487624622133048,20.},{4.326976291408619,21.},{4.16682025054415,22.},{4.007442270191581,23.},{3.848827581930999,24.},{3.6909772521960824,25.},{3.533890923387621,26.},{3.3775192543075785,27.},{3.221911213411722,28.},{3.0670291554360247,29.},{2.9128939952449864,30.},{2.7595034826911258,31.},{2.606755482950629,32.},{2.4486747988659405,33.},{2.2912612192626023,34.},{2.1357509841344284,35.},{1.9820905307957144,36.},{1.680194560884901,38.},{1.3852187828929574,40.},{1.096877451374393,42.},{0.8148779691310925,44.},{0.5389464994826453,46.},{0.27512860638016096,48.},{0.02654455522211221,50.},{-0.21614311166946532,52.},{-0.44783517527478434,54.},{-0.6842865521277486,56.},{-0.9256594818782552,58.},{-1.1722157727127442,60.},{-1.8127175638195325,65.},{-2.490977037365282,70.},{-3.220852777752422,75.},{-3.992257398138752,80.},{-4.801233732898559,85.},{-4.884341907755072,85.5},{-4.967863202252387,86.},{-5.6792850030558135,90.},{-6.576295584184468,95.},{-7.486859743501422,100.},{-9.239975177105872,110.},{-10.71451777375279,120.},{-11.71724726204385,130.},{-12.472384692245763,140.},{-13.085067592660632,150.},{-13.606060333782066,160.},{-14.062050687084879,170.},{-14.470591537717763,180.},{-14.842453559942024,190.},{-15.185537946620293,200.},{-15.50507451487766,210.},{-15.805477093216508,220.},{-16.359148622816097,240.},{-16.864221756309153,260.},{-17.331782147471895,280.},{-17.7704410644037,300.},{-18.1863994482277,320.},{-18.582846794542757,340.},{-18.964546221796557,360.},{-19.333726745661632,380.},{-19.69257556476376,400.},{-20.554367300484596,450.},{-21.37431184148772,500.},{-22.157071180737354,550.},{-22.89745771517206,600.},{-23.585866797897218,650.},{-24.206758461335397,700.},{-24.74733834618318,750.},{-25.200922702635545,800.},{-25.573825183196032,850.},{-25.880257267404012,900.},{-26.137443089588984,950.},{-26.360979711632908,1000.}}
Recycling an answer from here, this function stores up mouse point clicks in a variable pts. You will need to combine your curve in the Show function, suitably scaled. Here I just put in a sine plot.
It uses a dynamic module so the points will still be there when you save, close and reopen your notebook.
CreateDistribution[] :=
DynamicModule[{savepts = {{-1, -1}}},
Dynamic[EventHandler[
Show[Plot[Sin[x], {x, 0, 7}],
ListPlot[pts, AxesOrigin -> {0, 0},
PlotRange -> {{0, 7}, {0, 5}}]],
"MouseDown" :> (savepts =
pts = DeleteCases[
Append[pts, MousePosition["Graphics"]], {-1, -1}])],
Initialization :> (pts = savepts)]]
CreateDistribution[]
pts
{{0.371185, 0.357737}, {0.859027, 0.779375}, {1.55898,
1.01471}, {2.36498, 0.661709}, {2.95887,
0.161626}, {3.55277, -0.358067}, {4.10424, -0.799316}, {4.91024, -0.985622}, {5.6314, -0.573789}, {6.20409, -0.142345}, {6.71314,
0.367543}}

Multipanels using Manipulate and variable dependence

Is it possible to change the values of the variables in Manipulate? Suppose I have a Manipulate with two variables, x and y and we display the values. What I want to do is to make it in such a way that when I change the value of x, y gets updated to x*x. When I change the value of y, then x gets updated to the square root of y.
The other question is, can I have multiple panels in Manipulate? I would like to have a white panel under each slider.
Manipulate[
Row[{x, y}, " "],
Row[{
Control[{{x, 0, Style["x", "TI", 14]}, 0, 4 , Appearance -> "Labeled"}],
Control[{{y, 0, Style["y", "TI", 14]}, 0, 16, Appearance -> "Labeled"}]
}]
]
In the above plot I have set x to 3 and y to 9. Again, I would like to move y to say 4 and have x to move to 2. Similarly, I want to move x to 4 and have y move to 16.
Is is possible? or have I just encountered the chicken or the egg problem?
A bit hack-ish but to couple the sliders and get two panels, you could also do something like this
Panel[DynamicModule[{x, y, width = 250},
Grid[{{
Labeled[Slider[Dynamic[x, (x = #; y = #^2) &], {0, 5}],
{Style["x", "TI", 14], Dynamic[x]}, {Left, Right}],
Labeled[Slider[Dynamic[y, (y = #; x = Sqrt[#]) &], {0, 25}],
{Style["y", "TI", 14], Dynamic[y]}, {Left, Right}]},
Framed[Pane[#, width, Alignment -> Center], FrameMargins -> 10,
Background -> White, FrameStyle -> {Gray}] & /#
{Row[{"x=", Dynamic[x]}], Row[{"y=", Dynamic[y]}]}}, Alignment -> Left]]]
Screenshot:
How about
DynamicModule[{x = 0},
{Slider[Dynamic[x], {0, 1}],
Slider[Dynamic[x^2, (x = Sqrt##) &], {0, 1}]}]
which is a trivial modification of a code snippet I found in tutorial/IntroductionToDynamic in the docs?
EDIT: You can add panels etc as follows:
DynamicModule[{x = 0},
Row[{Column[{Slider[Dynamic[x], {0, 1}], Panel#Dynamic#x}],
Column[{Slider[Dynamic[x^2, (x = Sqrt##) &], {0, 1}],
Panel#Dynamic#Sqrt[x]}]}]]
It might be better to have each question separate. Hard to answer 2 questions in same place.
For the first question, you can use your own Dynamics to obtain better control. Here is one way:
Manipulate[
Row[{
Dynamic[Refresh[Text#Row[{"x=",x," y=",y}],TrackedSymbols->{event}]],
Dynamic[Refresh[event=Date[];y=x*x;"",TrackedSymbols->{x}]],
Dynamic[Refresh[event=Date[];x=Sqrt[y];"",TrackedSymbols->{y}]]
}],
{{x,2,"x"},0,100,1},
{{y,2,"y"},0,1000,1},
{{event,0},ControlType->None},
TrackedSymbols:>{None}
]
For your second question, a Manipulate, has one 'panel' where output goes to. So, you can't really do it with one Manipulate. But you can nest Manipulates, so you can do it that way by having each manipulate with its own controls all under one Manipulate.
Update1:
To share variables between 2 inner Manipulate, so when one Manipulate update its own variable, the other Manipulate sees the latest update, here is one possible way. When you movbe one Manipulate slider, the second Manipulate updates automatically with the new value.
Manipulate[
Grid[{{
Manipulate[( gx=x; Row[{"x=",x," y=",gy}]),
{{x,1,"x="},0,10,1}],
Manipulate[( gy=y; Row[{"x=",gx," y=",y}]),
{{y,1,"y="},0,10,1}]
}}],
{{gx,0},ControlType->None},
{{gy,0},ControlType->None},
ControlPlacement->Bottom
]

Mathematica: 3D wire frames

Does Mathematica support hidden line removal for wire frame images? If this isn't the case, has anybody here ever come across a way to do it? Lets start with this:
Plot3D[Sin[x+y^2], {x, -3, 3}, {y, -2, 2}, Boxed -> False]
To create a wire frame we can do:
Plot3D[Sin[x+y^2], {x, -3, 3}, {y, -2, 2}, Boxed -> False, PlotStyle -> None]
One thing we can do to achieve the effect is to color the all the surfaces white. This however, is undesirable. The reason is because if we export this hidden line wire frame model to pdf we will have all of those white polygons that Mathematica uses to render the image. I want to be able to obtain a wire frame with hidden line removal in pdf and/or eps format.
UPDATE:
I have posted a solution to this problem. The problem is that the code runs very slow. In its current state it is unable to generate the wireframe for the image in this question. Feel free to play with my code. I added a link to it at the end of my post. You can also find the code in this link
Here I present a solution. First I will show how to use the function that generates the wire frame, then I will proceed to explain in detail the rest of the functions that compose the algorithm.
wireFrame
wireFrame[g_] := Module[{figInfo, opt, pts},
{figInfo, opt} = G3ToG2Info[g];
pts = getHiddenLines[figInfo];
Graphics[Map[setPoints[#] &, getFrame[figInfo, pts]], opt]
]
The input of this function is a Graphics3D object preferably with no axes.
fig = ListPlot3D[
{{0, -1, 0}, {0, 1, 0}, {-1, 0, 1}, {1, 0, 1}, {-1, 1, 1}},
Mesh -> {10, 10},
Boxed -> False,
Axes -> False,
ViewPoint -> {2, -2, 1},
ViewVertical -> {0, 0, 1},
MeshStyle -> Directive[RGBColor[0, 0.5, 0, 0.5]],
BoundaryStyle -> Directive[RGBColor[1, 0.5, 0, 0.5]]
]
Now we apply the function wireFrame.
wireFrame[fig]
As you can see wireFrame obtained most of the lines and its colors. There is a green line that was not included in the wireframe. This is most likely due to my threshold settings.
Before I proceed to explain the details of the functions G3ToG2Info, getHiddenLines, getFrame and setPoints I will show you why wire frames with hidden line removal can be useful.
The image shown above is a screenshot of a pdf file generated by using the technique described in rasters in 3D graphics combined with the wire frame generated here. This can be advantageous in various ways. There is no need to keep the information for the triangles to show a colorful surface. Instead we show a raster image of the surface. All of the lines are very smooth, with the exception of the boundaries of the raster plot not covered by lines. We also have a reduction of file size. In this case the pdf file size reduced from 1.9mb to 78kb using the combination of the raster plot and the wire frame. It takes less time to display in the pdf viewer and the image quality is great.
Mathematica does a pretty good job at exporting 3D images to pdf files. When we import the pdf files we obtain a Graphics object composed of line segments and triangles. In some cases this objects overlap and thus we have hidden lines. To make a wire frame model with no surfaces we first need to remove this overlap and then remove the polygons. I will start by describing how to obtain the information from a Graphics3D image.
G3ToG2Info
getPoints[obj_] := Switch[Head[obj],
Polygon, obj[[1]],
JoinedCurve, obj[[2]][[1]],
RGBColor, {Table[obj[[i]], {i, 1, 3}]}
];
setPoints[obj_] := Switch[Length#obj,
3, Polygon[obj],
2, Line[obj],
1, RGBColor[obj[[1]]]
];
G3ToG2Info[g_] := Module[{obj, opt},
obj = ImportString[ExportString[g, "PDF", Background -> None], "PDF"][[1]];
opt = Options[obj];
obj = Flatten[First[obj /. Style[expr_, opts___] :> {opts, expr}], 2];
obj = Cases[obj, _Polygon | _JoinedCurve | _RGBColor, Infinity];
obj = Map[getPoints[#] &, obj];
{obj, opt}
]
This code is for Mathematica 8 in version 7 you would replace JoinedCurve in the function getPoints by Line. The function getPoints assumes that you are giving a primitive Graphics object. It will see what type of object it recieves and then extract the information it needs from it. If it is a polygon it gets a list of 3 points, for a line it obtains a list of 2 points and if it is a color then it gets a list of a single list containing 3 points. This has been done like this in order to maintain consistency with the lists.
The function setPoints does the reverse of getPoints. You input a list of points and it will determine if it should return a polygon, a line or a color.
To obtain a list of triangles, lines and colors we use G3ToG2Info. This function will use
ExportString and ImportString to obtain a Graphics object from the Graphics3D version. This info is store in obj. There is some clean up that we need to perform, first we get the options of the obj. This part is necessary because it may contain the PlotRange of the image. Then we obtain all the Polygon, JoinedCurve and RGBColor objects as described in obtaining graphics primitives and directives. Finally we apply the function getPoints on all of these objects to get a list of triangles, lines and colors. This part covers the line {figInfo, opt} = G3ToG2Info[g].
getHiddenLines
We want to be able to know what part of a line will not be displayed. To do this we need to know point of intersection between two line segments. The algorithm I'm using to find the intersection can be found here.
lineInt[L_, M_, EPS_: 10^-6] := Module[
{x21, y21, x43, y43, x13, y13, numL, numM, den},
{x21, y21} = L[[2]] - L[[1]];
{x43, y43} = M[[2]] - M[[1]];
{x13, y13} = L[[1]] - M[[1]];
den = y43*x21 - x43*y21;
If[den*den < EPS, Return[-Infinity]];
numL = (x43*y13 - y43*x13)/den;
numM = (x21*y13 - y21*x13)/den;
If[numM < 0 || numM > 1, Return[-Infinity], Return[numL]];
]
lineInt assumes that the line L and M do not coincide. It will return -Infinity if the lines are parallel or if the line containing the segment L does not cross the line segment M. If the line containing L intersects the line segment M then it returns a scalar. Suppose this scalar is u, then the point of intersection is L[[1]] + u (L[[2]]-L[[1]]). Notice that it is perfectly fine for u to be any real number. You can play with this manipulate function to test how lineInt works.
Manipulate[
Grid[{{
Graphics[{
Line[{p1, p2}, VertexColors -> {Red, Red}],
Line[{p3, p4}]
},
PlotRange -> 3, Axes -> True],
lineInt[{p1, p2}, {p3, p4}]
}}],
{{p1, {-1, 1}}, Locator, Appearance -> "L1"},
{{p2, {2, 1}}, Locator, Appearance -> "L2"},
{{p3, {1, -1}}, Locator, Appearance -> "M1"},
{{p4, {1, 2}}, Locator, Appearance -> "M2"}
]
Now that we know how to far we have to travel from L[[1]] to the line segment M we can find out what portion of a line segment lies within a triangle.
lineInTri[L_, T_] := Module[{res},
If[Length#DeleteDuplicates[Flatten[{T, L}, 1], SquaredEuclideanDistance[#1, #2] < 10^-6 &] == 3, Return[{}]];
res = Sort[Map[lineInt[L, #] &, {{T[[1]], T[[2]]}, {T[[2]], T[[3]]}, {T[[3]], T[[1]]} }]];
If[res[[3]] == Infinity || res == {-Infinity, -Infinity, -Infinity}, Return[{}]];
res = DeleteDuplicates[Cases[res, _Real | _Integer | _Rational], Chop[#1 - #2] == 0 &];
If[Length#res == 1, Return[{}]];
If[(Chop[res[[1]]] == 0 && res[[2]] > 1) || (Chop[res[[2]] - 1] == 0 && res[[1]] < 0), Return[{0, 1}]];
If[(Chop[res[[2]]] == 0 && res[[1]] < 0) || (Chop[res[[1]] - 1] == 0 && res[[2]] > 1), Return[{}]];
res = {Max[res[[1]], 0], Min[res[[2]], 1]};
If[res[[1]] > 1 || res[[1]] < 0 || res[[2]] > 1 || res[[2]] < 0, Return[{}], Return[res]];
]
This function returns the the portion of the line L that needs to be deleted. For instance, if it returns {.5, 1} this means that you will delete 50 percent of the line, starting from half the segment to the ending point of the segment. If L = {A, B} and the function returns {u, v} then this means that the line segment {A+(B-A)u, A+(B-A)v} is the section of the line that its contained in the triangle T.
When implementing lineInTri you need to be careful that the line L is not one of the edges of T, if this is the case then the line does not lie inside the triangle. This is where rounding erros can be bad. When Mathematica exports the image sometimes a line lies on the edge of the triangle but these coordinates differ by some amount. It is up to us to decide how close the line lies on the edge, otherwise the function will see that the line lies almost completely inside the triangle. This is the reason of the first line in the function. To see if a line lies on an edge of a triangle we can list all the points of the triangle and the line, and delete all the duplicates. You need to specify what a duplicate is in this case. In the end, if we end up with a list of 3 points this means that a line lies on an edge. The next part is a little complicated. What we do is check for the intersection of the line L with each edge of the triangle T and store this the results in a list. Next we sort the list and find out what section, if any, of the line lies in the triangle. Try to make sense out of it by playing with this, some of the tests include checking if an endpoint of the line is a vertex of the triangle, if the line is completely inside the triangle, partly inside or completely outside.
Manipulate[
Grid[{{
Graphics[{
RGBColor[0, .5, 0, .5], Polygon[{p3, p4, p5}],
Line[{p1, p2}, VertexColors -> {Red, Red}]
},
PlotRange -> 3, Axes -> True],
lineInTri[{p1, p2}, {p3, p4, p5}]
}}],
{{p1, {-1, -2}}, Locator, Appearance -> "L1"},
{{p2, {0, 0}}, Locator, Appearance -> "L2"},
{{p3, {-2, -2}}, Locator, Appearance -> "T1"},
{{p4, {2, -2}}, Locator, Appearance -> "T2"},
{{p5, {-1, 1}}, Locator, Appearance -> "T3"}
]
lineInTri will be used to see what portion of the line will not be drawn. This line will most likely be covered by many triangles. For this reason, we need to keep a list of all the portions of each line that will not be drawn. These lists will not have an order. All we know is that this lists are one dimensional segments. Each one consisting of numbers in the [0,1] interval. I'm not aware of a union function for one dimensional segments so here is my implementation.
union[obj_] := Module[{p, tmp, dummy, newp, EPS = 10^-3},
p = Sort[obj];
tmp = p[[1]];
If[tmp[[1]] < EPS, tmp[[1]] = 0];
{dummy, newp} = Reap[
Do[
If[(p[[i, 1]] - tmp[[2]]) > EPS && (tmp[[2]] - tmp[[1]]) > EPS,
Sow[tmp]; tmp = p[[i]],
tmp[[2]] = Max[p[[i, 2]], tmp[[2]]]
];
, {i, 2, Length#p}
];
If[1 - tmp[[2]] < EPS, tmp[[2]] = 1];
If[(tmp[[2]] - tmp[[1]]) > EPS, Sow[tmp]];
];
If[Length#newp == 0, {}, newp[[1]]]
]
This function would be shorter but here I have included some if statements to check if a number is close to zero or one. If one number is EPS apart from zero then we make this number zero, the same applies for one. Another aspect that I'm covering here is that if there is a relatively small portion of the segment to be displayed then it is most likely that it needs to be deleted. For instance if we have {{0,.5}, {.500000000001}} this means that we need to draw {{.5, .500000000001}}. But this segment is very small to be even noticed specially in a large line segment, for all we know those two numbers are the same. All of this things need to be taken into account when implementing union.
Now we are ready to see what needs to be deleted from a line segment. The next requires the list of objects generated from G3ToG2Info, an object from this list and an index.
getSections[L_, obj_, start_ ] := Module[{dummy, p, seg},
{dummy, p} = Reap[
Do[
If[Length#obj[[i]] == 3,
seg = lineInTri[L, obj[[i]]];
If[Length#seg != 0, Sow[seg]];
]
, {i, start, Length#obj}
]
];
If[Length#p == 0, Return[{}], Return[union[First#p]]];
]
getSections returns a list containing the portions that need to be deleted from L. We know that obj is the list of triangles, lines and colors, we know that objects in the list with a higher index will be drawn on top of ones with lower index. For this reason we need the index start. This is the index we will start looking for triangles in obj. Once we find a triangle we will obtain the portion of the segment that lies in the triangle using the function lineInTri. At the end we will end up with a list of sections which we can combine by using union.
Finally, we get to getHiddenLines. All this requires is to look at each object in the list returned by G3ToG2Info and apply the function getSections. getHiddenLines will return a list of lists. Each element is a list of sections that need to be deleted.
getHiddenLines[obj_] := Module[{pts},
pts = Table[{}, {Length#obj}];
Do[
If[Length#obj[[j]] == 2,
pts[[j]] = getSections[obj[[j]], obj, j + 1]
];
, {j, Length#obj}
];
Return[pts];
]
getFrame
If you have manage to understand the concepts up to here I'm sure you know what will be done next. If we have the list of triangles, lines and colors and the sections of the lines that need to be deleted we need to draw only the colors and the sections of the lines that are visible. First we make a complement function, this will tell us exactly what to draw.
complement[obj_] := Module[{dummy, p},
{dummy, p} = Reap[
If[obj[[1, 1]] != 0, Sow[{0, obj[[1, 1]]}]];
Do[
Sow[{obj[[i - 1, 2]], obj[[i, 1]]}]
, {i, 2, Length#obj}
];
If[obj[[-1, 2]] != 1, Sow[{obj[[-1, 2]], 1}]];
];
If[Length#p == 0, {}, Flatten# First#p]
]
Now the getFrame function
getFrame[obj_, pts_] := Module[{dummy, lines, L, u, d},
{dummy, lines} = Reap[
Do[
L = obj[[i]];
If[Length#L == 2,
If[Length#pts[[i]] == 0, Sow[L]; Continue[]];
u = complement[pts[[i]]];
If[Length#u > 0,
Do[
d = L[[2]] - L[[1]];
Sow[{L[[1]] + u[[j - 1]] d, L[[1]] + u[[j]] d}]
, {j, 2, Length#u, 2 }]
];
];
If[Length#L == 1, Sow[L]];
, {i, Length#obj}]
];
First#lines
]
Final words
I'm somewhat happy with the results of the algorithm. What I do not like is the execution speed. I have written this as I would in C/C++/java using loops. I tried my best to use Reap and Sow to create growing lists instead of using the function Append. Regardless of all of this I still had to use loops. It should be noted that the wire frame picture posted here took 63 seconds to generate. I tried doing a wire frame for the picture in the question but this 3D object contains about 32000 objects. It was taking about 13 seconds to compute the portions that need to be displayed for a line. If we assume that we have 32000 lines and it takes 13 seconds to do all the computations that will be about 116 hours of computational time.
I'm sure this time can be reduced if we use the function Compile on all of the routines and maybe finding a way not to use the Do loops. Can I get some help here Stack Overflow?
For your convinience I have uploaded the code to the web. You can find it here. If you can apply a modified version of this code to the plot in the question and show the wire frame I will mark your solution as the answer to this post.
Best,
J Manuel Lopez
This isn't right, but somewhat interesting:
Plot3D[Sin[x + y^2], {x, -3, 3}, {y, -2, 2}, Boxed -> False,
PlotStyle -> {EdgeForm[None], FaceForm[Red, None]}, Mesh -> False]
With a FaceForm of None, the polygon isn't rendered. I'm not sure there's a way to do this with the Mesh lines.

Mathematica: How to obtain data points plotted by plot command?

When plotting a function using Plot, I would like to obtain the set of data points plotted by the Plot command.
For instance, how can I obtain the list of points {t,f} Plot uses in the following simple example?
f = Sin[t]
Plot[f, {t, 0, 10}]
I tried using a method of appending values to a list, shown on page 4 of Numerical1.ps (Numerical Computation in Mathematica) by Jerry B. Keiper, http://library.wolfram.com/infocenter/Conferences/4687/ as follows:
f = Sin[t]
flist={}
Plot[f, {t, 0, 10}, AppendTo[flist,{t,f[t]}]]
but generate error messages no matter what I try.
Any suggestions would be greatly appreciated.
f = Sin[t];
plot = Plot[f, {t, 0, 10}]
One way to extract points is as follows:
points = Cases[
Cases[InputForm[plot], Line[___],
Infinity], {_?NumericQ, _?NumericQ}, Infinity];
ListPlot to 'take a look'
ListPlot[points]
giving the following:
EDIT
Brett Champion has pointed out that InputForm is superfluous.
ListPlot#Cases[
Cases[plot, Line[___], Infinity], {_?NumericQ, _?NumericQ},
Infinity]
will work.
It is also possible to paste in the plot graphic, and this is sometimes useful. If,say, I create a ListPlot of external data and then mislay the data file (so that I only have access to the generated graphic), I may regenerate the data by selecting the graphic cell bracket,copy and paste:
ListPlot#Transpose[{Range[10], 4 Range[10]}]
points = Cases[
Cases[** Paste_Grphic _Here **, Point[___],
Infinity], {_?NumericQ, _?NumericQ}, Infinity]
Edit 2.
I should also have cross-referenced and acknowledged this very nice answer by Yaroslav Bulatov.
Edit 3
Brett Champion has not only pointed out that FullForm is superfluous, but that in cases where a GraphicsComplex is generated, applying Normal will convert the complex into primitives. This can be very useful.
For example:
lp = ListPlot[Transpose[{Range[10], Range[10]}],
Filling -> Bottom]; Cases[
Cases[Normal#lp, Point[___],
Infinity], {_?NumericQ, _?NumericQ}, Infinity]
gives (correctly)
{{1., 1.}, {2., 2.}, {3., 3.}, {4., 4.}, {5., 5.}, {6., 6.}, {7.,
7.}, {8., 8.}, {9., 9.}, {10., 10.}}
Thanks to Brett Champion.
Finally, a neater way of using the general approach given in this answer, which I found here
The OP problem, in terms of a ListPlot, may be obtained as follows:
ListPlot#Cases[g, x_Line :> First#x, Infinity]
Edit 4
Even simpler
ListPlot#Cases[plot, Line[{x__}] -> x, Infinity]
or
ListPlot#Cases[** Paste_Grphic _Here **, Line[{x__}] -> x, Infinity]
or
ListPlot#plot[[1, 1, 3, 2, 1]]
This evaluates to True
plot[[1, 1, 3, 2, 1]] == Cases[plot, Line[{x__}] -> x, Infinity]
One way is to use EvaluationMonitor option with Reap and Sow, for example
In[4]:=
(points = Reap[Plot[Sin[x],{x,0,4Pi},EvaluationMonitor:>Sow[{x,Sin[x]}]]][[2,1]])//Short
Out[4]//Short= {{2.56457*10^-7,2.56457*10^-7},<<699>>,{12.5621,-<<21>>}}
In addition to the methods mentioned in Leonid's answer and my follow-up comment, to track plotting progress of slow functions in real time to see what's happening you could do the following (using the example of this recent question):
(* CPU intensive function *)
LogNormalStableCDF[{alpha_, beta_, gamma_, sigma_, delta_}, x_] :=
Block[{u},
NExpectation[
CDF[StableDistribution[alpha, beta, gamma, sigma], (x - delta)/u],
u \[Distributed] LogNormalDistribution[Log[gamma], sigma]]]
(* real time tracking of plot process *)
res = {};
ListLinePlot[res // Sort, Mesh -> All] // Dynamic
Plot[(AppendTo[res, {x, #}]; #) &#
LogNormalStableCDF[{1.5, 1, 1, 0.5, 1}, x], {x, -4, 6},
PlotRange -> All, PlotPoints -> 10, MaxRecursion -> 4]
etc.
Here is a very efficient way to get all the data points:
{plot, {points}} = Reap # Plot[Last#Sow#{x, Sin[x]}, {x, 0, 4 Pi}]
Based on the answer of Sjoerd C. de Vries, I've now written the following code which automates a plot preview (tested on Mathematica 8):
pairs[x_, y_List]:={x, #}& /# y
pairs[x_, y_]:={x, y}
condtranspose[x:{{_List ..}..}]:=Transpose # x
condtranspose[x_]:=x
Protect[SaveData]
MonitorPlot[f_, range_, options: OptionsPattern[]]:=
Module[{data={}, plot},
Module[{tmp=#},
If[FilterRules[{options},SaveData]!={},
ReleaseHold[Hold[SaveData=condtranspose[data]]/.FilterRules[{options},SaveData]];tmp]]&#
Monitor[Plot[(data=Union[data, {pairs[range[[1]], #]}]; #)& # f, range,
Evaluate[FilterRules[{options}, Options[Plot]]]],
plot=ListLinePlot[condtranspose[data], Mesh->All,
FilterRules[{options}, Options[ListLinePlot]]];
Show[plot, Module[{yrange=Options[plot, PlotRange][[1,2,2]]},
Graphics[Line[{{range[[1]], yrange[[1]]}, {range[[1]], yrange[[2]]}}]]]]]]
SetAttributes[MonitorPlot, HoldAll]
In addition to showing the progress of the plot, it also marks the x position where it currently calculates.
The main problem is that for multiple plots, Mathematica applies the same plot style for all curves in the final plot (interestingly, it doesn't on the temporary plots).
To get the data produced into the variable dest, use the option SaveData:>dest
Just another way, possibly implementation dependent:
ListPlot#Flatten[
Plot[Tan#t, {t, 0, 10}] /. Graphics[{{___, {_, y__}}}, ___] -> {y} /. Line -> List
, 2]
Just look into structure of plot (for different type of plots there would be a little bit different structure) and use something like that:
plt = Plot[Sin[x], {x, 0, 1}];
lstpoint = plt[[1, 1, 3, 2, 1]];

Custom Intervals of Markers in Mathematica PlotMarkers

I am trying to plot multiple lists in the same plot in Mathematica (ListLinePlot) and use PlotMarkers and the PlotLegend Package to get the final figure. The issue is that Mathematica puts a marker for every point and this makes it hard to tell which marker is where in the plot. Is it possible to have a plot marker appear every n sample (e.g. every 10 points for a 100 point plot).
The Directive at the moment is PlotMarkers->{Automatic, Small}.
I think adding something like Mesh->10 should work for you:
data = Accumulate /# RandomReal[{-1/2, 1}, {3, 100}];
ListLinePlot[data, PlotMarkers -> {Automatic, Small}, Mesh -> 10]
If you want more control over the location of the plot markers than Brett's answer gives you, then you probably have to place the markers manually. Eg (modifying Brett's example)
data = Accumulate /# RandomReal[{-1/2, 1}, {3, 100}];
col = {Red, Blue, Green};
decimate[i_] := {col[[i]], PointSize -> Medium,
Point /# Transpose[{Range[1, 100, 10], data[[i, 1 ;; -1 ;; 10]]}]}
ListLinePlot[data, PlotStyle -> col, Epilog -> Table[decimate[i], {i, 3}]]
Of course Point can be replaced with any graphics object you want - eg Text, Inset etc...
Also remember you can use Tooltip to cause the marker coordinates to pop up when you pass the mouse pointer over it:
The example of what I was describing in the comment. The markers don't behave properly.
Apparently I cannot post images yet, but running the following code
data = Accumulate /# RandomReal[{-1/2, 1}, {3, 100}];
ListLinePlot[data, PlotMarkers -> {Automatic, Small}, Mesh -> 5]
should give improper results. Also the number of data and plots in the same figure is quite large to individually select which points and I would like to keep the same Directives for different plots and data ranges as they tend to vary between 100 to around 300 in each case and I have to save them in different tables as they are used in other calculations along the way.
Plot Posted by belisarius, running the code above

Resources