Graphics macros: fix for plot() demo
[scilab.git] / scilab / modules / graphics / macros / plot.sci
1 // Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
2 // Copyright (C) 2004-2006 - INRIA - Fabrice Leray
3 // Copyright (C) 2008 - INRIA - Jean-Baptiste Silvy
4 // This file must be used under the terms of the CeCILL.
5 // This source file is licensed as described in the file COPYING, which
6 // you should have received as part of this distribution.  The terms
7 // are also available at
8 // http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
9
10 function plot(varargin)
11     // Try to build a new better parser that could manage things like:
12     // plot(x,y,'X',1:10); // where X stands for Xdata (Matlab recognizes
13     //it and treats it well...)
14
15     [lhs,rhs]=argn(0);
16
17     if ~rhs
18         //LineSpec and PropertySpec examples:
19         t = 0:%pi/20:2*%pi;
20         tt = t';
21         drawlater();
22         subplot(211);
23         plot(tt, sin(tt), "ro-.", tt, cos(tt), "cya+", tt, abs(sin(tt)), "--mo");
24         subplot(212);
25         plot([t ;t],[sin(t); cos(t)],"xdat",[1:2]);
26         drawnow();
27         return;
28     end
29
30
31
32     CurColor = 0; // current color used if no color specified via LineSpec
33     // nor PropertyName
34
35
36
37     ListArg = varargin;
38
39     //detect and set the current axes now:
40     if type(ListArg(1)) == 9
41         hdle = ListArg(1);
42         if (hdle.type == "Axes")
43             sca(ListArg(1));
44             ListArg(1) = null(); // remove this parameter from the list
45         else
46             warning("Handle should be an Axes handle")
47             return;
48         end
49     end
50
51
52     nv = size(ListArg)
53
54
55     argTypes=[];
56     couple=[];
57
58     typeOfPlot = "plot";
59     provided_data = 2;
60
61     for curArgIndex=1:nv
62         argTypes(curArgIndex,1) = type(ListArg(curArgIndex))
63     end
64
65     Ttmp=argTypes;
66
67     for i=1:nv-1
68         acceptedTypes=[];
69         acceptedTypes=find(Ttmp(i,1)==1 & or(Ttmp(i+1,1)==[1,13,130])) // to accept double, macro function or primitive as second argument
70
71         if (acceptedTypes<>[]) then
72             couple=[couple i];
73             Ttmp(i,1)  = 99; // Replace a known type by 99 (no meaning) to count it once only!
74             Ttmp(i+1,1)= 99; // to avoid having (x1,y1,x2,y2) ->couple=[1,2,3]
75             // With this trick, couple=[1,3];
76         end
77
78     end
79
80
81     if (couple==[]) // No data couple found
82         // Search for at least a single data , i.e.: plot(y)
83
84         if ((argTypes(1,1)==1 | argTypes(1,1)==8) & ListArg(1)<>[]) then // case plot(SINGLE y,...)
85             couple = 1;
86             provided_data = 1;
87
88             if (modulo(nv-couple,2)<>0) then
89                 P1 = couple+2 // Position of the first PropertyName field
90             else
91                 P1 = couple+1
92             end
93
94         else
95             warning("Error inside input argument : no data");
96             return;
97         end
98
99     else
100
101         // Some test to check wrong inputs
102         //
103         // 1. Test if 2 data couples (first : type==1, second : type=[1,13,130])
104         // are at least separated by 2 indices
105         if (couple(2:$)-couple(1:$-1)<2)
106             warning("Error inside input argument !");
107             return;
108         end
109
110         // 2. Test if no string couples happen before P1 (see below for P1 definition)
111         for index=1:couple($)
112             acceptedTypes=[];
113             acceptedTypes=find(Ttmp(index,1)==10 & Ttmp(index+1,1)==10)
114
115             if (acceptedTypes<>[]) then
116                 warning("Error inside input argument : String argument is an unknown option.");
117                 return;
118             end
119         end
120
121
122
123         if (modulo(nv-(couple($)+1),2)<>0) then
124             P1 = couple($)+3 // Position of the first PropertyName field
125         else
126             P1 = couple($)+2
127         end
128
129     end
130
131     numplot = size(couple,"*");
132
133     xyIndexLineSpec = zeros(numplot,3);
134     // xyIndexLineSpec is a matrix storing the index of x, y and linespec
135     // if one of these indices is 0 => it does not exist
136     // (which is possible for x and linepsec, not for y)
137
138     if (provided_data == 2) then
139
140         for curCouple=1:size(couple,"*")
141             xyIndexLineSpec(curCouple,1:2) = couple(curCouple) +[0,1] // x,y index storage
142
143             if (couple(curCouple)+2 < P1)
144                 if (argTypes(couple(curCouple)+2,1)==10) then // LineSpec treatment
145                     xyIndexLineSpec(curCouple,3) = couple(curCouple)+2;
146                 end
147             end
148         end
149     else
150         // we are in the case where: plot(SINGLE y,... x not specified
151         // or plot(handle,SINGLE y,...
152         xyIndexLineSpec(1,1) = 0; // no x specified
153         xyIndexLineSpec(1,2) = couple;
154
155         //pause;
156
157         if (couple+1 < P1)
158             if (argTypes(couple+1,1)==10) then // LineSpec treatment
159                 xyIndexLineSpec(1,3) = couple+1;
160             end
161         end
162     end
163
164
165
166     // delay the drawing commands
167     // smart drawlater
168     current_figure=gcf();
169     cur_draw_mode = current_figure.immediate_drawing;
170     current_figure.immediate_drawing = "off";
171
172     // check wether this is the first plot for the axes in which we will draw
173     curAxes = gca();
174     // save auto_clear state.
175     OldAutoClear = curAxes.auto_clear;
176
177     isFirstPlot = (curAxes.children == [])
178
179     //Now, we plot the decomposed plots one by one with their own linespec
180     // provided_data = 2 : x and y are provided
181
182     FinalAgreg=[]; // Final Compound containing all the new created plots.
183
184     //for i=numplot:-1:1
185     for i=1:numplot
186         // Set off auto_clear for allowing multiple graphics entity
187         // will be restored behond
188         if i>1 then
189             curAxes.auto_clear="off";
190         end
191
192         //default values
193         Marker=[];
194         MarkerSize=1;
195         Color=[];
196         LineStyle=1;
197         Line = %F;
198         Marker = %F;
199
200         if (provided_data == 2) then
201
202             if (type(ListArg(xyIndexLineSpec(i,2))) == 13 | type(ListArg(xyIndexLineSpec(i,2))) == 130)
203                 // A function (macro or primitive) is given. We need to build the vector or matrix.
204                 sizefirstarg = size(ListArg(xyIndexLineSpec(i,1)));
205                 buildFunc = ListArg(xyIndexLineSpec(i,2));
206                 firstarg = ListArg(xyIndexLineSpec(i,1));
207                 tmp = [];
208
209                 for ii=1:sizefirstarg(1,2)
210                     for jj=1:sizefirstarg(1,1)
211
212                         // function evaluation may fail
213                         // try/cacth is buggy for now
214                         // so use execstr until the bug is fixed
215                         err = execstr("tmp(jj,ii) = buildFunc(firstarg(jj,ii))","errcatch","n");
216
217                         if (err <> 0) then
218                             // reset data
219                             ResetFigureDDM(current_figure, cur_draw_mode);
220
221                             // get error
222                             [err_message, err_number, err_line, err_func] = lasterror(%t);
223
224                             clear buildFunc;
225                             // print it
226                             if (err_func <> "") then
227                                 // ascii(10) = \n
228                                 error(msprintf(gettext("%s: Error : unable to evaluate input function ''%s''.") + ascii(10) + gettext("Error %d at line %d of the function: ''%s''"), "plot", err_func,err_number, err_line, err_message));
229                             else
230                                 error(msprintf(gettext("%s: Error : unable to evaluate input function.") + ascii(10) + gettext("Error %d at line %d of the function: ''%s''"), "plot", err_number, err_line, err_message));
231                             end
232                             // exit function
233                             return;
234                         end
235
236                     end
237                 end
238
239
240                 ListArg(xyIndexLineSpec(i,2)) = tmp;
241                 // if there is an other iteration, we will have error message redefining function.
242                 // we need to clear here and not before, because user must see the warning if needed.
243                 clear buildFunc;
244             end
245             [X,Y] = checkXYPair(typeOfPlot,ListArg(xyIndexLineSpec(i,1)),ListArg(xyIndexLineSpec(i,2)),current_figure,cur_draw_mode)
246         else
247             if or(size(ListArg(xyIndexLineSpec(1,2)))==1)  // If this is a vector
248                 if size(ListArg(xyIndexLineSpec(1,2)), "r") == 1 then
249                     X=1:length(ListArg(xyIndexLineSpec(1,2))); // insert a column abcsissa vector of same length,
250                 else
251                     X=(1:length(ListArg(xyIndexLineSpec(1,2))))'; // insert a row abcsissa vector of same length,
252                 end
253             else                                  // if this is a matrix,
254                 X=(1:size(ListArg(xyIndexLineSpec(1,2)),1))'; // insert a row abcsissa vector with same size
255             end
256             // In both cases (matrix/vector), transpose it now so no warning is issued in checkXYPair().
257             [X,Y] = checkXYPair(typeOfPlot,X,ListArg(xyIndexLineSpec(1,2)),current_figure,cur_draw_mode)
258         end
259
260         // Case if 'Xdata', 'Ydata' or 'Zdata' have been set in (PropertyName,Propertyvalue) couples
261         // must be taken into account now
262
263         // P1 is the position of the first PropertyName field.
264         Property = P1;
265
266         while (Property <= nv-1)
267             PropertyName  = ListArg(Property);
268             PropertyValue = ListArg(Property+1);
269
270             // Xdata can ONLY be a vector (cf. Matlab help)
271             PName = getPlotPropertyName(PropertyName,current_figure,cur_draw_mode);
272             if (PName == "xdata")
273
274                 if (type(PropertyValue)<>1 | and(size(PropertyValue)<>1))
275                     warning("Xdata value must be a column or row vector.");
276                     ResetFigureDDM(current_figure, cur_draw_mode);
277                     return;
278                 else
279                     PropertyValue = PropertyValue(:); // force
280                     if or(size(X))==1  // If X is a vector (inevitably a column vector because checkXYPair always returns a column vector)
281                         X = PropertyValue; // X is replaced by PropertyValue
282                         [X,Y] = checkXYPair(typeOfPlot,X,Y,current_figure,cur_draw_mode)
283                     else // X is a matrix
284                         if size(PropertyValue,"*") == size(X,1)
285                             for j=1:size(PropertyValue,"*")
286                                 X(j,:) = PropertyValue(j,1);
287                             end
288                         else
289                             str="plot : incompatible dimensions in input arguments";
290                             warning(str);
291                             ResetFigureDDM(current_figure, cur_draw_mode);
292                         end
293                     end
294                 end
295
296                 // Ydata ONLY be a vector (contrary to what is said by the Matlab help)
297             elseif (PName == "ydata")
298
299                 if (type(PropertyValue)<>1 | and(size(PropertyValue)<>1))
300                     warning("Ydata value must be a column or row vector.");
301                     ResetFigureDDM(current_figure, cur_draw_mode);
302                     return;
303                 else
304                     PropertyValue = PropertyValue(:); // force
305                     if or(size(Y))==1  // If Y is a vector (inevitably a column vector because checkXYPair always returns a column vector)
306                         Y = PropertyValue; // Y is replaced by PropertyValue
307                         [X,Y] = checkXYPair(typeOfPlot,X,Y,current_figure,cur_draw_mode)
308                     else // Y is a matrix
309                         if size(PropertyValue,"*") == size(Y,1)
310                             for j=1:size(PropertyValue,"*")
311                                 Y(j,:) = PropertyValue(j);
312                             end
313                         else
314                             str="plot : incompatible dimensions in input arguments";
315                             warning(str);
316                             ResetFigureDDM(current_figure, cur_draw_mode);
317                         end
318                     end
319
320                 end
321
322                 // Zdata will be treated after plot building
323             end
324
325             Property = Property+2;
326         end
327
328
329
330         //Now we have an array xyIndexLineSpec [numplot x 3] containing indices pointing on T for :
331         // - x (<>0 if existing)
332         // - y
333         // - linespec (<>0 if existing)
334         // for each plot passed in argument
335         //       x | y | linespec
336         //       ----------------
337         //plot1   0|i1 |0    <=> plot(y)
338         //plot2  i2|i3 |0    <=> plot(x,y)
339         //plot3  i4|i5 |i6   <=> plot(x,y,LINESPEC)
340         //...
341
342
343
344         if (xyIndexLineSpec(i,3)<>0) then // if we have a line spec <=> index <> 0
345             [Color,Line,LineStyle,Marker,MarkerStyle,MarkerSize,fail] = getLineSpec(ListArg(xyIndexLineSpec(i,3)),current_figure,cur_draw_mode);
346         end
347
348         // The plot is made now :
349         err = execstr("plot2d(X,Y)","errcatch","m");
350
351         if err <> 0
352             mprintf("Error %d : in plot2d called by plot",err);
353             ResetFigureDDM(current_figure, cur_draw_mode);
354             return;
355         end
356
357         agreg=gce();  // when using plot2d, we always have an Compound as the current entity
358
359         FinalAgreg = [agreg FinalAgreg];
360
361         if Color==[]
362             DefaultColor = %T;
363         else
364             DefaultColor = %F;
365         end
366
367         for ii=size(agreg.children,"*"):-1:1
368             curPolyline=agreg.children(ii); // we apply linespec to the lines
369
370             // Color treatment : if no color specified by LineSpec nor PropertyName
371             // Set the default color to the curve
372             if DefaultColor == %T
373                 [Color,CurColor] = setDefaultColor(CurColor);
374             end
375
376             if (Marker == %T)
377                 curPolyline.mark_style=MarkerStyle;
378                 curPolyline.mark_mode ="on";
379                 curPolyline.mark_foreground = Color;
380                 curPolyline.mark_style=MarkerStyle;
381                 curPolyline.mark_size=MarkerSize;
382             else
383                 curPolyline.mark_mode ="off"
384             end
385
386             if (Line == %T)
387                 curPolyline.line_mode="on";
388                 curPolyline.foreground = Color;
389                 curPolyline.line_style = LineStyle;
390             else
391                 curPolyline.line_mode="off"
392             end
393
394             if (Line == %F & Marker ==%F) // no linespec nor PropertyName set
395                 curPolyline.line_mode="on";
396                 curPolyline.foreground = Color;
397                 curPolyline.line_style = LineStyle;
398             end
399
400         end
401     end
402
403     //Reset auto_clear Property
404     curAxes.auto_clear = OldAutoClear;
405
406     ///////////////////////////////////
407     //Global Property treatment      //
408     //PropertyName and PropertyValue //
409     ///////////////////////////////////
410
411
412
413     // Those properties will be applied to Agreg children
414     Agreg = glue(FinalAgreg(1:$))
415
416     nbCompound = find(Agreg.children.type=="Compound")
417
418     while (nbCompound<>[])
419         nbCompound=nbCompound(1);
420         unglue(Agreg.children(nbCompound));
421         nbCompound=find(Agreg.children.type=="Compound")
422     end
423
424
425
426     // P1 is the position of the first PropertyName field.
427     Property = P1;
428
429     Curves = Agreg.children
430     //Curves(:,1) = Curves(:,$:-1:1);
431
432     // set mark_size_unit to 'point' for all the curves
433     Curves.mark_size_unit="point";
434
435     while (Property <= nv-1)
436         setPlotProperty(ListArg(Property),ListArg(Property+1),Curves,current_figure,cur_draw_mode)
437
438         Property = Property+2;
439     end
440
441     // force drawing of box like in matlab
442     // for a first plot
443     // unless we are using centered axes
444     // to keep compatibility with Scilab 4
445     if  isFirstPlot & curAxes.x_location <> "origin" & curAxes.y_location <> "origin" then
446         curAxes.box = "on";
447     end
448
449
450
451     //postponed drawings are done now !
452     // smart drawnow
453     ResetFigureDDM(current_figure, cur_draw_mode)
454
455 endfunction