gnuplot.red 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639
  1. module plot; % device and driver independent plot services.
  2. % Author: Herbert Melenk.
  3. % Adjusted by A C Norman to be compatible with CSL - the original
  4. % was written to be fairly PSL-specific.
  5. create!-package('(plot),nil);
  6. global '(
  7. plotdriver!* % modulename of the actual driver.
  8. plotmax!* % maximal floating point number which
  9. % gnuplot supports on the machine
  10. % (mostly IEEE double or single precision).
  11. plotmin!* % lower bound (=1/plotmax!*)
  12. variation!* % definition of y-bigstep for smooth
  13. plotoptions!* % list for collecting the options.
  14. );
  15. fluid '(
  16. plotderiv!* % derivative for 2d plot
  17. );
  18. !#if (member 'psl lispsystem!*)
  19. macro procedure !@if u;
  20. <<if eval cadr u then caddr u else cadddr u>>;
  21. !@if(errorp errorset('!*writingfaslfile,nil,nil)
  22. or not !*writingfaslfile
  23. or errorp errorset('(load fcomp),nil,nil),
  24. <<prin2t "no support for fast float!";
  25. % ACN believes that the following line is just WRONG in that the
  26. % parentheses written in (x) are ignored by the RLISP parser, to the detriment
  27. % of the way that DM will handle things!
  28. dm(fdeclare,(x),nil); dm(thefloat,(x),cadr x);>>,
  29. nil);
  30. !#else
  31. symbolic macro procedure fdeclare u; nil;
  32. symbolic macro procedure thefloat u; cadr u;
  33. !#endif
  34. endmodule;
  35. module plotsynt; % Support for the syntax of the plot command.
  36. % Author: Herbert Melenk.
  37. % Create .. as the infix operator if not yet done.
  38. !*msg := nil; % prevent message ".. redefined" during load
  39. newtok '( (!. !.) !*interval!*);
  40. if not(gettype '!*interval!* = 'operator) then
  41. <<
  42. precedence .., or;
  43. algebraic operator ..;
  44. put('!*interval!*,'PRTCH,'! !.!.! );
  45. >>;
  46. !*msg := t;
  47. global '(plot_xmesh plot_ymesh plot_xrange plot_yrange plot_zrange);
  48. share plot_xmesh,plot_ymesh,plot_xrange,plot_yrange,plot_zrange;
  49. plot_xmesh := 20;
  50. plot_ymesh := 20;
  51. global'(!*plotrefine);
  52. fluid '(plotprecision!*);
  53. plotprecision!* := 0.9995;
  54. switch plotrefine; % if OFF no refinments are computed
  55. % for 2D plot.
  56. !*plotrefine:=t;
  57. fluid '(!*show_grid);
  58. switch show_grid;
  59. if null plotmax!* then
  60. <<
  61. load!-package 'arith;
  62. if not !!plumax then roundconstants();
  63. plotmax!* := !!plumax; % IEEE double precision
  64. >>;
  65. plotmin!*:= 1.0/plotmax!*;
  66. fluid '(plotranges!* plotfunctions!* plotstyle!* !*plotoverflow
  67. !*roundbf);
  68. put('plot,'psopfn,'ploteval);
  69. % I need the following definition only at compile time.
  70. macro procedure plotdriver u;
  71. {'apply,{'get,'plotdriver!*,mkquote cadr u},'list.cddr u};
  72. symbolic procedure ploteval u;
  73. begin scalar m;
  74. if null plotdriver!* then
  75. rederr "no active device driver for PLOT";
  76. m:=plotrounded(nil);
  77. !*plotoverflow := nil;
  78. plotranges!* := plotfunctions!* := nil;
  79. plotstyle!* := 'lines;
  80. plotdriver(init);
  81. for each option in u do ploteval1 reval option;
  82. ploteval2();
  83. plotrounded(m);
  84. end;
  85. symbolic procedure ploteval1 option;
  86. begin scalar x,do;
  87. do := get(plotdriver!*,'do);
  88. if pairp option and (x:=get(car option,do))
  89. then apply(x,list option) else
  90. if eqcar(option,'equal) and (x:=get(cadr option,do))
  91. then apply(x,list caddr option)
  92. else ploteval0 option;
  93. end;
  94. symbolic procedure ploteval0 option;
  95. begin scalar l,r,opt;
  96. opt:=get(plotdriver!*,'option);
  97. if flagp(option,opt) then
  98. <<plotoptions!*:=option . plotoptions!*; return>>;
  99. if eqcar(option,'list) then
  100. <<option := cdr option;
  101. if option and eqcar(car option,'list) then
  102. return (plotfunctions!*:=
  103. ('points.plotpoints option).plotfunctions!*);
  104. typerr(option,"plot option")
  105. >>;
  106. if eqcar(option,'equal) and flagp(cadr option,opt) then
  107. <<plotoptions!*:=(cadr option.caddr option). plotoptions!*;
  108. return>>;
  109. if not eqcar(option,'equal) then
  110. <<plotfunctions!*:= (nil.option) . plotfunctions!*; return>>;
  111. l:=reval cadr option;
  112. r:=reval caddr option;
  113. if l=0 or r=0 then return
  114. plotfunctions!*:=('implicit.if l=0 then r else l)
  115. . plotfunctions!*;
  116. if not idp l then rederr "illegal option in PLOT";
  117. if l memq '(size terminal view) then
  118. <<plotoptions!*:=(l.r).plotoptions!*; return>>;
  119. if eqcar(r,'!*interval!*) then
  120. % must be a range
  121. <<r:='!*interval!* . revalnuminterval(r,t);
  122. plotranges!* := (l . r) . plotranges!*>>
  123. else
  124. plotfunctions!* := (l . r) . plotfunctions!*;
  125. end;
  126. symbolic procedure ploteval2 ();
  127. % all options are collected now;
  128. begin scalar dvar,ivars;
  129. for each u in plotfunctions!* do
  130. <<if car u eq 'implicit and dvar and dvar neq car u then
  131. rederr "mixture of implicit and regular plot not supported";
  132. dvar:=car u or dvar;
  133. ivars := plotindepvars(cdr u,ivars)>>;
  134. % classify
  135. if null dvar then
  136. <<dvar:='(x y z);
  137. for each x in ivars do dvar:=delete(x,dvar);
  138. if dvar then dvar:=car dvar
  139. >>;
  140. if length ivars=1 then ploteval2x(car ivars,dvar) else
  141. if length ivars=2 then ploteval3xy(car ivars,cadr ivars,dvar)
  142. else typerr('list . for each p in plotfunctions!* collect
  143. if null car p then cdr p else
  144. {'equal,car p,cdr p},
  145. " plot option or function");
  146. plotdriver(show);
  147. end;
  148. symbolic procedure plotrange(x,d);
  149. begin scalar y;
  150. y:=assoc(x,plotranges!*);
  151. y:=if y then cdr y else d;
  152. if y=0 or null y then % return nil;
  153. y:={'!*INTERVAL!*, - plotmax!*, plotmax!*};
  154. if not eqcar(y,'!*INTERVAL!*) then
  155. typerr(y,"plot range");
  156. return {plotevalform0(rdwrap cadr y,nil) ,
  157. plotevalform0(rdwrap caddr y,nil)};
  158. end;
  159. symbolic procedure plotseteq(u,v);
  160. null u and null v or car u member v
  161. and plotseteq(cdr u,delete(car u,v));
  162. symbolic procedure plotindepvars(u,v);
  163. if idp u then
  164. if member(u,v) or member(u,'(e pi))
  165. or u eq 'i and !*complex then v
  166. else u . v
  167. else if eqcar(u,'file) then cddr u
  168. else if pairp u then
  169. if get(car u,'dname) then v else
  170. if member(car u,'(plus minus difference times quotient expt)) or
  171. get(car u,'!:RD!:) or get(car u,'simpfn) then
  172. <<for each x in cdr u do v:=plotindepvars(x,v); v>>
  173. else typerr(u,"expression in function to plot")
  174. else v;
  175. remprop('plotshow,'stat);
  176. symbolic procedure plotshow();
  177. plotdriver(show);
  178. put('plotshow,'stat,'endstat);
  179. remprop('plotreset,'stat);
  180. symbolic procedure plotreset();
  181. plotdriver(reset);
  182. put('plotreset,'stat,'endstat);
  183. endmodule; % plotsynt.
  184. module plotexp2; % Compute explicit 2-dim graph y=f(x);
  185. symbolic procedure ploteval2x(x,y);
  186. begin scalar xlo,xhi,ylo,yhi,rx,ry,fp,fcn,fcns,pts;
  187. if y='implicit then
  188. rederr "no implicit plot in one dimension";
  189. rx:=plotrange(x,
  190. reval(plot_xrange or '(!*interval!* -10 10)));
  191. xlo:=car rx;
  192. xhi:=cadr rx;
  193. fcns:= reverse plotfunctions!*;
  194. ry:=plotrange(y, reval(plot_yrange or nil));
  195. if ry then <<ylo:=car ry; yhi:=cadr ry>>;
  196. while fcns do
  197. <<fcn := car fcns; fcns := cdr fcns;
  198. if eqcar(fcn,'points) then fp:=caddr fcn else
  199. pts:=ploteval2x1(cdr fcn,x,xlo,xhi,ylo,yhi).pts;
  200. >>;
  201. plotdriver(plot!-2exp,x,y,pts,fp);
  202. end;
  203. symbolic procedure ploteval2x1(f,x,xlo,xhi,ylo,yhi);
  204. begin scalar plotsynerr!*,l,d,d0,u,v,vv,p,mx,mn,ff;
  205. scalar plotderiv!*;
  206. % compute algebraic derivative.
  207. plotderiv!*:=reval {'df,f,x};
  208. plotderiv!*:= if smemq('df,plotderiv!*) then nil
  209. else rdwrap plotderiv!* . plotderiv!*;
  210. ff:=rdwrap f;
  211. plot_xmesh := reval plot_xmesh;
  212. p:=float plot_xmesh;
  213. d:=(d0:=(xhi-xlo))/p;
  214. v:=xlo;
  215. for i:=0:plot_xmesh do
  216. <<vv:=if i=0 or i=plot_xmesh then v
  217. else v+d*(random(100)-50)*0.001;
  218. u:= plotevalform(ff,f,{x.vv});
  219. if plotsynerr!* then typerr(f,"function to plot");
  220. if eqcar(u,'overflow) then u:=nil;
  221. if u then
  222. <<
  223. if yhi and u>yhi then u:=yhi else
  224. if ylo and u<ylo then u:=ylo;;
  225. if null mx or u>mx then mx:=u;
  226. if null mn or u<mn then mn:=u
  227. >>;
  228. l:=(vv.u).l;
  229. v:=v+d;
  230. >>;
  231. variation!* :=
  232. if yhi and not(yhi=plotmax!*) then (yhi-ylo) else
  233. ploteval2xvariation l;
  234. if !*plotrefine then
  235. l:=smooth(reversip l,ff,f,x,mx,mn,ylo,yhi,d);
  236. return {for each x in l collect {car x,cdr x}};
  237. end;
  238. symbolic procedure ploteval2xvariation l;
  239. begin scalar u;
  240. % compute geometric mean value.
  241. integer m;
  242. u:=1.0;
  243. for each p in l do
  244. <<m:=m+1; p:=cdr p;
  245. if p and p neq 0.0 then u:=u*abs p;
  246. >>;
  247. return expt(u,1/float m);
  248. end;
  249. symbolic procedure smooth(l,ff,f,x,maxv,minv,ylo,yhi,d);
  250. begin scalar rat,grain,cutmax,cutmin,z,z0;
  251. z:=l;
  252. cutmax := yhi or (- 2*minv + 3*maxv);
  253. cutmin := ylo or (3*minv - 2*maxv);
  254. grain := variation!* *0.05;
  255. rat := d / if numberp maxv and maxv > minv then (maxv - minv)
  256. else 1;
  257. % Delete empty points in front of the point list.
  258. while z and null cdar z and cdr z and null cdadr z do z:=cdr z;
  259. % Find left starting point if there is no one yet.
  260. if z and null cdar z and cdr z then
  261. <<z0:= findsing(ff,f,x,ylo,yhi,cadr z,car z);
  262. if z0 then l:=z:=z0.cdr z;
  263. >>;
  264. while z and cdr z do
  265. <<z0:=z; z:=cdr z;
  266. smooth1(z0,ff,f,x,cutmin,cutmax,grain,rat,0,8)
  267. >>;
  268. return l;
  269. end;
  270. symbolic procedure smooth1(l,ff,f,x,minv,maxv,g,rat,lev,ml);
  271. smooth2(l,ff,f,x,minv,maxv,g,rat,lev,ml);
  272. symbolic procedure smooth2(l,ff,f,x,minv,maxv,g,rat,lev,ml);
  273. if lev >= ml then
  274. smooth3(l,ff,f,x,minv,maxv,g,rat,lev,ml)
  275. else
  276. if null cdar l then t else
  277. begin scalar p0,p1,p2,p3,x1,x2,x3,y1,y2,y3,d;
  278. scalar dy12,dy32,dx12,dx32,z,a,w;
  279. fdeclare(x1,x2,x3,y1,y2,y3,rat,d,dx12,dx32,dy12,dy32);
  280. lev:= add1 lev;
  281. p1:=car l; p3:=cadr l;
  282. x1:=car p1; y1:=cdr p1;
  283. x3:=car p3; y3:=cdr p3;
  284. nopoint:
  285. if null y3 then
  286. <<if null cddr l then return(cdr l:=nil);
  287. x2:=x3; y2:=y3; cdr l:=cddr l;
  288. p3:=cadr l; x3:=car p3; y3:=cdr p3;
  289. if y3 then goto outside else goto nopoint;
  290. >>;
  291. % Generate a new point
  292. x2:=(x1+x3)*0.5;
  293. if null (y2 := plotevalform(ff,f,{x.x2}))
  294. or eqcar(y2,'overflow) then goto outside;
  295. if y2 < minv or y2 > maxv then goto outside;
  296. dy32 := (y3 - y2) * rat; dx32 := x3 - x2;
  297. dy12 := (y1 - y2) * rat; dx12 := x1 - x2;
  298. d := sqrt((dy32**2 + dx32**2) * (dy12**2 + dx12**2));
  299. if zerop d then return t;
  300. w := (dy32*dy12 + dx32*dx12);
  301. d:= errorset({'quotient,w,d},nil,nil);
  302. % d is cos of angle between the vectors p2p1 and p2p3.
  303. % d near to 1 means that the angle is almost 0 (needle).
  304. if ploterrorp d then goto disc else d:=car d;
  305. a:=abs(y3-y1)<g;
  306. if d > plotprecision!* and a and lev=ml then goto disc;
  307. % I have a good point, insert it with destructive replacement.
  308. cdr l := (x2 . y2) . cdr l;
  309. % When I have an almost 180 degree angle, I compare the
  310. % derivative at the midpoint with the difference quotient.
  311. % If these are close to each other, I stop the refinement.
  312. if -d > plotprecision!*
  313. and (null plotderiv!*
  314. or
  315. ((w:=plotevalform(car plotderiv!*,cdr plotderiv!*,{x.x2}))
  316. and abs (w - (y3-y1)/(x3-x1))*rat <0.1))
  317. then return t;
  318. smooth2(cdr l,ff,f,x,minv,maxv,g,rat,lev,ml);
  319. smooth2(l,ff,f,x,minv,maxv,g,rat,lev,ml);
  320. return t;
  321. % Function has exceeded the boundaries. I try to locate the screen
  322. % crossing point.
  323. outside:
  324. cdr l := (x2 . nil) . cdr l;
  325. z := cdr l; % insert a break
  326. p2:= (x2 . y2); % artificial midpoint
  327. p0:=findsing(ff,f,x, minv, maxv, p1, p2);
  328. if p0 then
  329. << cdr l := p0 . z;
  330. smooth2(l,ff,f,x,minv,maxv,g,rat,lev,ml) >>;
  331. p0 := findsing(ff,f,x, minv, maxv, p3, p2);
  332. if p0 then
  333. << cdr z := p0 . cdr z;
  334. smooth2(cdr z,ff,f,x,minv,maxv,g,rat,lev,ml) >>;
  335. return;
  336. disc: % look for discontinuities.
  337. return smooth3(l,ff,f,x,minv,maxv,g,rat,lev,ml);
  338. end;
  339. symbolic procedure smooth3(l,ff,f,x,minv,maxv,g,rat,lev,ml);
  340. % detect discontinuities.
  341. begin scalar p1,p2,p3,x1,x2,x3,y1,y2,y3,d;
  342. scalar x2hi,y2hi,x2lo,y2lo,dir,hidir,chi,clo;
  343. scalar lobound,hibound;
  344. integer n;
  345. fdeclare(x1,x2,x3,y1,y2,y3,d,hidir);
  346. p1:=car l; p3:=cadr l;
  347. x1:=car p1; y1:=cdr p1;
  348. x3:=car p3; y3:=cdr p3;
  349. if abs(y3-y1)<variation!* then return t;
  350. % Bigstep found.
  351. hibound:=variation!**10.0; lobound:=-hibound;
  352. if y1>y3 then
  353. <<x2hi:=x1; y2hi:=y1; x2lo:= x3; y2lo:=y3; hidir:=-1.0>>
  354. else
  355. <<x2hi:=x3; y2hi:=y3; x2lo:= x1; y2lo:=y1; hidir:=1.0>>;
  356. n:=0; d:= (x3-x1)*0.5; x2:=x1+d;
  357. % intersection loop. Cut remaining interval into two parts.
  358. % If there is again a high increase in one direction we assume
  359. % a pole.
  360. next_point:
  361. if null (y2 := plotevalform(ff,f,{x.x2}))
  362. or eqcar(y2,'overflow) then goto outside;
  363. if y2 < minv then
  364. <<x2lo:=x2;y2lo:=minv;dir:=hidir>>
  365. else if y2 < y2lo then
  366. <<if y2lo<0.0 and y2<y2lo+y2lo and y2<lobound then clo:=t;
  367. x2lo:=x2;y2lo:=y2;dir:=hidir;>>
  368. else if y2 > maxv then
  369. <<x2hi:=x2;y2hi:=maxv;dir:=-hidir>>
  370. else if y2 > y2hi then
  371. <<if y2hi>0.0 and y2>y2hi+y2hi and y2>hibound then chi:=t;
  372. x2hi:=x2;y2hi:=y2;dir:=-hidir;>> else
  373. goto no_disc;
  374. if dir and (n:=n+1)<20 and (not clo or not chi) then
  375. <<d:=d/2; x2:=x2+d*dir; goto next_point>>;
  376. no_disc:
  377. if null dir then return t;
  378. if clo then y2lo:=minv;
  379. if chi then y2hi:=maxv;
  380. outside:
  381. p1:=(x2hi.y2hi); p3:=(x2lo.y2lo);
  382. if hidir=1.0 then <<p2:=p3;p3:=p1;p1:=p2>>;
  383. cdr l := p1 . (car p1.nil) . p3 . cdr l;
  384. return;
  385. brk:
  386. cdr l := (car p1.nil) . cdr l;
  387. return;
  388. end;
  389. symbolic procedure findsing(ff,f,x,minv,maxv,p1,p3);
  390. % P3 is a point with a singularity.
  391. % Try to locate the point where we exceed minv/maxv by subdivision.
  392. begin scalar p1, p2, p3, x1, x2, x3, y1, y2, y3, d, x2n;
  393. x1:=car p1; y1:=cdr p1;
  394. x3:=car p3; y3:=cdr p3;
  395. d := (x3-x1)*0.5; x2n:=x1+d;
  396. for i:=1:5 do
  397. << d:=d*0.5; x2:= x2n;
  398. if null(y2 := plotevalform(ff,f,{x.x2}))
  399. or eqcar(y2,'overflow)
  400. or y2 < minv or y2 > maxv
  401. then x2n := x2n - d
  402. else << p2 := (x2 . y2); x2n := x2n + d>>
  403. >>;
  404. if null p2 then return nil;
  405. % generate uniform maxima/minima
  406. x2 := car p2; y2 := cdr p2;
  407. y2 := if (eqcar(y2,'overflow) and cdr y2<0) or y2<minv
  408. then minv
  409. else if eqcar(y2,'overflow) or y2>maxv then maxv else y2;
  410. return (x2 . y2)
  411. end;
  412. endmodule; % plotexp2
  413. module plotexp3; % Computing surface z=f(x,y) with regular grid.
  414. % Author: Herbert Melenk, ZIB Berlin.
  415. symbolic procedure ploteval3xy(x,y,z);
  416. begin scalar rx,ry,rz,f,fcn;
  417. rx:=plotrange(x,
  418. reval(plot_xrange or '(!*interval!* -10 10)));
  419. ry:=plotrange(y,
  420. reval(plot_yrange or '(!*interval!* -10 10)));
  421. rz:=plotrange(z, reval(plot_zrange or nil));
  422. fcn := car reverse plotfunctions!*;
  423. if z='implicit then
  424. return ploteval2xyimpl(rx,ry,fcn,x,y);
  425. f:= if eqcar(fcn,'points) then caddr fcn else
  426. ploteval3xy1(cdar plotfunctions!*,
  427. z,if rz then car rz, if rz then cadr rz,
  428. x,car rx,cadr rx,
  429. y,car ry,cadr ry);
  430. plotdriver(plot!-3exp!-reg,x,y,z,f);
  431. end;
  432. symbolic procedure ploteval3xy1(f,z,zlo,zhi,x,xlo,xhi,y,ylo,yhi);
  433. begin scalar u,dx,dy,xx,yy,l,ff;
  434. ff := rdwrap f;
  435. xlo:=rdwrap xlo; xhi:=rdwrap xhi;
  436. ylo:=rdwrap ylo; yhi:=rdwrap yhi;
  437. plot_xmesh := reval plot_xmesh;
  438. plot_ymesh := reval plot_ymesh;
  439. dx:=float(xhi-xlo)/float(plot_xmesh);
  440. dy:=float(yhi-ylo)/float(plot_ymesh);
  441. l:=
  442. for j:=0:plot_ymesh collect
  443. <<
  444. for i:=0:plot_xmesh collect
  445. <<
  446. xx:=(xlo+i*dx); yy:=(ylo+j*dy);
  447. u:=plotevalform(ff,f,{x . xx,y . yy});
  448. if eqcar(u,'overflow) then u:=nil;
  449. if null u then % look for an isolated singularity
  450. u:=ploteval3xysingular(ff,f,x,xx,dx,y,yy,dy,zhi,zlo);
  451. u := if zhi and u>zhi then zhi else
  452. if zlo and u<zlo then zlo else u;
  453. {xx,yy,u}
  454. >>
  455. >>;
  456. return l;
  457. end;
  458. symbolic procedure ploteval3xysingular(ff,f,x,xx,dx,y,yy,dy,zhi,zlo);
  459. % set up an iteration approaching a cirital point.
  460. <<dx:=dx/4; dy:=dy/4;
  461. ploteval3xysingular1(ff,f,x,xx,dx,y,yy,dy,zhi,zlo,
  462. plotevalform(ff,f,{x . (xx+dx), y . (yy+dy)}),0)
  463. >>;
  464. symbolic procedure ploteval3xysingular1(ff,f,x,xx,dx,y,yy,dy,zhi,zlo,w,c);
  465. if null w then 0 else if c>8 then w else
  466. if w>zhi then zhi else
  467. if w<zlo then zlo else
  468. begin scalar wnew;
  469. dx:=dx/2; dy:=dy/2;
  470. wnew := plotevalform(ff,f,{x . (xx+dx), y . (yy+dy)});
  471. return
  472. if null wnew then w else
  473. if abs(wnew-w) <abs wnew/20 then wnew else
  474. ploteval3xysingular1(ff,f,x,xx,dx,y,yy,dy,zhi,zlo,wnew,c+1);
  475. end;
  476. endmodule;
  477. module plotimp2; % Implicit plot: compute the varity {x,y|f(x,y)=0}.
  478. % Author: Herbert Melenk, ZIB Berlin.
  479. % data structure:
  480. %
  481. % point = {x,y,f(x,y),t1,t2,t3,...}
  482. % where ti are the numbers of the triangles which
  483. % have this point as a node.
  484. % The point list is unique - adjacent triangles share
  485. % the list for equal nodes. The node numbers are
  486. % updated in place.
  487. %
  488. % triangle = {t#,p1,p2,p3}
  489. % triangles are stored in triangle vector by number
  490. %
  491. fluid '(p3!-triacount!* p3!-trias!* !*p3!-trace);
  492. symbolic procedure ploteval2xyimpl(rx,ry,f,x,y);
  493. begin scalar l,form,g;
  494. form := plot!-form!-prep(cdr f,x,y);
  495. l:=p3!-plot(car rx,cadr rx, car ry,cadr ry,reval plot_xmesh,form);
  496. if !*show_grid then g:= p3!-show!-meshes();
  497. plotdriver(plot!-2imp,x,y,l,g,car rx,cadr rx,car ry,cadr ry);
  498. end;
  499. symbolic procedure p3!-init();
  500. <<p3!-triacount!*:=0;
  501. p3!-trias!*:=mkxvect();
  502. >>;
  503. symbolic procedure mk!-point(x0,y0,fcn);
  504. {x0,y0,apply2(fcn,x0,y0)};
  505. symbolic procedure p3!-delete!-pt!-reference(i,p);
  506. cdr cddr p := delete(i,cdddr p);
  507. symbolic procedure mk!-tria(i,p1,p2,p3);
  508. % make a triangle from 3 points. If the number is given,
  509. % reuse it. Otherwise generate a fresh number.
  510. begin scalar p; integer i;
  511. i := i or (p3!-triacount!* := p3!-triacount!* #+1);
  512. p:={i,p1,p2,p3,p3!-tria!-zerop!*(p1,p2,p3)};
  513. xputv(p3!-trias!*,i,p);
  514. aconc(p1,i); aconc(p2,i); aconc(p3,i);
  515. if !*p3!-trace then print!-tria("new triangle ",p);
  516. return p;
  517. end;
  518. symbolic procedure print!-tria(tx,w);
  519. <<prin2 tx; prin2 car w; w:=cdr w;
  520. prin2l {{car car w,cadr car w,{caddr car w}},
  521. {car cadr w,cadr cadr w,{caddr cadr w}},
  522. {car caddr w,cadr caddr w,{caddr caddr w}}};
  523. terpri();
  524. >>;
  525. symbolic procedure p3!-tria!-zerop!*(p1,p2,p3);
  526. % Here I test whether the triangle contains a zero line.
  527. begin scalar f1,f2,f3;
  528. f1 := caddr p1;
  529. f2 := caddr p2;
  530. f3 := caddr p3;
  531. return f1*f2 <= 0.0 or f1*f3 <= 0.0;
  532. end;
  533. symbolic procedure p3!-tria!-zerop(w);
  534. % Fast access to stored zerop property.
  535. cadddr cdr w;
  536. symbolic procedure p3!-neighbours p;
  537. % Compute the direct neighbours of p - the traingles which have
  538. % two nodes respectively one complete edge in common with p.
  539. begin scalar l,p1,p2,p3; integer n;
  540. if fixp p then p:=xgetv(p3!-trias!*,p);
  541. n:= car p; p:=cdr p;
  542. p1:=delete(n,cdddr car p);
  543. p2:=delete(n,cdddr cadr p);
  544. p3:=delete(n,cdddr caddr p);
  545. l:={find!-one!-common(p1,p2),
  546. find!-one!-common(p2,p3),
  547. find!-one!-common(p3,p1)};
  548. while nil memq l do l:=delete(nil,l);
  549. return for each w in l collect xgetv(p3!-trias!*,w);
  550. end;
  551. symbolic procedure p3!-edge!-length(p1,p2);
  552. begin scalar dx,dy;
  553. fdeclare('p3!-edge!-length,dx,dy);
  554. dx:=thefloat car p1 - thefloat car p2;
  555. dy:=thefloat cadr p1 - thefloat cadr p2;
  556. return sqrt(dx*dx + dy*dy)
  557. end;
  558. symbolic procedure p3!-tria!-surface w;
  559. begin scalar x1,x2,x3,y1,y2,y3,p1,p2,p3;
  560. fdeclare('p3!-tria!-surface,x1,x2,x3,y1,y2,y3);
  561. w:=cdr w;
  562. x1:=car (p1:=car w); y1:=cadr p1;
  563. x2:=car (p2:=cadr w); y2:=cadr p2;
  564. x3:=car (p3:=caddr w); y3:=cadr p3;
  565. return abs ((0.5*(x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2))));
  566. end;
  567. symbolic procedure p3!-tria!-length w;
  568. begin scalar p1,p2,p3;
  569. w:=cdr w;
  570. p1:=car w; p2:=cadr w; p3:=caddr w;
  571. return
  572. (0.3*(p3!-edge!-length(p1,p2)
  573. + p3!-edge!-length(p2,p3)
  574. + p3!-edge!-length(p3,p1)));
  575. end;
  576. symbolic procedure p3!-tria!-midpoint(w);
  577. <<w:= cdr w;
  578. {(thefloat car car w
  579. + thefloat car cadr w
  580. + thefloat car caddr w)/3.0,
  581. (thefloat cadr car w
  582. + thefloat cadr cadr w
  583. + thefloat cadr caddr w)/3.0}
  584. >>;
  585. symbolic procedure p3!-tria!-goodpoint(w);
  586. % Here I assume that there is a zero in the triangle. Compute
  587. % a point inside the triangle which is as close as possible
  588. % to the desired line, but without local recomputation of
  589. % function values.
  590. begin scalar p1,p2,p3,f1,f2,f3,s1,s2,s3;
  591. w:=cdr w;
  592. p1:=car w; p2:=cadr w; p3:=caddr w;
  593. if (f1:=caddr p1)=0.0 then return {car p1,cadr p1} else
  594. if (f2:=caddr p2)=0.0 then return {car p2,cadr p2} else
  595. if (f3:=caddr p3)=0.0 then return {car p3,cadr p3};
  596. s1:=f1<0.0; s2:=f2<0.0; s3:=f3<0.0;
  597. return if s1=s2 then
  598. p3!-tria!-goodpoint1(p1,f1,p3,f3,p2,f2)
  599. else if s1=s3 then
  600. p3!-tria!-goodpoint1(p1,f1,p2,f2,p3,f3)
  601. else
  602. p3!-tria!-goodpoint1(p2,f2,p1,f1,p3,f3)
  603. end;
  604. symbolic procedure p3!-tria!-goodpoint1(p1,f1,p2,f2,p3,f3);
  605. % Now I know that f2 has the opposite sign to f1 and f3.
  606. % I take the linearly interpolated zero of f on p1-p2 and p2-p3
  607. % return their arithmetic mean value which lies inside the
  608. % triangle.
  609. begin scalar x1,x2,y1,y2,s;
  610. fdeclare (x1,x2,y1,y2,s,f1,f2,f3);
  611. s:=f1-f2;
  612. x1:=(f1*thefloat car p2 - f2*thefloat car p1)/s;
  613. y1:=(f1*thefloat cadr p2 - f2*thefloat cadr p1)/s;
  614. s:=f3-f2;
  615. x2:=(f3*thefloat car p2 - f2*thefloat car p3)/s;
  616. y2:=(f3*thefloat cadr p2 - f2*thefloat cadr p3)/s;
  617. return {(x1+x2)*0.5, (y1+y2)*0.5};
  618. end;
  619. symbolic procedure p3!-tri!-refine!-one!-tria (w,fn);
  620. % Refine one triangle by inserting a new point in the mid
  621. % of the longest edge. Also, refine the triangle adjacent
  622. % to that edge with the same point.
  623. begin scalar p1,p2,p3,pn,xn,yn,new,nb,y; integer i;
  624. scalar x1,x2,y1,y2,d1,d2,d3,s;
  625. fdeclare (x1,x2,y1,y2,s,d1,d2,d3);
  626. if fixp w then w :=xgetv(p3!-trias!*,w); % record.
  627. if !*p3!-trace then print!-tria("refine ",w);
  628. i:=car w; w :=cdr w;
  629. % delete reference to this triangle.
  630. p3!-delete!-pt!-reference(i,car w);
  631. p3!-delete!-pt!-reference(i,cadr w);
  632. p3!-delete!-pt!-reference(i,caddr w);
  633. % find longest edge
  634. d1:=p3!-edge!-length(car w,cadr w);
  635. d2:=p3!-edge!-length(cadr w,caddr w);
  636. d3:=p3!-edge!-length(car w,caddr w);
  637. if d1>=d2 and d1>=d3 then
  638. <<p1:=car w; p2:=cadr w; p3:=caddr w>>
  639. else if d2>=d1 and d2>=d3 then
  640. <<p1:=cadr w; p2:=caddr w; p3:=car w>>
  641. else
  642. <<p1:=car w; p2:=caddr w, p3:=cadr w>>;
  643. % identify the neighbour triangle.
  644. nb := find!-one!-common(cdddr p1,cdddr p2);
  645. % compute new point almost at the mid.
  646. s:=0.45+0.01*random(10);
  647. x1:=car p1; y1:=cadr p1;
  648. x2:=car p2; y2:=cadr p2;
  649. xn:=x1*s+x2*(1.0-s);
  650. yn:=y1*s+y2*(1.0-s);
  651. pn:=mk!-point(xn,yn,fn);
  652. construct:
  653. % construct new triangles
  654. new:=mk!-tria(i,p1,pn,p3).new;
  655. new:=mk!-tria(nil,p2,pn,p3).new;
  656. % update the neighbour, if there is one
  657. if null nb then return new;
  658. w:=nb; nb:=nil;
  659. if fixp w then w :=xgetv(p3!-trias!*,w); % record.
  660. i:=car w; w:=cdr w;
  661. p3!-delete!-pt!-reference(i,car w);
  662. p3!-delete!-pt!-reference(i,cadr w);
  663. p3!-delete!-pt!-reference(i,caddr w);
  664. % find the far point
  665. p3 := if not((y:=car w) eq p1 or y eq p2) then y else
  666. if not((y:=cadr w) eq p1 or y eq p2) then y else
  667. caddr w;
  668. goto construct;
  669. end;
  670. %symbolic procedure p3!-tri!-refine!-one!-tria!-center (w,fn);
  671. % % Refine one triangle by inserting a new point in the center.
  672. % begin scalar p1,p2,p3,pn,xn,yn,new,nb,y; integer i;
  673. % scalar x1,x2,x3,y1,y2,y3;
  674. % fdeclare (x1,x2,x3,y1,y2,y3);
  675. % if fixp w then w :=xgetv(p3!-trias!*,w); % record.
  676. % if !*p3!-trace then print!-tria("refine ",w);
  677. % i:=car w; w :=cdr w;
  678. %
  679. % % delete reference to this triangle.
  680. % p3!-delete!-pt!-reference(i,car w);
  681. % p3!-delete!-pt!-reference(i,cadr w);
  682. % p3!-delete!-pt!-reference(i,caddr w);
  683. %
  684. % % compute center.
  685. % p1:=car w; p2:=cadr w; p3:=caddr w;
  686. % x1:=car p1; y1:=cadr p1;
  687. % x2:=car p2; y2:=cadr p2;
  688. % x3:=car p3; y3:=cadr p3;
  689. % xn:=(x1+x2+x3)*0.33333;
  690. % yn:=(y1+y2+y3)*0.33333;
  691. % pn:=mk!-point(xn,yn,fn);
  692. %construct:
  693. % % construct new triangles
  694. % new:=mk!-tria(i,p1,p2,pn).new;
  695. % new:=mk!-tria(nil,p2,p3,pn).new;
  696. % new:=mk!-tria(nil,p1,p3,pn).new;
  697. % return new;
  698. % end;
  699. symbolic procedure find!-one!-common(u,v);
  700. % fast equivalent to "car intersection(u,v)".
  701. if null u then nil else
  702. if memq(car u,v) then car u else
  703. find!-one!-common(cdr u,v);
  704. %%%%%% Main program for implicit plot.
  705. symbolic procedure p3!-plot(xlo,xhi,ylo,yhi,pts,fcn);
  706. begin scalar p1,p2,p3,p4,sf,z;
  707. p3!-init();
  708. % setup initial triangularization.
  709. p1:=mk!-point(xlo,ylo,fcn);
  710. p2:=mk!-point(xhi,ylo,fcn);
  711. p3:=mk!-point(xhi,yhi,fcn);
  712. p4:=mk!-point(xlo,yhi,fcn);
  713. mk!-tria(nil,p1,p2,p3);
  714. mk!-tria(nil,p1,p3,p4);
  715. sf:=((xhi-xlo)+(yhi-ylo))*1.5/float pts;
  716. z:=p3!-plot!-refine(sf,fcn);
  717. if !*p3!-trace then
  718. for each w in z do print!-tria("zero triangle:",w);
  719. z:=p3!-plot!-collect(z);
  720. return p3!-plot!-draw(z);
  721. return nil;
  722. end;
  723. symbolic procedure p3!-plot!-refine(sflimit,fcn);
  724. begin scalar z,zn,w,s,limit2,again;
  725. integer i;
  726. limit2 := 0.15*sflimit;
  727. % In the first stage I refine all triangles until they
  728. % are fine enough for a coarse overview.
  729. again := t;
  730. phase1:
  731. z:=nil; again:=nil;
  732. for i:=p3!-triacount!* step -1 until 1 do
  733. << w := xgetv(p3!-trias!*,i);
  734. if p3!-tria!-length w > sflimit then
  735. <<again:=t; p3!-tri!-refine!-one!-tria (w,fcn)>>
  736. else if not again and p3!-tria!-zerop w
  737. then z:=car w.z;
  738. >>;
  739. if again then goto phase1;
  740. % Next I refine further only the triangles which contain a zero.
  741. % I must store in z only the numbers of triangles because these
  742. % may be modified as side effects by copying.
  743. phase2:
  744. again:=nil;
  745. for each w in z do
  746. if (s:=p3!-tria!-length (w:=xgetv(p3!-trias!*,w))) >limit2 then
  747. <<for each q in p3!-tri!-refine!-one!-tria (w,fcn) do
  748. if p3!-tria!-zerop q and not memq(car q,zn)
  749. then zn:=car q.zn;
  750. again:=t>>
  751. else if p3!-tria!-zerop w and not memq(car w,zn)
  752. then zn:=car w.zn;
  753. z:=zn; zn:=nil;
  754. if again then goto phase2;
  755. % In the third phase I refine those critical triangles futher:
  756. % non-zeros with two zero neighbours. These may be turning points
  757. % or bifurcations.
  758. phase3:
  759. for i:=p3!-triacount!* step -1 until 1 do
  760. p3!-refine!-phase3(i,i,6,fcn,limit2*0.3);
  761. % Return the final list of zeros in ascending order.
  762. z:=for i:=1:p3!-triacount!* join
  763. if p3!-tria!-zerop(w:=xgetv(p3!-trias!*,i)) then {w};
  764. return z;
  765. end;
  766. symbolic procedure p3!-refine!-phase3(i,low,lev,fn,lth);
  767. begin scalar w; integer c;
  768. if lev=0 then return nil;
  769. w:=if numberp i then xgetv(p3!-trias!*,i) else i;
  770. if car w<low or p3!-tria!-length w < lth then return nil;
  771. lev:=lev-1;
  772. for each q in p3!-neighbours w do
  773. if p3!-tria!-zerop q then c:=c+1;
  774. if p3!-tria!-zerop w
  775. then (if c eq 2 then return nil)
  776. else (if c #< 2 then return nil);
  777. for each q in p3!-tri!-refine!-one!-tria (w,fn) do
  778. p3!-refine!-phase3(q,low,lev,fn,lth);
  779. end;
  780. symbolic procedure p3!-plot!-collect(z);
  781. begin scalar lines,l,q,p,s;
  782. for each w1 in z do
  783. for each w2 in p3!-neighbours w1 do
  784. if car w2 > car w1 and p3!-tria!-zerop w2 then
  785. q:=(w1.w2) . q;
  786. lineloop:
  787. if null q then return lines;
  788. l:={caar q, (p:=cdar q)}; q:= cdr q;
  789. % first I extend the back end.
  790. while q and p do
  791. <<
  792. if(s:= atsoc(p,q)) then l:=nconc(l,{p:=cdr s}) else
  793. if(s:=rassoc(p,q)) then l:=nconc(l,{p:=car s});
  794. if s then q:=delete(s,q) else p:=nil;
  795. >>;
  796. % next I extend the front end.
  797. p:=car l;
  798. while q and p do
  799. <<
  800. if(s:=rassoc(p,q)) then l:=(p:=car s).l else
  801. if(s:= atsoc(p,q)) then l:=(p:=cdr s).l;
  802. if s then q:=delete(s,q) else p:=nil;
  803. >>;
  804. lines := l.lines;
  805. goto lineloop;
  806. end;
  807. symbolic procedure p3!-plot!-draw(l);
  808. begin scalar r,s;
  809. return
  810. for each w in l collect
  811. <<r:=nil;
  812. for each q in w join
  813. <<s:=p3!-tria!-goodpoint q;
  814. if r neq s then {r:=s}>>
  815. >>;
  816. end;
  817. symbolic procedure p3!-show!-meshes();
  818. % generate plot of meshes;
  819. begin scalar d,l,w,s,p1,p2; integer i;
  820. i:=1;
  821. loop:
  822. w:=xgetv(p3!-trias!*,i);
  823. if null w then return l;
  824. w:=cdr w;
  825. d:= {{car car w, cadr car w},
  826. {car cadr w,cadr cadr w},
  827. {car caddr w,cadr caddr w},
  828. {car car w, cadr car w}} ;
  829. while d and cdr d do
  830. <<p1:=car d; p2:=cadr d; d:=cdr d;
  831. if car p1 > car p2 then <<w:=p2;p2:=p1;p1:=w>>;
  832. s:={p1,p2};
  833. if not member(s,l) then l:=s.l
  834. >>;
  835. i:=i+1;
  836. goto loop;
  837. end;
  838. endmodule; % plotimp2
  839. module plotnum; % Numeric evaluation of algebraic expressions.
  840. fluid '(plotsynerr!* ploteval!-alist2!*);
  841. global '(!*plotinterrupts);
  842. symbolic procedure plotrounded d;
  843. % Switching rounded mode safely on and off.
  844. begin scalar o,!*protfg,c,r,!*msg;
  845. !*protfg:=t;
  846. if null d then
  847. <<c:='!*complex; r:=!*rounded;
  848. if not r and not c and dmode!* then
  849. <<o:=get(dmode!*,'dname); setdmode(o,nil)>>;
  850. o:={o,r,!*roundbf,c,precision 0};
  851. if not r then
  852. <<!*rounded:=t; setdmode('rounded,t)>>;
  853. !*roundbf:=nil;
  854. return o;
  855. >>
  856. else
  857. <<
  858. !*rounded:=cadr d;
  859. if not caddr d then
  860. <<!*roundbf:=nil; setdmode('rounded,nil)>>;
  861. if car(d) then setdmode(car d,t);
  862. precision car cddddr d;
  863. >>;
  864. end;
  865. symbolic procedure adomainp u;
  866. % numberp test in an algebraic form.
  867. numberp u or (pairp u and idp car u and get(car u,'dname))
  868. or eqcar(u,'minus) and adomainp cadr u;
  869. symbolic procedure revalnuminterval(u,num);
  870. % Evaluate u as interval; numeric bounds required if num=T.
  871. begin scalar l;
  872. if not eqcar(u,'!*interval!*) then typerr(u,"interval");
  873. l:={reval cadr u,reval caddr u};
  874. if null num or(adomainp car l and adomainp cadr l)then return l;
  875. typerr(u,"numeric interval");
  876. end;
  877. ploteval!-alist2!*:={{'x . 1},{'y . 2}};
  878. symbolic procedure plot!-form!-prep(f,x,y);
  879. % f is a REDUCE function expression depending of x and y.
  880. % Compute a form which allows me to evaluate "f(x,y)" as
  881. % a LISP expr.
  882. {'lambda,'(!&1 !&2),
  883. {'plot!-form!-call2,mkquote rdwrap f,mkquote f,
  884. mkquote x,'!&1,
  885. mkquote y,'!&2}};
  886. symbolic procedure plot!-form!-call2(ff,f,x,x0,y,y0);
  887. % Here I hack into the statically allocated a-list in order
  888. % to save cons cells.
  889. begin scalar a;
  890. a:=car ploteval!-alist2!*;
  891. car a := x; cdr a := x0;
  892. a:=cadr ploteval!-alist2!*;
  893. car a:= y; cdr a:= y0;
  894. return plotevalform(ff,f,ploteval!-alist2!*);
  895. end;
  896. % The following routines are used to transform a REDUCE algebraic
  897. % expression into a form which can be evaluated directly in LISP.
  898. symbolic procedure rdwrap f;
  899. begin scalar r,!*msg,!*protfg;
  900. !*protfg:=t;
  901. r:=errorset({'rdwrap1,mkquote f},nil,nil);
  902. return if errorp r then 'failed else car r;
  903. end;
  904. symbolic procedure rdwrap1 f;
  905. if numberp f then float f
  906. else if f='pi then 3.141592653589793238462643
  907. else if f='e then 2.7182818284590452353602987
  908. else if f='i and !*complex then rederr "i not LISP evaluable"
  909. else if atom f then f
  910. else if get(car f,'dname) then rdwrap!-dm f
  911. else if eqcar(f, 'MINUS) then
  912. begin scalar x;
  913. x := rdwrap1 cadr f;
  914. return if numberp x then minus float x else {'MINUS, x}
  915. end
  916. else if eqcar(f,'expt) then rdwrap!-expt f
  917. else rdwrap1 car f . rdwrap1 cdr f;
  918. symbolic procedure rdwrap!-dm f;
  919. % f is a domain element.
  920. if car f eq '!:RD!: then
  921. if atom cdr f then cdr f else bf2flr f
  922. else if car f eq '!:CR!: then rdwrap!-cr f
  923. else if car f eq '!:GI!: then rdwrap!-cmplex(f,float cadr f,float cddr f)
  924. else if car f eq '!:DN!: then rdwrap2 cdr f
  925. else << plotsynerr!*:=t;
  926. rerror(PLOTPACKAGE, 32, {get(car f, 'DNAME),
  927. "illegal domain for PLOT"})
  928. >>;
  929. symbolic procedure rdwrap!-cr f;
  930. begin scalar re,im;
  931. re := cadr f;
  932. if not atom re then re := bf2flr re;
  933. im := cddr f;
  934. if not atom im then im := bf2flr im;
  935. return rdwrap!-cmplx(f,re,im);
  936. end;
  937. symbolic procedure rdwrap!-cmplx(f,re,im);
  938. if abs im * 1000.0 > abs re then typerr(f,"real number") else re;
  939. symbolic procedure plotrepart u;
  940. if car u eq '!:rd!: then u else
  941. if car u memq '(!:gi!: !:cr!:) then '!:rd!: . cadr u;
  942. symbolic procedure rdwrap!-expt f;
  943. % preserve integer second argument.
  944. if fixp caddr f then {'expt!-int,rdwrap1 cadr f,caddr f}
  945. else {'expt,rdwrap1 cadr f, rdwrap caddr f};
  946. symbolic procedure rdwrap2 f;
  947. % Convert from domain to LISP evaluable value.
  948. if atom f then f else float car f * 10^cdr f;
  949. symbolic procedure rdwrap!* f;
  950. % convert a domain element to float.
  951. if null f then 0.0 else rdwrap f;
  952. symbolic procedure rdunwrap f;
  953. if f=0.0 then 0 else if f=1.0 then 1 else '!:rd!: . f;
  954. symbolic procedure expt!-int(a,b); expt(a,fix b);
  955. symbolic procedure plotevalform(ff,f,a);
  956. % ff is LISP evaluable,f REDUCE evaluable.
  957. begin scalar u,w,!*protfg,mn,r,!*msg;
  958. !*protfg := t;
  959. if ff='failed then goto non_lisp;
  960. u:= errorset({'plotevalform1,mkquote ff,mkquote a},nil,nil);
  961. if not ploterrorp u and numberp (u:=car u) then
  962. <<if abs u > plotmax!* then return plotoverflow plotmax!* else
  963. return u;
  964. >>;
  965. non_lisp:
  966. w := {'simp, mkquote
  967. sublis(
  968. for each p in a collect (car p.rdunwrap cdr p),
  969. f)};
  970. u := errorset(w,nil,nil);
  971. if ploterrorp u or (u:=car u) eq 'failed then return nil;
  972. if denr u neq 1 then return nil;
  973. u:=numr u;
  974. if numberp u then <<r:=float u; goto x>>;
  975. if not domainp u or not memq(car u,'(!:rd!: !:gi!: !:cr!:))
  976. then return nil;
  977. if evalgreaterp(plotrepart u, fl2rd plotmax!*) then
  978. return plotoverflow plotmax!* else
  979. if evalgreaterp(fl2rd (-plotmax!*),plotrepart u) then
  980. return plotoverflow (-plotmax!*);
  981. r:=errorset({'rdwrap!-dm,mkquote u},nil,nil);
  982. if errorp r or(r:=car r) eq 'failed then return nil;
  983. x: return if mn then -r else r;
  984. end;
  985. symbolic procedure plotoverflow u;
  986. <<if not !*plotoverflow then
  987. lprim "plot number range exceeded";
  988. !*plotoverflow := t;
  989. 'overflow . u
  990. >> where !*protfg = nil;
  991. symbolic procedure plotevalform0(f,a);
  992. (if ploterrorp u
  993. then typerr(f,"expression for plot") else car u)
  994. where u=
  995. errorset({'plotevalform1,mkquote f,mkquote a},nil,nil);
  996. symbolic procedure plotevalform1(f,a);
  997. begin scalar x,w;
  998. if numberp f then return float f;
  999. if (x:=assoc(f,a)) then return plotevalform1(cdr x,a);
  1000. if not atom f and flagp(car f,'plotevallisp) then
  1001. return eval
  1002. (car f . for each y in cdr f collect plotevalform1(y,a));
  1003. if not atom f then f :=
  1004. car f . for each y in cdr f collect prepf plotevalform2(y,a);
  1005. w:=simp f;
  1006. if denr w neq 1 or not domainp numr w then goto err;
  1007. return rdwrap!* numr w;
  1008. err:
  1009. plotsynerr!*:=t;
  1010. typerr(prepsq w,"numeric value");
  1011. end;
  1012. symbolic procedure plotevalform2(f,a);
  1013. begin scalar x,w;
  1014. if fixp f then return f;
  1015. if floatp f then return rdunwrap f;
  1016. if (x:=assoc(f,a)) then return plotevalform2(cdr x,a);
  1017. if not atom f and flagp(car f,'plotevallisp) then
  1018. return rdunwrap eval
  1019. (car f . for each y in cdr f collect plotevalform1(y,a));
  1020. if not atom f then f :=
  1021. car f . for each y in cdr f collect prepf plotevalform2(y,a);
  1022. w:=simp f;
  1023. if denr w neq 1 or not domainp numr w then goto err;
  1024. return numr w;
  1025. err:
  1026. plotsynerr!*:=t;
  1027. typerr(prepsq w,"numeric value");
  1028. end;
  1029. flag('(plus difference times quotient exp expt expt!-int minus
  1030. sin cos tan cot asin acos acot atan log),'plotevallisp);
  1031. symbolic procedure ploterrorp u;
  1032. if u member !*plotinterrupts
  1033. then rederr prin2 "***** plot killed"
  1034. else atom u or cdr u;
  1035. endmodule;
  1036. module xvect; % Support for vectors with adaptive length.
  1037. % Author: Herbert Melenk, ZIB-Berlin.
  1038. symbolic procedure mkxvect(); {mkvect(1024)};
  1039. symbolic procedure xputv(v,n,w);
  1040. begin scalar i,j;
  1041. i:=iquotient(n,1024); j:=iremainder(n,1024);
  1042. while(i>0) do
  1043. <<if null cdr v then cdr v:= mkxvect();
  1044. v:=cdr v; i:=i #- 1>>;
  1045. iputv(car v,j,w);
  1046. return w;
  1047. end;
  1048. symbolic procedure xgetv(v,n);
  1049. begin scalar i,j,w;
  1050. i:=iquotient(n,1024); j:=iremainder(n,1024);
  1051. while(i>0 and v) do
  1052. <<v:=cdr v; i:=i #- 1>>;
  1053. w:=if v then igetv(car v,j);
  1054. return w
  1055. end;
  1056. endmodule;
  1057. module gnuplot; % REDUCE interace for gnuplot graphics.
  1058. create!-package('(gnuplot),nil);
  1059. global '(!*plotusepipe !*plotpause plotdta!* plotdta!*2 plotmax!*
  1060. plotmin!* plotcmds!* plotcommand!* plotheader!*
  1061. plotcleanup!* plottmp!* lispsystem!* !*plotinterrupts
  1062. plotoptions!* plotdriver!*);
  1063. fluid '(plotstyle!*);
  1064. plotdriver!*:='gnuplot;
  1065. put('gnuplot,'do,'gp!-do);
  1066. put('gnuplot,'option,'gp!-option);
  1067. !#if (member 'psl lispsystem!*)
  1068. !#if (member 'unix lispsystem!*,
  1069. in "$reduce/plot/rgpunx.red"$
  1070. !#elif (intersection '(dos os2 winnt alphant) lispsystem!*)
  1071. in "$reduce\plot\rgpwin.red"$
  1072. !#elif (member 'vms lispsystem!*)
  1073. in "$reduce/plot/rgpvms.red"$
  1074. !#endif
  1075. !#elif (member 'csl lispsystem!*)
  1076. in "../cslsrc/rgpcsl.red"$
  1077. !#endif
  1078. endmodule;
  1079. %==================================================================
  1080. module gnupldrv; % main GNUPLOT driver.
  1081. % Author: Herbert Melenk.
  1082. global '(!*plotusepipe !*trplot !*plotkeep !*plotrefine
  1083. !*plotinterrupts);
  1084. switch plotusepipe; % use pipes
  1085. switch trplot; % list Gnuplot commands to REDUCE
  1086. % output (e.g. screen).
  1087. switch plotkeep; % if ON, the command and data files are
  1088. % not erased after calling Gnuplot.
  1089. global '(
  1090. !*plotpause % Gnuplot pause command at the end:
  1091. % nil: no pause
  1092. % -1: Gnuplot will ask the user for
  1093. % a return key.
  1094. % number>0: Gnuplot will wait <number>
  1095. % seconds.
  1096. plotcommand!* % string: command to start gnuplot
  1097. plotcmds!* % file for collecting commands
  1098. plotdta!* % files for collecting data
  1099. plotheader!* % list of Gnuplot commands (strings)
  1100. % for initializing GNUPLOT
  1101. plotcleanup!* % list of system commands (strings)
  1102. % for cleaning up after gnuplot
  1103. );
  1104. if null plotcommand!* then rederr
  1105. " no support of GNUPLOT for this installation";
  1106. fluid '(plot!-files!* plotpipe!*);
  1107. symbolic procedure gp!-init();
  1108. <<
  1109. plot!-files!* := plotdta!*;
  1110. plotoptions!*:= nil;
  1111. PlotOpenDisplay();
  1112. >>;
  1113. put('gnuplot,'init,'gp!-init);
  1114. symbolic procedure plot!-filename();
  1115. <<plot!-files!* := cdr plot!-files!*; u>>
  1116. where u=if null plot!-files!* then
  1117. rederr "ran out of scratch files" else car plot!-files!*;
  1118. symbolic procedure gp!-reset();
  1119. if !*plotusepipe and plotpipe!* then
  1120. << plotprin2 "exit"; plotterpri();
  1121. close plotpipe!*; plotpipe!*:=nil;>>;
  1122. put('gnuplot,'reset,'gp!-reset);
  1123. symbolic procedure PlotOpenDisplay();
  1124. begin
  1125. if null plotpipe!* then
  1126. if not !*plotusepipe then plotpipe!* := open(plotcmds!*,'output)
  1127. else <<plotpipe!* :=pipe!-open(plotcommand!*,'output)>>;
  1128. if null plotheader!* then nil else
  1129. if atom plotheader!* then <<plotprin2 plotheader!*; plotterpri()>>
  1130. else if eqcar(plotheader!*,'list) then
  1131. for each x in cdr plotheader!* do <<plotprin2 x; plotterpri()>>
  1132. else typerr(plotheader!*,"gnuplot header");
  1133. end;
  1134. symbolic procedure gp!-show();
  1135. if !*plotusepipe and plotpipe!* then
  1136. << channelflush plotpipe!*; >>
  1137. else
  1138. <<if !*plotpause then plotprin2lt{"pause ",!*plotpause};
  1139. close plotpipe!*;
  1140. plotpipe!* := nil;
  1141. if plotcommand!* then
  1142. <<plot!-exec plotcommand!*;
  1143. if not !*plotkeep then
  1144. for each u in plotcleanup!* do system u;
  1145. >>;
  1146. >>;
  1147. put('gnuplot,'show,'gp!-show);
  1148. symbolic procedure plot!-exec u; system u;
  1149. symbolic procedure plotprin2 u;
  1150. <<prin2 u; wrs v;
  1151. if !*trplot then prin2 u>> where v=wrs plotpipe!*,!*lower=t;
  1152. symbolic procedure plotterpri();
  1153. <<terpri(); wrs v;
  1154. if !*trplot then terpri() >> where v=wrs plotpipe!*;
  1155. symbolic procedure plotprin2lt l;
  1156. <<for each x in l do plotprin2 x; plotterpri()>>;
  1157. fluid '(plotprinitms!*);
  1158. symbolic procedure plotprinexpr u;
  1159. begin scalar plotprinitms!*,!*lower,v;
  1160. !*lower:=t;
  1161. v := wrs plotpipe!*;
  1162. plotprinitms!* := 0;
  1163. if eqcar(u,'file) then
  1164. <<prin2 '!"; prin2 cadr u;prin2 '!"; prin2 " ">>
  1165. else
  1166. errorset(list('plotprinexpr1,mkquote u,nil),nil,nil);
  1167. wrs v;
  1168. end;
  1169. symbolic procedure plotprinexpr1(u,oldop);
  1170. begin scalar op;
  1171. if plotprinitms!* > 5 then
  1172. <<prin2 "\"; terpri(); plotprinitms!*:=0>>;
  1173. if atom u then
  1174. <<prin2 if u='e then 2.718281 else
  1175. if u='pi then 3.14159 else u;
  1176. plotprinitms!* := plotprinitms!*+1>>
  1177. else
  1178. if eqcar(u,'!:rd!:) then
  1179. plotprinexpr1 (if atom cdr u then cdr u else
  1180. bf2flr u,nil)
  1181. else
  1182. if (op:=car u) memq '(plus times difference quotient expt) then
  1183. plotprinexpr2(cdr u,get(car u,'PRTCH),
  1184. oldop and not (op memq(oldop memq
  1185. '(difference plus times quotient expt)))
  1186. ,op)
  1187. else
  1188. if op='MINUS then
  1189. <<prin2 "(-";
  1190. plotprinexpr1(cadr u,t);
  1191. prin2 ")">>
  1192. else
  1193. if get(car u,'!:RD!:) then
  1194. <<prin2 car u; plotprinexpr2(cdr u,'!, ,t,nil)>>
  1195. else
  1196. typerr(u," expression for printing")
  1197. end;
  1198. symbolic procedure plotprinexpr2(u,sep,br,op);
  1199. <<if br then prin2 " (";
  1200. while u do
  1201. <<plotprinexpr1(car u,op);
  1202. u := cdr u;
  1203. if u then prin2 sep>>;
  1204. if br then prin2 ") "
  1205. >>;
  1206. symbolic procedure gnuploteval u;
  1207. % Support of explicit calls to GNUPLOT in algebraic mode.
  1208. begin scalar m,evallhseqp!*;
  1209. evallhseqp!* := t;
  1210. m:=plotrounded(nil);
  1211. PlotOpenDisplay();
  1212. for each v in u do
  1213. <<plotprinexpr reval v; plotprin2 " ">>;
  1214. plotterpri();
  1215. plotrounded(m);
  1216. end;
  1217. put('gnuplot,'psopfn,'gnuploteval);
  1218. % Declare options which are supported by GNUPLOT:
  1219. flag ('(
  1220. % keyword options
  1221. contour nocontour logscale nologscale surface nosurface
  1222. % equation type options
  1223. hidden3d xlabel ylabel zlabel title
  1224. ),'gp!-option);
  1225. put('gnuplot,'option,'gp!-option);
  1226. symbolic procedure plotpoints u;
  1227. begin scalar f,fn,of,dim,w;
  1228. fn := plot!-filename();
  1229. f := open(fn,'output);
  1230. of := wrs f;
  1231. w:={'plotpoints0,mkquote(nil.u)};
  1232. dim:=errorset(w,t,nil);
  1233. wrs of;
  1234. close f;
  1235. if ploterrorp dim then
  1236. rederr "failure during plotting point set";
  1237. return if car dim=2 then {'file,fn,'x} else {'file,fn,'x,'y};
  1238. end;
  1239. symbolic procedure plotpoints0 u;
  1240. begin scalar z,bool;
  1241. integer n;
  1242. for each x in cdr u do
  1243. if not bool and eqcar(x,'list) then n:=plotpoints0 x
  1244. else
  1245. <<bool:=t; n:=n#+1;
  1246. z:=rdwrap reval x;
  1247. if not numberp z then <<wrs nil; typerr(x,"number")>>;
  1248. prin2 z; prin2 " ";
  1249. >>;
  1250. terpri();
  1251. return n;
  1252. end;
  1253. symbolic procedure plotpoints1 u;
  1254. begin scalar f,fn,of,y;
  1255. fn := plot!-filename();
  1256. f := open(fn,'output);
  1257. of := wrs f;
  1258. for each x in u do
  1259. <<for each y in x do
  1260. << if null y or nil member y then t else
  1261. for each z in y do <<plotprin2number z; prin2 " ">>;
  1262. terpri()
  1263. >>;
  1264. terpri();
  1265. >>;
  1266. wrs of;
  1267. close f;
  1268. return fn;
  1269. end;
  1270. symbolic procedure plotprin2number u;
  1271. prin2 if floatp u and abs u < plotmin!* then "0.0" else u;
  1272. flag ('(xlabel ylabel zlabel),'plotstring);
  1273. symbolic procedure gp!-plotoptions();
  1274. <<if not('polar memq plotoptions!*)then
  1275. plotoptions!* := 'nopolar . plotoptions!*;
  1276. if not('contour memq plotoptions!*)then
  1277. plotoptions!* := 'nocontour . plotoptions!*;
  1278. for each x in plotoptions!* do
  1279. <<plotprin2 "set ";
  1280. if idp x then plotprin2 x else
  1281. <<plotprin2 car x;
  1282. plotprin2 " ";
  1283. if flagp(car x,'plotstring) then plotprin2 '!";
  1284. plotprin2 cdr x;
  1285. if flagp(car x,'plotstring) then plotprin2 '!">>;
  1286. plotterpri()
  1287. >>;
  1288. >>;
  1289. symbolic procedure plotstyle1();
  1290. if plotstyle!* then
  1291. <<plotprin2 " \";
  1292. plotterpri();
  1293. plotprin2 " with ";
  1294. plotprin2 plotstyle!*;
  1295. plotprin2 " ";
  1296. >>;
  1297. symbolic procedure plottitle option;
  1298. <<plotprin2 "set title ";
  1299. plotprin2 '!";
  1300. plotprin2 option;
  1301. plotprin2 '!";
  1302. plotterpri()>>;
  1303. put('title,'gp!-do,'plottitle);
  1304. symbolic procedure plotstyle option;
  1305. if option memq'(lines points linespoints impulses dots errorbars)
  1306. then plotstyle!* := option
  1307. else typerr(caddr option, "plot style option");
  1308. put('style,'gp!-do,'plotstyle);
  1309. % Drivers for different picture types.
  1310. symbolic procedure gp!-2exp(x,y,pts,fp);
  1311. % x: name of x coordinate,
  1312. % y: name of y coordinate,
  1313. % pts: list of computed point sets,
  1314. % fp: list of user supplied point sets.
  1315. begin scalar cm;
  1316. plotoptions!* := 'noparametric . plotoptions!*;
  1317. plotprin2lt{"set size 1,1"};
  1318. plotprin2lt{"set xlabel ",'!",x,'!"};
  1319. plotprin2lt{"set ylabel ",'!",y,'!"};
  1320. gp!-plotoptions();
  1321. plotprin2lt{"set nokey"};
  1322. if pts or fp then plotprin2 "plot ";
  1323. for each f in reversip pts do
  1324. << if cm then <<plotprin2 ",\"; plotterpri()>>;
  1325. plotprin2 "'"; plotprin2 plotpoints1 f; plotprin2 "'";
  1326. plotprin2 " with lines"; cm:=t;
  1327. >>;
  1328. if fp then
  1329. << if cm then <<plotprin2 ",\"; plotterpri()>>;
  1330. plotprin2 "'"; plotprin2 fp; plotprin2 "'";
  1331. plotprin2 if cm then " with points" else " with lines";
  1332. >>;
  1333. plotterpri();
  1334. end;
  1335. put('gnuplot,'plot!-2exp,'gp!-2exp);
  1336. symbolic procedure gp!-3exp(x,y,z,f);
  1337. % x: name of x coordinate,
  1338. % y: name of y coordinate,
  1339. % z: name of z coordinate,
  1340. % f: orthogonal list of point lists.
  1341. begin scalar h;
  1342. h:=member('hidden3d,plotoptions!*);
  1343. if h then f:=for each l in f collect
  1344. for each p in l collect {caddr p};
  1345. f:=plotpoints1 f;
  1346. plotprin2lt{"set nohidden3d"};
  1347. if not h then plotoptions!* := 'parametric .
  1348. delete('noparametric,plotoptions!*)
  1349. else
  1350. plotoptions!* := 'noparametric .
  1351. delete('parametric,plotoptions!*);
  1352. plotprin2lt{"set view 60,30,1,1"};
  1353. plotprin2lt{"set size 1,1"};
  1354. if h then plotprin2lt{"set format xy ",'!",'!"};
  1355. plotprin2lt{"set xlabel ",'!",x,'!"};
  1356. plotprin2lt{"set ylabel ",'!",y,'!"};
  1357. plotprin2lt{"set zlabel ",'!",z,'!"};
  1358. gp!-plotoptions();
  1359. plotprin2lt{"set nokey"};
  1360. plotprin2 "splot ";
  1361. plotprin2 "'"; plotprin2 f; plotprin2 "'";
  1362. plotprin2 " with lines ";
  1363. plotterpri();
  1364. plotprin2lt{"set nohidden3d"};
  1365. plotprin2lt{"set format xy"};
  1366. end;
  1367. put('gnuplot,'plot!-3exp!-reg,'gp!-3exp);
  1368. symbolic procedure gp!-2imp(x,y,l,g,xmin,xmax,ymin,ymax);
  1369. % x,y: names of coordinates,
  1370. % l: point lists for funtion,
  1371. % g: nil or point lists for grid,
  1372. % xmin..ymax: minimum and maximum coordinate values.
  1373. begin scalar f,q;
  1374. q:={{xmin,ymin},nil,{xmin,ymax},nil,
  1375. {xmax,ymin},nil,{xmax,ymax}};
  1376. plotoptions!* := 'noparametric . plotoptions!*;
  1377. f:=plotpoints1 (q.l);
  1378. plotprin2lt{"set size 1,1"};
  1379. plotprin2lt{"set xlabel ",'!",x,'!"};
  1380. plotprin2lt{"set ylabel ",'!",y,'!"};
  1381. gp!-plotoptions();
  1382. plotprin2lt{"set nokey"};
  1383. plotprin2 "plot "; plotprin2 "'"; plotprin2 f; plotprin2 "'";
  1384. plotprin2 " with lines";
  1385. if g then
  1386. <<plotprin2 ", '"; plotprin2 plotpoints1 g;
  1387. plotprin2 "' with lines";
  1388. >>;
  1389. plotterpri();
  1390. end;
  1391. put('gnuplot,'plot!-2imp,'gp!-2imp);
  1392. endmodule;
  1393. end;