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