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