plotter.c 99 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776
  1. /* This file is part of the GNU plotutils package. Copyright (C) 1989,
  2. 1990, 1991, 1995, 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free
  3. Software Foundation, Inc.
  4. The GNU plotutils package is free software. You may redistribute it
  5. and/or modify it under the terms of the GNU General Public License as
  6. published by the Free Software foundation; either version 2, or (at your
  7. option) any later version.
  8. The GNU plotutils package is distributed in the hope that it will be
  9. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License along
  13. with the GNU plotutils package; see the file COPYING. If not, write to
  14. the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
  15. Boston, MA 02110-1301, USA. */
  16. /* This file contains the point plotter half of GNU graph. The point
  17. plotter could easily be linked with other software. It translates a
  18. sequence of points, regarded as defining a polyline or a sequence of
  19. polylines, to a sequence of libplot calls. There is support for
  20. multigraphing, i.e. producing a plot consisting of more than a single
  21. graph. Each graph may be drawn from more than one file, i.e., input
  22. stream, and each input stream may provide more than a single polyline.
  23. A `point' is a structure. Each point structure contains the following
  24. fields:
  25. x and y coordinates of the point
  26. a `have_x_errorbar' flag (true or false)
  27. a `have_y_errorbar' flag (true or false)
  28. xmin and xmax (meaningful only if have_x_errorbar is set)
  29. ymin and ymax (meaningful only if have_y_errorbar is set)
  30. a `pendown' flag
  31. a symbol type (a small integer, interpreted as a marker type)
  32. a symbol size (a fraction of the size of the plotting box)
  33. a symbol font name (relevant only for symbol types >= 32)
  34. a linemode (a small integer; line style unless line_color is empty)
  35. a line_color (a string, overrides linemode color unless empty)
  36. a linewidth (a fraction of the size of the display device)
  37. a polyline fill-fraction (in the interval [0,1], <0 means no fill)
  38. a use_color flag (true or false)
  39. The point plotter constructs a polyline from each successive run of
  40. points that have the pendown flag set. It assumes that the final seven
  41. fields are assumed to be the same for each point in such a run, i.e., it
  42. takes their values from the first point of the run. At the location of
  43. each point on the polyline, the appropriate marker symbol, if any, will
  44. be plotted. Symbol types greater than or equal to 32 are interpreted as
  45. single characters to be plotted, rather than symbols.
  46. Points without the pendown flag set cause the polyline to be broken, and
  47. a new one to begin, before the symbol (if any) is plotted.
  48. The plotter supports five basic linemodes: 1 through 5. The
  49. interpretation of `linemode' depends on the polyline's use_color flag.
  50. linemode If monochrome If color
  51. 1 solid red
  52. 2 dotted green
  53. 3 dotdashed blue
  54. 4 shortdashed magenta
  55. 5 longdashed cyan
  56. In the monochrome case, the pattern simply repeats: 6,7,8,9,10 are
  57. equivalent to 1,2,3,4,5, etc. In the colored case, the sequence of
  58. colors also repeats. But linemodes 1,2,3,4,5 are drawn solid, while
  59. 6,7,8,9,10 are drawn dotted, 11,12,13,14,15 are drawn dotdashed, etc.
  60. So there are 25 distinct colored linemodes, and 5 distinct monochrome
  61. (black) ones.
  62. The color of a symbol will be the same as the color of the polyline on
  63. which it is plotted.
  64. linemodes -1, -2, etc. have a special interpretation. They are
  65. `disconnected' linemodes: no polyline will appear, but if color is
  66. being used, the color of the plotted symbols (if any) will be
  67. linemode-dependent. -1,-2,-3,-4,5 signify red,green,blue,magenta,cyan
  68. (the same sequence as for 1,2,3,4,5); thereafter the sequence repeats.
  69. linemode 0 is special (for backward compatibility). No line is drawn;
  70. symbol #1 (a point) will be used. So using linemode 0 is the same as
  71. using linemode -1, symbol 1.
  72. The point plotter is invoked by calling the following, in order.
  73. new_multigrapher() creates a new point plotter.
  74. begin_graph()
  75. set_graph_parameters() initializes global structures used by
  76. the draw_frame_of_graph() and plot_point() routines. These include
  77. the structures that specify the linear transformation from user
  78. coordinates to the coordinates used by libplot, and structures
  79. that specify the style of the plot frame.
  80. draw_frame_of_graph() plots the graph frame. [Optional.]
  81. plot_point() uses libplot routines to plot a single point, together
  82. with (possibly) a line extending to it from the last point, and
  83. a symbol. [Alternatively, plot_point_array() can be used, to plot
  84. an array of points.]
  85. end_graph()
  86. ..
  87. [The begin_graph()..end_graph() block can be repeated indefinitely
  88. if desired, to create a multigraph. set_graph_parameters() allows
  89. for repositioning of later graphs.]
  90. ..
  91. delete_multigrapher() deletes the point plotter.
  92. There is also a function end_polyline_and_flush(), which is useful for
  93. real-time display. */
  94. #include "sys-defines.h"
  95. #include "libcommon.h"
  96. #include "plot.h"
  97. #include "extern.h"
  98. /* we use floating point libplot coordinates in the range [0,PLOT_SIZE] */
  99. #define PLOT_SIZE 4096.0
  100. #define FUZZ 0.000001 /* bd. on floating pt. roundoff error */
  101. #define NEAR_EQUALITY(a, b, scale) (fabs((a) - (b)) < (FUZZ * fabs(scale)))
  102. typedef unsigned int outcode; /* for Cohen-Sutherland clipper */
  103. enum { TOP = 0x1, BOTTOM = 0x2, RIGHT = 0x4, LEFT = 0x8 };
  104. enum { ACCEPTED = 0x1, CLIPPED_FIRST = 0x2, CLIPPED_SECOND = 0x4 };
  105. #define TRIAL_NUMBER_OF_TICK_INTERVALS 5
  106. #define MAX_NUM_SUBTICKS 29 /* max num. of linearly spaced subticks */
  107. #define RELATIVE_SUBTICK_SIZE 0.4 /* subtick_size / tick_size */
  108. /* if a log axis spans >5.0 orders of magnitude, don't plot log subsubticks */
  109. #define MAX_DECADES_WITH_LOG_SUBSUBTICKS 5.0
  110. /* inter-tick spacing types, returned by scale1() and spacing_type() */
  111. #define S_ONE 0
  112. #define S_TWO 1
  113. #define S_FIVE 2
  114. #define S_TWO_FIVE 3 /* we don't use this one, but user may request it */
  115. #define S_UNKNOWN -2
  116. /* valid graph axis layout types; A_LOG2, anyone? */
  117. #define A_LINEAR 0
  118. #define A_LOG10 1
  119. /* The x_trans and y_trans elements of a Multigrapher specify the current
  120. linear transformation from user coordinates to device coordinates. They
  121. are used both in the plotting of a graph frame, and in the plotting of
  122. data points within a graph. */
  123. typedef struct
  124. {
  125. /* Input (user) coordinates, all floating point. These are the
  126. coordinates used in the original data points (or their base-10 logs,
  127. for an axis of log type). We'll map them to the unit interval
  128. [0.0,1.0]. */
  129. double input_min, input_max; /* min, max */
  130. double input_range; /* max - min, precomputed for speed */
  131. /* If we're reversing axes, we'll then map [0.0,1.0] to [1.0,0.0] */
  132. bool reverse;
  133. /* We'll map [0.0,1.0] to another (smaller) interval, linearly */
  134. double squeezed_min, squeezed_max; /* min, max */
  135. double squeezed_range; /* max - min */
  136. /* Output [i.e., libplot] coordinates. The interval [0.0,1.0] will be
  137. mapped to this range, and the squeezed interval to a sub-range. This
  138. is so that the box within which points are plotted will be smaller
  139. than the full area of the graphics display. */
  140. double output_min, output_max; /* min */
  141. double output_range; /* max - min */
  142. } Transform;
  143. /* Affine transformation macros */
  144. /* X Scale: convert from user x value to normalized x coordinate (floating
  145. point, 0.0 to 1.0), and inverse. */
  146. #define XS(x) (((x) - multigrapher->x_trans.input_min)/multigrapher->x_trans.input_range)
  147. #define XSI(x) (((x)*multigrapher->x_trans.input_range) + multigrapher->x_trans.input_min)
  148. /* X Reflect: map [0,1] to [1,0], if that's called for */
  149. #define XR(x) (multigrapher->x_trans.reverse ? 1.0 - (x) : (x))
  150. /* X Squeeze: map [0,1] range for normalized x coordinate into a smaller
  151. interval, the x range for the plotting area within the graphics display */
  152. #define XSQ(x) (multigrapher->x_trans.squeezed_min + (x) * multigrapher->x_trans.squeezed_range)
  153. #define XSQI(x) (((x) - multigrapher->x_trans.squeezed_min)/multigrapher->x_trans.squeezed_range)
  154. /* X Plot: convert from normalized x coordinate to floating point libplot
  155. coordinate. */
  156. #define XP(x) (multigrapher->x_trans.output_min + (x) * multigrapher->x_trans.output_range)
  157. #define XPI(x) (((x) - multigrapher->x_trans.output_min)/multigrapher->x_trans.output_range)
  158. /* X Value: convert from user x value (floating point) to floating point
  159. libplot coordinate. */
  160. #define XV(x) XP(XSQ(XR(XS(x))))
  161. #define XVI(x) XSI(XR(XSQI(XPI(x))))
  162. /* X Normalize: the same, but do not perform reflection if any. (We use
  163. this for plotting of axes and their labels.) */
  164. #define XN(y) XP(XSQ(XS(y)))
  165. /* Y Scale: convert from user y value to normalized y coordinate (floating
  166. point, 0.0 to 1.0). */
  167. #define YS(y) (((y) - multigrapher->y_trans.input_min)/multigrapher->y_trans.input_range)
  168. #define YSI(y) (((y)*multigrapher->y_trans.input_range) + multigrapher->y_trans.input_min)
  169. /* Y Reflect: map [0,1] to [1,0], if that's called for */
  170. #define YR(y) (multigrapher->y_trans.reverse ? 1.0 - (y) : (y))
  171. /* Y Squeeze: map [0,1] range for normalized y coordinate into a smaller
  172. interval, the y range for the plotting area within the graphics display */
  173. #define YSQ(y) (multigrapher->y_trans.squeezed_min + (y) * multigrapher->y_trans.squeezed_range)
  174. #define YSQI(y) (((y) - multigrapher->y_trans.squeezed_min)/multigrapher->y_trans.squeezed_range)
  175. /* Y Plot: convert from normalized y coordinate to floating point libplot
  176. coordinate. */
  177. #define YP(y) (multigrapher->y_trans.output_min + (y) * multigrapher->y_trans.output_range)
  178. #define YPI(y) (((y) - multigrapher->y_trans.output_min)/multigrapher->y_trans.output_range)
  179. /* Y Value: convert from user y value (floating point) to floating point
  180. libplot coordinate. (We use this for plotting of points.) */
  181. #define YV(y) YP(YSQ(YR(YS(y))))
  182. #define YVI(x) YSI(YR(YSQI(YPI(x))))
  183. /* Y Normalize: the same, but do not perform reflection if any. (We use
  184. this for plotting of axes and their labels.) */
  185. #define YN(y) YP(YSQ(YS(y)))
  186. /* Size Scale: convert distances, or sizes, from normalized coors to
  187. libplot coordinates. (Used for tick, symbol, and font sizes.) The min
  188. should really be precomputed. */
  189. #define SS(x) \
  190. (DMIN(multigrapher->x_trans.output_range * multigrapher->x_trans.squeezed_range, \
  191. multigrapher->y_trans.output_range * multigrapher->y_trans.squeezed_range) * (x))
  192. /* The `x_axis' and `y_axis' elements of a Multigrapher, which are of type
  193. `Axis', specify the layout of the two axes of a graph. They are used in
  194. the drawing of a graph frame. All elements that are doubles are
  195. expressed in user coordinates (unless the axis is logarithmic, in which
  196. case logs are taken before this structure is filled in). */
  197. /* The `x_axis' and `y_axis' elements are filled in by calls to
  198. prepare_axis() when set_graph_parameters() is called. The only
  199. exceptions to this are the elements `max_width' and `non_user_ticks',
  200. which are filled in by draw_frame_of_graph(), as the frame for a graph
  201. is being drawn. */
  202. typedef struct
  203. {
  204. const char *font_name; /* fontname for axis label and tick labels */
  205. double font_size; /* font size for axis label and tick labels */
  206. const char *label; /* axis label (a string) */
  207. int type; /* axis layout type (A_LINEAR or A_LOG10) */
  208. double tick_spacing; /* distance between ticks */
  209. int min_tick_count, max_tick_count; /* tick location = count * spacing */
  210. bool have_lin_subticks; /* does axis have linearly spaced subticks? */
  211. double lin_subtick_spacing; /* distance between linearly spaced subticks */
  212. int min_lin_subtick_count, max_lin_subtick_count;
  213. bool have_normal_subsubticks; /* does axis have logarithmic subsubticks?*/
  214. bool user_specified_subsubticks; /* axis has user-spec'd subsubticks? */
  215. double subsubtick_spacing; /* spacing for user-specified ones */
  216. double other_axis_loc; /* location of intersection w/ other axis */
  217. double alt_other_axis_loc; /* alternative loc. (e.g. right end vs. left)*/
  218. bool switch_axis_end; /* other axis at right/top, not left/bottom? */
  219. bool omit_ticks; /* just plain omit them (and their labels) ? */
  220. double max_label_width; /* max width of labels placed on axis, in
  221. libplot coors (we update this during
  222. plotting, for y axis only) */
  223. int labelled_ticks; /* number of labelled ticks, subticks, and
  224. subsubticks drawn on the axis
  225. (we update this during plotting, so we
  226. can advise the user to specify a tick
  227. spacing by hand if labelled_ticks <= 2) */
  228. } Axis;
  229. /* The Multigrapher structure. A pointer to one of these is passed as the
  230. first argument to each Multigrapher method (e.g., plot_point()). */
  231. struct MultigrapherStruct
  232. {
  233. /* multigrapher parameters (not updated over a multigrapher's lifetime) */
  234. plPlotter *plotter; /* GNU libplot Plotter handle */
  235. const char *output_format; /* type of libplot device driver [unused] */
  236. const char *bg_color; /* color of background, if non-NULL */
  237. bool save_screen; /* erase display when opening plotter? */
  238. /* graph parameters (constant over any single graph) */
  239. Transform x_trans, y_trans; /* user->device coor transformations */
  240. Axis x_axis, y_axis; /* information on each axis */
  241. grid_type grid_spec; /* frame specification */
  242. double blankout_fraction; /* 1.0 means blank whole box before drawing */
  243. bool no_rotate_y_label; /* useful for pre-X11R6 X servers */
  244. double tick_size; /* fractional tick size */
  245. double subtick_size; /* fractional subtick size (for linear axes) */
  246. double frame_line_width; /* fractional width of lines in the frame */
  247. double half_line_width; /* approx. half of this, in libplot coors */
  248. const char *frame_color; /* color for frame (and graph, if no -C) */
  249. const char *title; /* graph title */
  250. const char *title_font_name; /* font for graph title */
  251. double title_font_size; /* fractional height of graph title */
  252. int clip_mode; /* 0, 1, or 2 (cf. clipping in gnuplot) */
  253. /* following elements are updated during plotting of points; they're the
  254. chief repository for internal state */
  255. bool first_point_of_polyline; /* true only at beginning of each polyline */
  256. double oldpoint_x, oldpoint_y; /* last-plotted point */
  257. int symbol; /* symbol being plotted at each point */
  258. int linemode; /* linemode used for polyline */
  259. };
  260. /* forward references */
  261. static int clip_line (Multigrapher *multigrapher, double *x0_p, double *y0_p, double *x1_p, double *y1_p);
  262. static int spacing_type (double spacing);
  263. static outcode compute_outcode (Multigrapher *multigrapher, double x, double y, bool tolerant);
  264. static void plot_abscissa_log_subsubtick (Multigrapher *multigrapher, double xval);
  265. static void plot_errorbar (Multigrapher *multigrapher, const Point *p);
  266. static void plot_ordinate_log_subsubtick (Multigrapher *multigrapher, double xval);
  267. static void prepare_axis (Axis *axisp, Transform *trans, double min, double max, double spacing, const char *font_name, double font_size, const char *label, double subsubtick_spacing, bool user_specified_subsubticks, bool round_to_next_tick, bool log_axis, bool reverse_axis, bool switch_axis_end, bool omit_ticks);
  268. static void print_tick_label (char *labelbuf, const Axis *axis, const Transform *transform, double val);
  269. static void scale1 (double min, double max, double *tick_spacing, int *tick_spacing_type);
  270. static void set_line_style (Multigrapher *multigrapher, int style, const char *line_color, bool use_color);
  271. static void transpose_portmanteau (int *val);
  272. /* print_tick_label() prints a label on an axis tick. The format depends
  273. * on whether the axis is a log axis or a linear axis; also, the magnitude
  274. * of the axis labels.
  275. */
  276. static void
  277. print_tick_label (char *labelbuf, const Axis *axis, const Transform *transform, double val)
  278. {
  279. int prec;
  280. char *eloc, *ptr;
  281. char labelbuf_tmp[64], incrbuf[64];
  282. double spacing;
  283. bool big_exponents;
  284. double min, max;
  285. /* two possibilities: large/small exponent magnitudes */
  286. min = (axis->type == A_LOG10
  287. ? pow (10.0, transform->input_min) : transform->input_min);
  288. max = (axis->type == A_LOG10
  289. ? pow (10.0, transform->input_max) : transform->input_max);
  290. big_exponents = (((min != 0.0 && fabs (log10 (fabs (min))) >= 4.0)
  291. || (max != 0.0 && fabs (log10 (fabs (max))) >= 4.0))
  292. ? true : false);
  293. if (big_exponents)
  294. /* large exponents, rewrite as foo x 10^bar, using escape sequences */
  295. {
  296. char *src = labelbuf_tmp, *dst = labelbuf;
  297. int exponent;
  298. char floatbuf[64];
  299. char *fptr = floatbuf;
  300. double prefactor;
  301. sprintf (labelbuf_tmp, "%e", val);
  302. if ((eloc = strchr (labelbuf_tmp, (int)'e')) == NULL)
  303. return;
  304. if (axis->type == A_LOG10 && !axis->user_specified_subsubticks)
  305. /* a hack: this must be a power of 10, so just print "10^bar" */
  306. {
  307. sscanf (++eloc, "%d", &exponent);
  308. sprintf (dst, "10\\sp%d\\ep", exponent);
  309. return;
  310. }
  311. /* special case: zero prints as `0', not 0.0x10^whatever */
  312. if (val == 0.0)
  313. {
  314. *dst++ = '0';
  315. *dst = '\0';
  316. return;
  317. }
  318. while (src < eloc)
  319. *fptr++ = *src++;
  320. *fptr = '\0';
  321. sscanf (floatbuf, "%lf", &prefactor); /* get foo */
  322. sscanf (++src, "%d", &exponent); /* get bar */
  323. spacing = (axis->type == A_LINEAR
  324. ? axis->tick_spacing
  325. : axis->subsubtick_spacing); /* user-specified, for log axis*/
  326. sprintf (incrbuf, "%f",
  327. spacing / pow (10.0, (double)exponent));
  328. ptr = strchr (incrbuf, (int)'.');
  329. prec = 0;
  330. if (ptr != NULL)
  331. {
  332. int count = 0;
  333. while (*(++ptr))
  334. {
  335. count++;
  336. if (*ptr != '0')
  337. prec = count;
  338. }
  339. }
  340. /* \sp ... \ep is start_superscript ... end_superscript, and \r6 is
  341. right-shift by 1/6 em. \mu is the `times' character. */
  342. sprintf (dst, "%.*f\\r6\\mu10\\sp%d\\ep",
  343. prec, prefactor, exponent);
  344. return;
  345. }
  346. else /* small-size exponent magnitudes */
  347. {
  348. if (axis->type == A_LOG10 && !axis->user_specified_subsubticks)
  349. /* a hack: this must be a (small) power of 10, so we'll just use
  350. %g format (same as %f, no trailing zeroes) */
  351. {
  352. sprintf (labelbuf, "%.9g", val);
  353. return;
  354. }
  355. /* always use no. of digits of precision present in increment */
  356. spacing = (axis->type == A_LINEAR
  357. ? axis->tick_spacing
  358. : axis->subsubtick_spacing); /* user-specified, for log axis*/
  359. sprintf (incrbuf, "%.9f", spacing);
  360. ptr = strchr (incrbuf, (int)'.');
  361. prec = 0;
  362. if (ptr != NULL)
  363. {
  364. int count = 0;
  365. while (*(++ptr))
  366. {
  367. count++;
  368. if (*ptr != '0')
  369. prec = count;
  370. }
  371. }
  372. sprintf (labelbuf, "%.*f", prec, val);
  373. return;
  374. }
  375. }
  376. /* Algorithm SCALE1, for selecting an inter-tick spacing that will yield a
  377. * good-looking linear-format axis. The spacing is always 1.0, 2.0, or 5.0
  378. * times a power of ten.
  379. *
  380. * Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
  381. * SCALE3 for Determination of Scales on Computer Generated
  382. * Plots", Communications of the ACM, 10 (1973), 639-640.
  383. * Also cited as ACM Algorithm 463.
  384. *
  385. * We call this routine even when the axis is logarithmic rather than
  386. * linear. In that case the arguments are the logs of the actual
  387. * arguments, so that it computes an optimal inter-tick factor rather than
  388. * an optimal inter-tick spacing.
  389. */
  390. /* ARGS: min,max = data min, max
  391. tick_spacing = inter-tick spacing
  392. tick_spacing_type = 0,1,2 i.e. S_ONE,S_TWO,S_FIVE */
  393. static void
  394. scale1 (double min, double max, double *tick_spacing, int *tick_spacing_type)
  395. {
  396. int k;
  397. double nal;
  398. double a, b;
  399. /* valid interval lengths */
  400. static const double vint[] =
  401. {
  402. 1.0, 2.0, 5.0, 10.0
  403. };
  404. /* Corresponding breakpoints. The published algorithm uses geometric
  405. means, i.e. sqrt(2), sqrt(10), sqrt(50), but using sqrt(10)=3.16...
  406. will (if nticks=5, as we choose it to be) cause intervals of length
  407. 1.5 to yield an inter-tick distance of 0.2 rather than 0.5. So we
  408. could reduce it to 2.95. Similarly we could reduce sqrt(50) to 6.95
  409. so that intervals of length 3.5 will yield an inter-tick distance of
  410. 1.0 rather than 0.5. */
  411. static const double sqr[] =
  412. {
  413. M_SQRT2, 3.16228, 7.07107
  414. };
  415. /* compute trial inter-tick interval length */
  416. a = (max - min) / TRIAL_NUMBER_OF_TICK_INTERVALS;
  417. a *= (max > min) ? 1.0 : -1.0; /* paranoia, max>min always */
  418. if (a <= 0.0)
  419. {
  420. fprintf(stderr, "%s: error: the trial inter-tick spacing '%g' is bad\n",
  421. progname, a);
  422. exit (EXIT_FAILURE);
  423. }
  424. nal = floor(log10(a));
  425. b = a * pow (10.0, -nal); /* 1.0 <= b < 10.0 */
  426. /* round to closest permissible inter-tick interval length */
  427. k = 0;
  428. do
  429. {
  430. if (b < sqr[k])
  431. break;
  432. k++;
  433. }
  434. while (k < 3);
  435. *tick_spacing = (max > min ? 1.0 : -1.0) * vint[k] * pow (10.0, nal);
  436. /* for increment type, 0,1,2 means 1,2,5 times a power of 10 */
  437. *tick_spacing_type = (k == 3 ? 0 : k);
  438. return;
  439. }
  440. /* Determine whether an inter-tick spacing (in practice, one specified by
  441. the user) is 1.0, 2.0, or 5.0 times a power of 10. */
  442. static int
  443. spacing_type (double incr)
  444. {
  445. int i;
  446. int i_tenpower = (int)(floor(log10(incr)));
  447. double tenpower = 1.0;
  448. bool neg_power = false;
  449. if (i_tenpower < 0)
  450. {
  451. neg_power = true;
  452. i_tenpower = -i_tenpower;
  453. }
  454. for (i = 0; i < i_tenpower; i++)
  455. tenpower *= 10;
  456. if (neg_power)
  457. tenpower = 1.0 / tenpower;
  458. if (NEAR_EQUALITY(incr, tenpower, tenpower))
  459. return S_ONE;
  460. else if (NEAR_EQUALITY(incr, 2 * tenpower, tenpower))
  461. return S_TWO;
  462. else if (NEAR_EQUALITY(incr, 2.5 * tenpower, tenpower))
  463. return S_TWO_FIVE;
  464. else if (NEAR_EQUALITY(incr, 5 * tenpower, tenpower))
  465. return S_FIVE;
  466. else
  467. return S_UNKNOWN;
  468. }
  469. /* prepare_axis() fills in the Axis structure for an axis, and some of
  470. * the linear transformation variables in the Transform structure also.
  471. */
  472. /* ARGS: user_specified_subticks = linear ticks on a log axis?
  473. round_to_next_tick = round limits to next tick mark?
  474. log_axis = log axis?
  475. reverse_axis = reverse min, max?
  476. switch_axis_end = intersection w/ other axis in alt. pos.?
  477. omit_ticks = suppress all ticks and tick labels? */
  478. static void
  479. prepare_axis (Axis *axisp, Transform *trans, double min, double max, double spacing, const char *font_name, double font_size, const char *label, double subsubtick_spacing, bool user_specified_subsubticks, bool round_to_next_tick, bool log_axis, bool reverse_axis, bool switch_axis_end, bool omit_ticks)
  480. {
  481. double range;
  482. int tick_spacing_type = 0;
  483. double tick_spacing, lin_subtick_spacing;
  484. int min_tick_count, max_tick_count;
  485. int min_lin_subtick_count, max_lin_subtick_count;
  486. bool have_lin_subticks;
  487. if (min > max)
  488. /* paranoia, max < min is swapped at top level */
  489. {
  490. fprintf(stderr, "%s: error: min > max for an axis, which is not allowed\n",
  491. progname);
  492. exit (EXIT_FAILURE);
  493. }
  494. if (min == max) /* expand in a clever way */
  495. {
  496. max = floor (max + 1.0);
  497. min = ceil (min - 1.0);
  498. }
  499. if (log_axis) /* log axis, data are stored in logarithmic form */
  500. /* compute a tick spacing; user can't specify it */
  501. {
  502. scale1 (min, max, &tick_spacing, &tick_spacing_type);
  503. if (tick_spacing <= 1.0)
  504. {
  505. tick_spacing = 1.0;
  506. tick_spacing_type = S_ONE;
  507. }
  508. }
  509. else /* linear axis */
  510. {
  511. if (spacing == 0.0) /* i.e., not specified by user */
  512. scale1 (min, max, &tick_spacing, &tick_spacing_type);
  513. else /* luser is boss, don't use SCALE1 */
  514. {
  515. tick_spacing = spacing;
  516. tick_spacing_type = spacing_type (spacing);
  517. }
  518. }
  519. range = max - min; /* range is not negative */
  520. if (round_to_next_tick) /* expand both limits to next tick */
  521. {
  522. if (user_specified_subsubticks)
  523. /* Special Case. If user specified the `spacing' argument to -x or
  524. -y on a logarithmic axis, our usual tick-generating and
  525. tick-plotting algorithms are disabled. So we don't bother with
  526. min_tick_count or several other fields of the axis struct;
  527. instead we just compute a new (rounded) max, min, and range.
  528. Since most data are stored as logs, this is complicated. */
  529. {
  530. double true_min = pow (10.0, min), true_max = pow (10.0, max);
  531. double true_range = true_max - true_min;
  532. int min_count, max_count;
  533. min_count = (int)(floor ((true_min + FUZZ * true_range)
  534. / subsubtick_spacing));
  535. max_count = (int)(ceil ((true_max - FUZZ * true_range)
  536. / subsubtick_spacing));
  537. /* avoid core dump, do *not* reduce minimum to zero! */
  538. if (min_count > 0)
  539. min = log10 (min_count * subsubtick_spacing);
  540. max = log10 (max_count * subsubtick_spacing);
  541. range = max - min;
  542. min_tick_count = max_tick_count = 0; /* keep gcc happy */
  543. }
  544. else /* normal `expand limits to next tick' case */
  545. {
  546. min_tick_count = (int)(floor((min + FUZZ * range)/ tick_spacing));
  547. max_tick_count = (int)(ceil((max - FUZZ * range)/ tick_spacing));
  548. /* max_tick_count > min_tick_count always */
  549. /* tickval = tick_spacing * count,
  550. for all count in [min_count,max_count]; must have >=2 ticks */
  551. min = tick_spacing * min_tick_count;
  552. max = tick_spacing * max_tick_count;
  553. range = max - min;
  554. }
  555. }
  556. else /* don't expand limits to next tick */
  557. {
  558. min_tick_count = (int)(ceil((min - FUZZ * range)/ tick_spacing));
  559. max_tick_count = (int)(floor((max + FUZZ * range)/ tick_spacing));
  560. /* max_tick_count <= min_tick_count is possible */
  561. /* tickval = incr * count,
  562. for all count in [min_count,max_count]; can have 0,1,2,3... ticks */
  563. }
  564. /* Allow 5 subticks per tick if S_FIVE or S_TWO_FIVE, 2 if S_TWO. Case
  565. S_ONE is special; we try 10, 5, and 2 in succession */
  566. switch (tick_spacing_type)
  567. {
  568. case S_FIVE:
  569. case S_TWO_FIVE:
  570. lin_subtick_spacing = tick_spacing / 5;
  571. break;
  572. case S_TWO:
  573. lin_subtick_spacing = tick_spacing / 2;
  574. break;
  575. case S_ONE:
  576. lin_subtick_spacing = tick_spacing / 10;
  577. min_lin_subtick_count = (int)(ceil((min - FUZZ * range)/ lin_subtick_spacing));
  578. max_lin_subtick_count = (int)(floor((max + FUZZ * range)/ lin_subtick_spacing));
  579. if (max_lin_subtick_count - min_lin_subtick_count > MAX_NUM_SUBTICKS)
  580. {
  581. lin_subtick_spacing = tick_spacing / 5;
  582. min_lin_subtick_count = (int)(ceil((min - FUZZ * range)/ lin_subtick_spacing));
  583. max_lin_subtick_count = (int)(floor((max + FUZZ * range)/ lin_subtick_spacing));
  584. if (max_lin_subtick_count - min_lin_subtick_count > MAX_NUM_SUBTICKS)
  585. lin_subtick_spacing = tick_spacing / 2;
  586. }
  587. break;
  588. default:
  589. /* in default case, i.e. S_UNKNOWN, we won't plot linear subticks */
  590. lin_subtick_spacing = tick_spacing; /* not actually needed, since not plotted */
  591. break;
  592. }
  593. /* smallest possible inter-subtick factor for a log axis is 10.0 */
  594. if (log_axis && lin_subtick_spacing <= 1.0)
  595. lin_subtick_spacing = 1.0;
  596. min_lin_subtick_count = (int)(ceil((min - FUZZ * range)/ lin_subtick_spacing));
  597. max_lin_subtick_count = (int)(floor((max + FUZZ * range)/ lin_subtick_spacing));
  598. have_lin_subticks
  599. = ((tick_spacing_type != S_UNKNOWN /* S_UNKNOWN -> no subticks */
  600. && (max_lin_subtick_count - min_lin_subtick_count) <= MAX_NUM_SUBTICKS)
  601. ? true : false);
  602. /* fill in parameters for axis-specific affine transformation */
  603. trans->input_min = min;
  604. trans->input_max = max;
  605. trans->input_range = range; /* precomputed for speed */
  606. trans->reverse = reverse_axis;
  607. /* fill in axis-specific plot frame variables */
  608. axisp->switch_axis_end = switch_axis_end;
  609. axisp->omit_ticks = omit_ticks;
  610. axisp->label = label;
  611. axisp->font_name = font_name;
  612. axisp->font_size = font_size;
  613. axisp->max_label_width = 0.0;
  614. axisp->type = log_axis ? A_LOG10 : A_LINEAR;
  615. axisp->tick_spacing = tick_spacing;
  616. axisp->min_tick_count = min_tick_count;
  617. axisp->max_tick_count = max_tick_count;
  618. axisp->have_lin_subticks = have_lin_subticks;
  619. axisp->lin_subtick_spacing = lin_subtick_spacing;
  620. axisp->min_lin_subtick_count = min_lin_subtick_count;
  621. axisp->max_lin_subtick_count = max_lin_subtick_count;
  622. axisp->user_specified_subsubticks = user_specified_subsubticks;
  623. axisp->subsubtick_spacing = subsubtick_spacing;
  624. axisp->labelled_ticks = 0; /* updated during drawing of frame */
  625. if (log_axis) /* logarithmic axis */
  626. /* do we have special logarithmic subsubticks, and should we label them? */
  627. {
  628. if (max - min <=
  629. MAX_DECADES_WITH_LOG_SUBSUBTICKS + FUZZ)
  630. /* not too many orders of magnitude, so plot normal log subsubticks */
  631. axisp->have_normal_subsubticks = true;
  632. else
  633. /* too many orders of magnitude, don't plot log subsubticks */
  634. axisp->have_normal_subsubticks = false;
  635. }
  636. else /* linear axes don't have log subsubticks */
  637. axisp->have_normal_subsubticks = false;
  638. }
  639. /* The following routines [new_multigrapher(), begin_graph(),
  640. * set_graph_parameters(), draw_frame_of_graph(), plot_point(),
  641. * end_graph(), delete_multigrapher()] are the basic routines of the
  642. * point-plotter . See descriptions at the head of this file. */
  643. /* Create a new Multigrapher. The arguments, after the first, are the
  644. libplot Plotter parameters that the `graph' user can set on the command
  645. line. */
  646. Multigrapher *
  647. new_multigrapher (const char *output_format, const char *bg_color, const char *bitmap_size, const char *emulate_color, const char *max_line_length, const char *meta_portable, const char *page_size, const char *rotation_angle, bool save_screen)
  648. {
  649. plPlotterParams *plotter_params;
  650. plPlotter *plotter;
  651. Multigrapher *multigrapher;
  652. multigrapher = (Multigrapher *)xmalloc (sizeof (Multigrapher));
  653. /* set Plotter parameters */
  654. plotter_params = pl_newplparams ();
  655. pl_setplparam (plotter_params, "BG_COLOR", (void *)bg_color);
  656. pl_setplparam (plotter_params, "BITMAPSIZE", (void *)bitmap_size);
  657. pl_setplparam (plotter_params, "EMULATE_COLOR", (void *)emulate_color);
  658. pl_setplparam (plotter_params, "MAX_LINE_LENGTH", (void *)max_line_length);
  659. pl_setplparam (plotter_params, "META_PORTABLE", (void *)meta_portable);
  660. pl_setplparam (plotter_params, "PAGESIZE", (void *)page_size);
  661. pl_setplparam (plotter_params, "ROTATION", (void *)rotation_angle);
  662. /* create Plotter and open it */
  663. plotter = pl_newpl_r (output_format, NULL, stdout, stderr, plotter_params);
  664. if (plotter == (plPlotter *)NULL)
  665. return (Multigrapher *)NULL;
  666. pl_deleteplparams (plotter_params);
  667. multigrapher->plotter = plotter;
  668. if (pl_openpl_r (plotter) < 0)
  669. return (Multigrapher *)NULL;
  670. multigrapher->bg_color = bg_color;
  671. /* if called for, erase it; set up the user->device coor map */
  672. if (!save_screen || bg_color)
  673. pl_erase_r (plotter);
  674. pl_fspace_r (plotter, 0.0, 0.0, (double)PLOT_SIZE, (double)PLOT_SIZE);
  675. return multigrapher;
  676. }
  677. int
  678. delete_multigrapher (Multigrapher *multigrapher)
  679. {
  680. int retval;
  681. retval = pl_closepl_r (multigrapher->plotter);
  682. if (retval >= 0)
  683. retval = pl_deletepl_r (multigrapher->plotter);
  684. free (multigrapher);
  685. return retval;
  686. }
  687. void
  688. begin_graph (Multigrapher *multigrapher, double scale, double trans_x, double trans_y)
  689. {
  690. pl_savestate_r (multigrapher->plotter);
  691. pl_fconcat_r (multigrapher->plotter,
  692. scale, 0.0, 0.0, scale,
  693. trans_x * PLOT_SIZE, trans_y * PLOT_SIZE);
  694. }
  695. void
  696. end_graph (Multigrapher *multigrapher)
  697. {
  698. pl_restorestate_r (multigrapher->plotter);
  699. }
  700. /* ARGS:
  701. Multigrapher *multigrapher
  702. double frame_line_width = fractional width of lines in the frame
  703. const char *frame_color = color for frame (and plot if no -C option)
  704. const char *title = graph title
  705. const char *title_font_name = font for graph title (string)
  706. double title_font_size = font size for graph title
  707. double tick_size = fractional size of ticks
  708. grid_type grid_spec = gridstyle (and tickstyle) spec
  709. double x_min, x_max, x_spacing
  710. double y_min, y_max, y_spacing
  711. bool spec_x_spacing
  712. bool spec_y_spacing
  713. double width, height, up, right
  714. const char *x_font_name
  715. double x_font_size
  716. const char *x_label
  717. const char *y_font_name
  718. double y_font_size
  719. const char *y_label
  720. bool no_rotate_y_label
  721. -- portmanteaux args --
  722. int log_axis = whether axes should be logarithmic
  723. int round_to_next_tick = round limits to the next tick mark
  724. int switch_axis_end = put axes at right/top, not left/bottom?
  725. int omit_ticks = omit all ticks, tick labels from an axis?
  726. -- other args --
  727. int clip_mode = 0, 1, or 2
  728. double blankout_fraction = 1.0 means blank out whole box before plot
  729. bool transpose_axes */
  730. void
  731. set_graph_parameters (Multigrapher *multigrapher, double frame_line_width, const char *frame_color, const char *title, const char *title_font_name, double title_font_size, double tick_size, grid_type grid_spec, double x_min, double x_max, double x_spacing, double y_min, double y_max, double y_spacing, bool spec_x_spacing, bool spec_y_spacing, double width, double height, double up, double right, const char *x_font_name, double x_font_size, const char *x_label, const char *y_font_name, double y_font_size, const char *y_label, bool no_rotate_y_label, int log_axis, int round_to_next_tick, int switch_axis_end, int omit_ticks, int clip_mode, double blankout_fraction, bool transpose_axes)
  732. {
  733. double x_subsubtick_spacing = 0.0, y_subsubtick_spacing = 0.0;
  734. /* local portmanteau variables */
  735. int reverse_axis = 0; /* min > max on an axis? */
  736. int user_specified_subsubticks = 0; /* i.e. linear ticks on a log axis? */
  737. if (log_axis & X_AXIS)
  738. {
  739. if (spec_x_spacing)
  740. /* spacing is handled specially for log axes */
  741. {
  742. spec_x_spacing = false;
  743. user_specified_subsubticks |= X_AXIS;
  744. x_subsubtick_spacing = x_spacing;
  745. }
  746. }
  747. if (log_axis & Y_AXIS)
  748. {
  749. if (spec_y_spacing)
  750. {
  751. /* spacing is handled specially for log axes */
  752. spec_y_spacing = false;
  753. user_specified_subsubticks |= Y_AXIS;
  754. y_subsubtick_spacing = y_spacing;
  755. }
  756. }
  757. /* check for reversed axes (min > max) */
  758. if (x_max < x_min)
  759. {
  760. reverse_axis |= X_AXIS;
  761. {
  762. double temp;
  763. temp = x_min;
  764. x_min = x_max;
  765. x_max = temp;
  766. }
  767. }
  768. if (x_max == x_min)
  769. {
  770. fprintf (stderr,
  771. "%s: identical upper and lower x limits (%f, %f) are separated\n",
  772. progname, x_max, x_min);
  773. /* separate them */
  774. x_max += 1.0;
  775. x_min -= 1.0;
  776. }
  777. /* check for reversed axes (min > max) */
  778. if (y_max < y_min)
  779. {
  780. reverse_axis |= Y_AXIS;
  781. {
  782. double temp;
  783. temp = y_min;
  784. y_min = y_max;
  785. y_max = temp;
  786. }
  787. }
  788. if (y_max == y_min)
  789. {
  790. fprintf (stderr,
  791. "%s: identical upper and lower x limits (%f, %f) are separated\n",
  792. progname, y_max, y_min);
  793. /* separate them */
  794. y_max += 1.0;
  795. y_min -= 1.0;
  796. }
  797. /* At this point, min < max for each axis, if the user specified the two
  798. limits on an axis; reverse_axis portmanteau variable keeps track of
  799. whether either axis was discovered to be reversed. */
  800. /* silently accept negative spacing as equivalent as positive */
  801. if (spec_x_spacing)
  802. {
  803. if (x_spacing == 0.0)
  804. {
  805. fprintf (stderr,
  806. "%s: error: the spacing between ticks on an axis is zero\n",
  807. progname);
  808. exit (EXIT_FAILURE);
  809. }
  810. x_spacing = fabs (x_spacing);
  811. }
  812. if (spec_y_spacing)
  813. {
  814. if (y_spacing == 0.0)
  815. {
  816. fprintf (stderr,
  817. "%s: error: the spacing between ticks on an axis is zero\n",
  818. progname);
  819. exit (EXIT_FAILURE);
  820. }
  821. y_spacing = fabs (y_spacing);
  822. }
  823. /* now transpose the two axes (i.e. their portmanteau variables, labels,
  824. limits etc.) if transpose_axes was set */
  825. if (transpose_axes)
  826. {
  827. const char *temp_string;
  828. double temp_double;
  829. transpose_portmanteau (&log_axis);
  830. transpose_portmanteau (&round_to_next_tick);
  831. transpose_portmanteau (&switch_axis_end);
  832. transpose_portmanteau (&omit_ticks);
  833. transpose_portmanteau (&reverse_axis);
  834. transpose_portmanteau (&user_specified_subsubticks);
  835. temp_string = x_label;
  836. x_label = y_label;
  837. y_label = temp_string;
  838. temp_double = x_min;
  839. x_min = y_min;
  840. y_min = temp_double;
  841. temp_double = x_max;
  842. x_max = y_max;
  843. y_max = temp_double;
  844. temp_double = x_spacing;
  845. x_spacing = y_spacing;
  846. y_spacing = temp_double;
  847. temp_double = x_subsubtick_spacing;
  848. x_subsubtick_spacing = y_subsubtick_spacing;
  849. y_subsubtick_spacing = temp_double;
  850. }
  851. /* fill in the Multigrapher struct */
  852. multigrapher->frame_line_width = frame_line_width;
  853. multigrapher->frame_color = frame_color;
  854. multigrapher->no_rotate_y_label = no_rotate_y_label;
  855. multigrapher->blankout_fraction = blankout_fraction;
  856. if (title != NULL)
  857. multigrapher->title = xstrdup (title);
  858. else
  859. multigrapher->title = NULL;
  860. if (title_font_name != NULL)
  861. multigrapher->title_font_name = xstrdup (title_font_name);
  862. else
  863. multigrapher->title_font_name = NULL;
  864. multigrapher->title_font_size = title_font_size;
  865. multigrapher->tick_size = tick_size;
  866. multigrapher->subtick_size = RELATIVE_SUBTICK_SIZE * tick_size;
  867. multigrapher->grid_spec = grid_spec;
  868. multigrapher->clip_mode = clip_mode;
  869. /* fill in the Transform and Axis elements for each coordinate */
  870. prepare_axis (&multigrapher->x_axis, &multigrapher->x_trans,
  871. x_min, x_max, x_spacing,
  872. x_font_name, x_font_size, x_label,
  873. x_subsubtick_spacing,
  874. (bool)(user_specified_subsubticks & X_AXIS),
  875. (bool)(round_to_next_tick & X_AXIS),
  876. (bool)(log_axis & X_AXIS),
  877. (bool)(reverse_axis & X_AXIS),
  878. (bool)(switch_axis_end & X_AXIS),
  879. (bool)(omit_ticks & X_AXIS));
  880. prepare_axis (&multigrapher->y_axis, &multigrapher->y_trans,
  881. y_min, y_max, y_spacing,
  882. y_font_name, y_font_size, y_label,
  883. y_subsubtick_spacing,
  884. (bool)(user_specified_subsubticks & Y_AXIS),
  885. (bool)(round_to_next_tick & Y_AXIS),
  886. (bool)(log_axis & Y_AXIS),
  887. (bool)(reverse_axis & Y_AXIS),
  888. (bool)(switch_axis_end & Y_AXIS),
  889. (bool)(omit_ticks & Y_AXIS));
  890. /* fill in additional parameters in the two Transform structures */
  891. multigrapher->x_trans.squeezed_min = right;
  892. multigrapher->x_trans.squeezed_max = right + width;
  893. multigrapher->x_trans.squeezed_range = width;
  894. multigrapher->y_trans.squeezed_min = up;
  895. multigrapher->y_trans.squeezed_max = up + height;
  896. multigrapher->y_trans.squeezed_range = height;
  897. /* specify interval range for each coordinate, in libplot units */
  898. multigrapher->x_trans.output_min = 0.0;
  899. multigrapher->x_trans.output_max = (double)PLOT_SIZE;
  900. multigrapher->x_trans.output_range = multigrapher->x_trans.output_max - multigrapher->x_trans.output_min;
  901. multigrapher->x_trans.output_min = 0.0;
  902. multigrapher->y_trans.output_max = (double)PLOT_SIZE;
  903. multigrapher->y_trans.output_range = multigrapher->y_trans.output_max - multigrapher->y_trans.output_min;
  904. /* fill in fields in Axis structs dealing with location of other axis */
  905. if (multigrapher->grid_spec != AXES_AT_ORIGIN)
  906. /* Normal case */
  907. {
  908. /* axes are at left/bottom */
  909. multigrapher->x_axis.other_axis_loc = multigrapher->x_trans.input_min;
  910. multigrapher->y_axis.other_axis_loc = multigrapher->y_trans.input_min;
  911. /* secondary axes (used only if --switch-axis-end is specified) */
  912. multigrapher->x_axis.alt_other_axis_loc = multigrapher->x_trans.input_max;
  913. multigrapher->y_axis.alt_other_axis_loc = multigrapher->y_trans.input_max;
  914. }
  915. else
  916. /* Special case: grid type #4, AXES_AT_ORIGIN */
  917. {
  918. /* In this case (grid type #4), we don't allow the user to move the
  919. axis position by using the --switch-axis-end option. Each axis is
  920. at the value 0 (the origin) if the value 0 is between the limits
  921. of the opposing axis. Otherwise, the position is at the end
  922. closer to the value of 0. */
  923. multigrapher->x_axis.other_axis_loc
  924. = (multigrapher->x_trans.input_min * multigrapher->x_trans.input_max <= 0.0) ? 0.0 :
  925. (multigrapher->x_trans.input_min > 0.0 ? multigrapher->x_trans.input_min : multigrapher->x_trans.input_max);
  926. multigrapher->y_axis.other_axis_loc
  927. = (multigrapher->y_trans.input_min * multigrapher->y_trans.input_max <= 0.0) ? 0.0 :
  928. (multigrapher->y_trans.input_min > 0.0 ? multigrapher->y_trans.input_min : multigrapher->y_trans.input_max);
  929. /* secondary axes are the same */
  930. multigrapher->x_axis.alt_other_axis_loc = multigrapher->x_axis.other_axis_loc;
  931. multigrapher->y_axis.alt_other_axis_loc = multigrapher->y_axis.other_axis_loc;
  932. multigrapher->x_axis.switch_axis_end = (((multigrapher->x_trans.input_max - multigrapher->x_axis.other_axis_loc)
  933. < (multigrapher->x_axis.other_axis_loc - multigrapher->x_trans.input_min))
  934. ? true : false);
  935. multigrapher->y_axis.switch_axis_end = (((multigrapher->y_trans.input_max - multigrapher->y_axis.other_axis_loc)
  936. < (multigrapher->y_axis.other_axis_loc - multigrapher->y_trans.input_min))
  937. ? true : false);
  938. }
  939. /* The following is a version of (multigrapher->frame_line_width)/2
  940. (expressed in terms of libplot coordinates) which the plotter uses as
  941. an offset, to get highly accurate positioning of ticks and labels. */
  942. if (frame_line_width < 0.0
  943. || pl_havecap_r (multigrapher->plotter, "WIDE_LINES") == 0)
  944. multigrapher->half_line_width = 0.0;/* N.B. <0.0 -> default width, pres. small */
  945. else
  946. multigrapher->half_line_width = 0.5 * frame_line_width * multigrapher->x_trans.output_range;
  947. /* initialize the plotter state variables */
  948. multigrapher->first_point_of_polyline = true;
  949. multigrapher->oldpoint_x = 0.0;
  950. multigrapher->oldpoint_y = 0.0;
  951. }
  952. /* draw_frame_of_graph() plots the graph frame (grid plus axis labels and
  953. title), using the multigrapher's variables (the multigrapher->x_axis,
  954. multigrapher->y_axis, multigrapher->x_trans, multigrapher->y_trans
  955. structures). It comprises several almost-independent tasks:
  956. 0. draw opaque white rectangle (``canvas''), if requested
  957. 1. draw title, if any, above drawing box
  958. 2. draw axes, and a full drawing box if requested
  959. 3. draw ticks, grid lines, and tick labels on abscissa;
  960. also subticks, if abscissa is a linear axis
  961. 4. draw ticks, grid lines, and tick labels on ordinate,
  962. also subticks, if ordinate is a linear axis
  963. 5. draw sub-tick marks, sub-grid lines, and sub-labels,
  964. if abscissa is a logarithmic axis
  965. 6. draw sub-tick marks, sub-grid lines, and sub-labels,
  966. if ordinate is a logarithmic axis
  967. 7. draw axis label on abscissa
  968. 8. draw axis label on ordinate (normally rotated 90 degrees)
  969. A savestate()--restorestate() pair is wrapped around these nine tasks.
  970. They are not quite independent because in (4) and (6) we keep track of
  971. the maximum width of the tick labels on the ordinate, to position the
  972. rotated ordinate label properly in (8).
  973. At the conclusion of the eight tasks, we warn the user if (i) he/she
  974. didn't specify a tick spacing for a logarithmic axis by hand, and (ii)
  975. our default algorithm for drawing ticks on a logarithmic axis has drawn
  976. too few ticks (i.e. <= 2 ticks) on the axis.
  977. Task #0 (drawing a white canvas) isn't always performed; only if the
  978. argument draw_canvas is set. It isn't set when we call this function
  979. for the first time; see graph.c. */
  980. void
  981. draw_frame_of_graph (Multigrapher *multigrapher, bool draw_canvas)
  982. {
  983. static bool tick_warning_printed = false; /* when too few labelled ticks */
  984. /* wrap savestate()--restorestate() around all 9 tasks */
  985. pl_savestate_r (multigrapher->plotter);
  986. /* set color for graph frame */
  987. if (multigrapher->frame_color)
  988. pl_pencolorname_r (multigrapher->plotter, multigrapher->frame_color);
  989. /* set line width as a fraction of size of display, <0.0 means default */
  990. pl_flinewidth_r (multigrapher->plotter,
  991. multigrapher->frame_line_width * (double)PLOT_SIZE);
  992. /* axes (or box) will be drawn in solid line style */
  993. pl_linemod_r (multigrapher->plotter, "solid");
  994. /* turn off filling */
  995. pl_filltype_r (multigrapher->plotter, 0);
  996. /* 0. DRAW AN OPAQUE WHITE BOX */
  997. if (draw_canvas)
  998. {
  999. pl_savestate_r (multigrapher->plotter);
  1000. /* use user-specified background color (if any) instead of white */
  1001. if (pl_havecap_r (multigrapher->plotter, "SETTABLE_BACKGROUND") != 0
  1002. && multigrapher->bg_color)
  1003. pl_colorname_r (multigrapher->plotter, multigrapher->bg_color);
  1004. else
  1005. pl_colorname_r (multigrapher->plotter, "white");
  1006. pl_filltype_r (multigrapher->plotter, 1); /* turn on filling */
  1007. pl_fbox_r (multigrapher->plotter,
  1008. XP(XSQ(0.5 - 0.5 * multigrapher->blankout_fraction)),
  1009. YP(YSQ(0.5 - 0.5 * multigrapher->blankout_fraction)),
  1010. XP(XSQ(0.5 + 0.5 * multigrapher->blankout_fraction)),
  1011. YP(YSQ(0.5 + 0.5 * multigrapher->blankout_fraction)));
  1012. pl_restorestate_r (multigrapher->plotter);
  1013. }
  1014. /* 1. DRAW THE TITLE, I.E. THE TOP LABEL */
  1015. if (multigrapher->grid_spec != NO_AXES
  1016. && !multigrapher->y_axis.switch_axis_end /* no title if x axis is at top of plot */
  1017. && multigrapher->title != NULL && *multigrapher->title != '\0')
  1018. {
  1019. double title_font_size;
  1020. /* switch to our font for drawing title */
  1021. pl_fontname_r (multigrapher->plotter, multigrapher->title_font_name);
  1022. title_font_size = pl_ffontsize_r (multigrapher->plotter,
  1023. SS(multigrapher->title_font_size));
  1024. pl_fmove_r (multigrapher->plotter,
  1025. XP(XSQ(0.5)),
  1026. YP(YSQ(1.0
  1027. + (((multigrapher->grid_spec == AXES_AND_BOX
  1028. || multigrapher->grid_spec == AXES
  1029. || multigrapher->grid_spec == BOX)
  1030. && (multigrapher->tick_size <= 0.0) ? 1.0 : 0.5)
  1031. * fabs(multigrapher->tick_size))))
  1032. + 0.65 * title_font_size
  1033. + multigrapher->half_line_width);
  1034. /* title centered, bottom spec'd */
  1035. pl_alabel_r (multigrapher->plotter, 'c', 'b', multigrapher->title);
  1036. }
  1037. /* 2. DRAW AXES FOR THE PLOT */
  1038. switch (multigrapher->grid_spec)
  1039. {
  1040. case AXES_AND_BOX_AND_GRID:
  1041. case AXES_AND_BOX:
  1042. case BOX:
  1043. /* draw a box, not just a pair of axes */
  1044. pl_fbox_r (multigrapher->plotter,
  1045. XP(XSQ(0.0)), YP(YSQ(0.0)), XP(XSQ(1.0)), YP(YSQ(1.0)));
  1046. break;
  1047. case AXES:
  1048. {
  1049. double xstart, ystart, xmid, ymid, xend, yend;
  1050. xstart = (multigrapher->x_axis.switch_axis_end
  1051. ? XN(multigrapher->x_axis.other_axis_loc) - multigrapher->half_line_width
  1052. : XN(multigrapher->x_axis.alt_other_axis_loc) + multigrapher->half_line_width);
  1053. ystart = (multigrapher->y_axis.switch_axis_end
  1054. ? YN(multigrapher->y_axis.alt_other_axis_loc)
  1055. : YN(multigrapher->y_axis.other_axis_loc));
  1056. xmid = (multigrapher->x_axis.switch_axis_end
  1057. ? XN(multigrapher->x_axis.alt_other_axis_loc)
  1058. : XN(multigrapher->x_axis.other_axis_loc));
  1059. ymid = ystart;
  1060. xend = xmid;
  1061. yend = (multigrapher->y_axis.switch_axis_end
  1062. ? YN(multigrapher->y_axis.other_axis_loc) - multigrapher->half_line_width
  1063. : YN(multigrapher->y_axis.alt_other_axis_loc) + multigrapher->half_line_width);
  1064. pl_fmove_r (multigrapher->plotter, xstart, ystart);
  1065. pl_fcont_r (multigrapher->plotter, xmid, ymid);
  1066. pl_fcont_r (multigrapher->plotter, xend, yend);
  1067. }
  1068. break;
  1069. case AXES_AT_ORIGIN:
  1070. {
  1071. double xpos, ypos;
  1072. xpos = (multigrapher->x_axis.switch_axis_end
  1073. ? XN(multigrapher->x_axis.other_axis_loc)
  1074. : XN(multigrapher->x_axis.alt_other_axis_loc));
  1075. ypos = (multigrapher->y_axis.switch_axis_end
  1076. ? YN(multigrapher->y_axis.alt_other_axis_loc)
  1077. : YN(multigrapher->y_axis.other_axis_loc));
  1078. pl_fline_r (multigrapher->plotter,
  1079. xpos, YP(YSQ(0.0)) - multigrapher->half_line_width,
  1080. xpos, YP(YSQ(1.0)) + multigrapher->half_line_width);
  1081. pl_fline_r (multigrapher->plotter,
  1082. XP(XSQ(0.0)) - multigrapher->half_line_width, ypos,
  1083. XP(XSQ(1.0)) + multigrapher->half_line_width, ypos);
  1084. }
  1085. break;
  1086. case NO_AXES:
  1087. default:
  1088. break;
  1089. }
  1090. /* 3. PLOT TICK MARKS, GRID LINES, AND TICK LABELS ON ABSCISSA */
  1091. if (multigrapher->grid_spec != NO_AXES && !multigrapher->x_axis.omit_ticks
  1092. && !multigrapher->x_axis.user_specified_subsubticks)
  1093. {
  1094. int i;
  1095. double xval, xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
  1096. /* there is no way you could use longer labels on tick marks! */
  1097. char labelbuf[2048];
  1098. /* switch to our font for drawing x axis label and tick labels */
  1099. pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
  1100. pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->x_axis.font_size));
  1101. for (i = multigrapher->x_axis.min_tick_count; i <= multigrapher->x_axis.max_tick_count; i++)
  1102. /* tick range can be empty */
  1103. {
  1104. xval = i * multigrapher->x_axis.tick_spacing;
  1105. /* discard tick locations outside plotting area */
  1106. if (xval < multigrapher->x_trans.input_min - FUZZ * xrange
  1107. || xval > multigrapher->x_trans.input_max + FUZZ * xrange)
  1108. continue;
  1109. /* Plot the abscissa tick labels. */
  1110. if (!multigrapher->y_axis.switch_axis_end
  1111. && !(multigrapher->grid_spec == AXES_AT_ORIGIN
  1112. /* don't plot label if it could run into an axis */
  1113. && NEAR_EQUALITY (xval, multigrapher->x_axis.other_axis_loc,
  1114. multigrapher->x_trans.input_range)
  1115. && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_min)
  1116. && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_max)))
  1117. /* print labels below bottom boundary */
  1118. {
  1119. pl_fmove_r (multigrapher->plotter,
  1120. XV (xval),
  1121. YN (multigrapher->y_axis.other_axis_loc)
  1122. - (SS ((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75) * fabs(multigrapher->tick_size))
  1123. + multigrapher->half_line_width));
  1124. print_tick_label (labelbuf,
  1125. &multigrapher->x_axis, &multigrapher->x_trans,
  1126. (multigrapher->x_axis.type == A_LOG10) ? pow (10.0, xval) : xval);
  1127. pl_alabel_r (multigrapher->plotter, 'c', 't', labelbuf);
  1128. multigrapher->x_axis.labelled_ticks++;
  1129. }
  1130. else
  1131. /* print labels above top boundary */
  1132. if (multigrapher->y_axis.switch_axis_end
  1133. && !(multigrapher->grid_spec == AXES_AT_ORIGIN
  1134. /* don't plot label if it could run into an axis */
  1135. && NEAR_EQUALITY (xval, multigrapher->x_axis.other_axis_loc,
  1136. multigrapher->x_trans.input_range)
  1137. && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_min)
  1138. && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_max)))
  1139. {
  1140. pl_fmove_r (multigrapher->plotter,
  1141. XV (xval),
  1142. YN (multigrapher->y_axis.alt_other_axis_loc)
  1143. + (SS ((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75) * fabs(multigrapher->tick_size))
  1144. + multigrapher->half_line_width));
  1145. print_tick_label (labelbuf,
  1146. &multigrapher->x_axis, &multigrapher->x_trans,
  1147. (multigrapher->x_axis.type == A_LOG10) ? pow (10.0, xval) : xval);
  1148. pl_alabel_r (multigrapher->plotter, 'c', 'b', labelbuf);
  1149. multigrapher->x_axis.labelled_ticks++;
  1150. }
  1151. /* Plot the abscissa tick marks, and vertical grid lines. */
  1152. switch (multigrapher->grid_spec)
  1153. {
  1154. case AXES_AND_BOX_AND_GRID:
  1155. pl_linemod_r (multigrapher->plotter, "dotted");
  1156. pl_fmove_r (multigrapher->plotter, XV(xval), YP(YSQ(0.0)));
  1157. pl_fcont_r (multigrapher->plotter, XV(xval), YP(YSQ(1.0)));
  1158. pl_linemod_r (multigrapher->plotter, "solid");
  1159. /* fall through */
  1160. case AXES_AND_BOX:
  1161. case BOX:
  1162. if (!multigrapher->y_axis.switch_axis_end)
  1163. {
  1164. pl_fmove_r (multigrapher->plotter,
  1165. XV (xval),
  1166. YN (multigrapher->y_axis.alt_other_axis_loc));
  1167. pl_fcont_r (multigrapher->plotter,
  1168. XV (xval),
  1169. YN (multigrapher->y_axis.alt_other_axis_loc)
  1170. - (SS (multigrapher->tick_size)
  1171. + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
  1172. : -multigrapher->half_line_width)));
  1173. }
  1174. else
  1175. {
  1176. pl_fmove_r (multigrapher->plotter,
  1177. XV (xval),
  1178. YN (multigrapher->y_axis.other_axis_loc));
  1179. pl_fcont_r (multigrapher->plotter,
  1180. XV (xval),
  1181. YN (multigrapher->y_axis.other_axis_loc)
  1182. + (SS (multigrapher->tick_size)
  1183. + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
  1184. : -multigrapher->half_line_width)));
  1185. }
  1186. /* fall through */
  1187. case AXES:
  1188. case AXES_AT_ORIGIN:
  1189. if (!multigrapher->y_axis.switch_axis_end)
  1190. {
  1191. pl_fmove_r (multigrapher->plotter,
  1192. XV (xval),
  1193. YN (multigrapher->y_axis.other_axis_loc));
  1194. pl_fcont_r (multigrapher->plotter,
  1195. XV (xval),
  1196. YN (multigrapher->y_axis.other_axis_loc)
  1197. + (SS (multigrapher->tick_size)
  1198. + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
  1199. : -multigrapher->half_line_width)));
  1200. }
  1201. else
  1202. {
  1203. pl_fmove_r (multigrapher->plotter,
  1204. XV (xval),
  1205. YN (multigrapher->y_axis.alt_other_axis_loc));
  1206. pl_fcont_r (multigrapher->plotter,
  1207. XV (xval),
  1208. YN (multigrapher->y_axis.alt_other_axis_loc)
  1209. - (SS (multigrapher->tick_size)
  1210. + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
  1211. : -multigrapher->half_line_width)));
  1212. }
  1213. break;
  1214. default: /* shouldn't happen */
  1215. break;
  1216. }
  1217. }
  1218. if (multigrapher->x_axis.have_lin_subticks)
  1219. {
  1220. double subtick_size; /* libplot coordinates */
  1221. /* linearly spaced subticks on log axes are as long as reg. ticks */
  1222. subtick_size = (multigrapher->x_axis.type == A_LOG10
  1223. ? SS(multigrapher->tick_size) : SS(multigrapher->subtick_size));
  1224. /* Plot the linearly spaced subtick marks on the abscissa */
  1225. for (i = multigrapher->x_axis.min_lin_subtick_count; i <= multigrapher->x_axis.max_lin_subtick_count; i++)
  1226. /* tick range can be empty */
  1227. {
  1228. xval = i * multigrapher->x_axis.lin_subtick_spacing;
  1229. /* discard subtick locations outside plotting area */
  1230. if (xval < multigrapher->x_trans.input_min - FUZZ * xrange
  1231. || xval > multigrapher->x_trans.input_max + FUZZ * xrange)
  1232. continue;
  1233. switch (multigrapher->grid_spec)
  1234. {
  1235. case AXES_AND_BOX_AND_GRID:
  1236. case AXES_AND_BOX:
  1237. case BOX:
  1238. /* draw on both sides */
  1239. if (!multigrapher->y_axis.switch_axis_end)
  1240. {
  1241. pl_fmove_r (multigrapher->plotter,
  1242. XV (xval),
  1243. YN (multigrapher->y_axis.alt_other_axis_loc));
  1244. pl_fcont_r (multigrapher->plotter,
  1245. XV (xval),
  1246. YN (multigrapher->y_axis.alt_other_axis_loc)
  1247. - (subtick_size
  1248. + (subtick_size > 0.0 ? multigrapher->half_line_width
  1249. : -multigrapher->half_line_width)));
  1250. }
  1251. else
  1252. {
  1253. pl_fmove_r (multigrapher->plotter,
  1254. XV (xval),
  1255. YN (multigrapher->y_axis.other_axis_loc));
  1256. pl_fcont_r (multigrapher->plotter,
  1257. XV (xval),
  1258. YN (multigrapher->y_axis.other_axis_loc)
  1259. + (subtick_size
  1260. + (subtick_size > 0.0 ? multigrapher->half_line_width
  1261. : -multigrapher->half_line_width)));
  1262. }
  1263. /* fall through */
  1264. case AXES:
  1265. case AXES_AT_ORIGIN:
  1266. if (!multigrapher->y_axis.switch_axis_end)
  1267. /* draw on only one side */
  1268. {
  1269. pl_fmove_r (multigrapher->plotter,
  1270. XV (xval),
  1271. YN (multigrapher->y_axis.other_axis_loc));
  1272. pl_fcont_r (multigrapher->plotter,
  1273. XV (xval),
  1274. YN (multigrapher->y_axis.other_axis_loc)
  1275. + (subtick_size
  1276. + (subtick_size > 0.0 ? multigrapher->half_line_width
  1277. : -multigrapher->half_line_width)));
  1278. }
  1279. else
  1280. {
  1281. pl_fmove_r (multigrapher->plotter,
  1282. XV (xval),
  1283. YN (multigrapher->y_axis.alt_other_axis_loc));
  1284. pl_fcont_r (multigrapher->plotter,
  1285. XV (xval),
  1286. YN (multigrapher->y_axis.alt_other_axis_loc)
  1287. - (subtick_size
  1288. + (subtick_size > 0.0 ? multigrapher->half_line_width
  1289. : -multigrapher->half_line_width)));
  1290. }
  1291. break;
  1292. default: /* shouldn't happen */
  1293. break;
  1294. }
  1295. }
  1296. }
  1297. /* plot a vertical dotted line at x = 0 */
  1298. if (multigrapher->grid_spec != AXES_AT_ORIGIN
  1299. && multigrapher->grid_spec != BOX
  1300. && multigrapher->x_axis.type == A_LINEAR
  1301. && multigrapher->x_trans.input_min * multigrapher->x_trans.input_max < 0.0)
  1302. {
  1303. pl_linemod_r (multigrapher->plotter, "dotted");
  1304. pl_fline_r (multigrapher->plotter,
  1305. XV(0.0), YP(YSQ(0.0)), XV(0.0), YP(YSQ(1.0)));
  1306. pl_linemod_r (multigrapher->plotter, "solid");
  1307. }
  1308. }
  1309. /* 4. PLOT TICK MARKS, GRID LINES, AND TICK LABELS ON ORDINATE */
  1310. if (multigrapher->grid_spec != NO_AXES && !multigrapher->y_axis.omit_ticks
  1311. && !multigrapher->y_axis.user_specified_subsubticks)
  1312. {
  1313. int i;
  1314. double yval, yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
  1315. /* there is no way you could use longer labels on tick marks! */
  1316. char labelbuf[2048];
  1317. /* switch to our font for drawing y axis label and tick labels */
  1318. pl_fontname_r (multigrapher->plotter, multigrapher->y_axis.font_name);
  1319. pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->y_axis.font_size));
  1320. for (i = multigrapher->y_axis.min_tick_count; i <= multigrapher->y_axis.max_tick_count; i++)
  1321. /* range can be empty */
  1322. {
  1323. yval = i * multigrapher->y_axis.tick_spacing;
  1324. /* discard tick locations outside plotting area */
  1325. if (yval < multigrapher->y_trans.input_min - FUZZ * yrange
  1326. || yval > multigrapher->y_trans.input_max + FUZZ * yrange)
  1327. continue;
  1328. /* Plot the ordinate tick labels. */
  1329. if (!multigrapher->x_axis.switch_axis_end
  1330. && !(multigrapher->grid_spec == AXES_AT_ORIGIN
  1331. /* don't plot label if it could run into an axis */
  1332. && NEAR_EQUALITY (yval, multigrapher->y_axis.other_axis_loc,
  1333. multigrapher->y_trans.input_range)
  1334. && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_min)
  1335. && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_max)))
  1336. /* print labels to left of left boundary */
  1337. {
  1338. double new_width;
  1339. pl_fmove_r (multigrapher->plotter,
  1340. XN (multigrapher->x_axis.other_axis_loc)
  1341. - (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
  1342. * fabs(multigrapher->tick_size))
  1343. + multigrapher->half_line_width),
  1344. YV (yval));
  1345. print_tick_label (labelbuf,
  1346. &multigrapher->y_axis, &multigrapher->y_trans,
  1347. (multigrapher->y_axis.type == A_LOG10) ? pow (10.0, yval) : yval);
  1348. new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
  1349. pl_alabel_r (multigrapher->plotter, 'r', 'c', labelbuf);
  1350. multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
  1351. multigrapher->y_axis.labelled_ticks++;
  1352. }
  1353. else
  1354. /* print labels to right of right boundary */
  1355. if (multigrapher->x_axis.switch_axis_end
  1356. && !(multigrapher->grid_spec == AXES_AT_ORIGIN
  1357. /* don't plot label if it could run into an axis */
  1358. && NEAR_EQUALITY (yval, multigrapher->y_axis.other_axis_loc,
  1359. multigrapher->y_trans.input_range)
  1360. && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_min)
  1361. && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_max)))
  1362. {
  1363. double new_width;
  1364. pl_fmove_r (multigrapher->plotter,
  1365. XN (multigrapher->x_axis.alt_other_axis_loc)
  1366. + (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
  1367. * fabs(multigrapher->tick_size))
  1368. + multigrapher->half_line_width),
  1369. YV (yval));
  1370. print_tick_label (labelbuf,
  1371. &multigrapher->y_axis, &multigrapher->y_trans,
  1372. (multigrapher->y_axis.type == A_LOG10) ? pow (10.0, yval) : yval);
  1373. new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
  1374. pl_alabel_r (multigrapher->plotter, 'l', 'c', labelbuf);
  1375. multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
  1376. multigrapher->y_axis.labelled_ticks++;
  1377. }
  1378. /* Plot the tick marks on the y-axis, and horizontal grid lines. */
  1379. switch (multigrapher->grid_spec)
  1380. {
  1381. case AXES_AND_BOX_AND_GRID:
  1382. pl_linemod_r (multigrapher->plotter, "dotted");
  1383. pl_fmove_r (multigrapher->plotter, XP(XSQ(0.0)), YV (yval));
  1384. pl_fcont_r (multigrapher->plotter, XP(XSQ(1.0)), YV (yval));
  1385. pl_linemod_r (multigrapher->plotter, "solid");
  1386. /* fall through */
  1387. case AXES_AND_BOX:
  1388. case BOX:
  1389. if (!multigrapher->x_axis.switch_axis_end)
  1390. {
  1391. pl_fmove_r (multigrapher->plotter,
  1392. XN (multigrapher->x_axis.alt_other_axis_loc),
  1393. YV (yval));
  1394. pl_fcont_r (multigrapher->plotter,
  1395. XN (multigrapher->x_axis.alt_other_axis_loc)
  1396. - (SS (multigrapher->tick_size)
  1397. + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
  1398. : -multigrapher->half_line_width)),
  1399. YV (yval));
  1400. }
  1401. else
  1402. {
  1403. pl_fmove_r (multigrapher->plotter,
  1404. XN (multigrapher->x_axis.other_axis_loc),
  1405. YV (yval));
  1406. pl_fcont_r (multigrapher->plotter,
  1407. XN (multigrapher->x_axis.other_axis_loc)
  1408. + (SS (multigrapher->tick_size)
  1409. + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
  1410. : -multigrapher->half_line_width)),
  1411. YV (yval));
  1412. }
  1413. /* fall through */
  1414. case AXES:
  1415. case AXES_AT_ORIGIN:
  1416. if (!multigrapher->x_axis.switch_axis_end)
  1417. {
  1418. pl_fmove_r (multigrapher->plotter,
  1419. XN (multigrapher->x_axis.other_axis_loc),
  1420. YV (yval));
  1421. pl_fcont_r (multigrapher->plotter,
  1422. XN (multigrapher->x_axis.other_axis_loc)
  1423. + (SS (multigrapher->tick_size)
  1424. + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
  1425. : -multigrapher->half_line_width)),
  1426. YV (yval));
  1427. }
  1428. else
  1429. {
  1430. pl_fmove_r (multigrapher->plotter,
  1431. XN (multigrapher->x_axis.alt_other_axis_loc),
  1432. YV (yval));
  1433. pl_fcont_r (multigrapher->plotter,
  1434. XN (multigrapher->x_axis.alt_other_axis_loc)
  1435. - (SS (multigrapher->tick_size)
  1436. + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
  1437. : -multigrapher->half_line_width)),
  1438. YV (yval));
  1439. }
  1440. break;
  1441. default: /* shouldn't happen */
  1442. break;
  1443. }
  1444. }
  1445. if (multigrapher->y_axis.have_lin_subticks)
  1446. {
  1447. double subtick_size; /* libplot coordinates */
  1448. /* linearly spaced subticks on a log axis are as long as regular ticks */
  1449. subtick_size = (multigrapher->y_axis.type == A_LOG10
  1450. ? SS(multigrapher->tick_size) : SS(multigrapher->subtick_size));
  1451. /* Plot the linearly spaced subtick marks on the ordinate */
  1452. for (i = multigrapher->y_axis.min_lin_subtick_count; i <= multigrapher->y_axis.max_lin_subtick_count; i++)
  1453. /* range can be empty */
  1454. {
  1455. yval = i * multigrapher->y_axis.lin_subtick_spacing;
  1456. /* discard subtick locations outside plotting area */
  1457. if (yval < multigrapher->y_trans.input_min - FUZZ * yrange
  1458. || yval > multigrapher->y_trans.input_max + FUZZ * yrange)
  1459. continue;
  1460. /* Plot the tick marks on the y-axis, and horizontal grid lines. */
  1461. switch (multigrapher->grid_spec)
  1462. {
  1463. case AXES_AND_BOX_AND_GRID:
  1464. case AXES_AND_BOX:
  1465. case BOX:
  1466. if (!multigrapher->x_axis.switch_axis_end)
  1467. {
  1468. pl_fmove_r (multigrapher->plotter,
  1469. XN (multigrapher->x_axis.alt_other_axis_loc),
  1470. YV (yval));
  1471. pl_fcont_r (multigrapher->plotter,
  1472. XN (multigrapher->x_axis.alt_other_axis_loc)
  1473. - (subtick_size
  1474. + (subtick_size > 0.0 ? multigrapher->half_line_width
  1475. : -multigrapher->half_line_width)),
  1476. YV (yval));
  1477. }
  1478. else
  1479. {
  1480. pl_fmove_r (multigrapher->plotter,
  1481. XN (multigrapher->x_axis.other_axis_loc),
  1482. YV (yval));
  1483. pl_fcont_r (multigrapher->plotter,
  1484. XN (multigrapher->x_axis.other_axis_loc)
  1485. + (subtick_size
  1486. + (subtick_size > 0.0 ? multigrapher->half_line_width
  1487. : -multigrapher->half_line_width)),
  1488. YV (yval));
  1489. }
  1490. /* fall through */
  1491. case AXES:
  1492. case AXES_AT_ORIGIN:
  1493. if (!multigrapher->x_axis.switch_axis_end)
  1494. {
  1495. pl_fmove_r (multigrapher->plotter,
  1496. XN (multigrapher->x_axis.other_axis_loc),
  1497. YV (yval));
  1498. pl_fcont_r (multigrapher->plotter,
  1499. XN (multigrapher->x_axis.other_axis_loc)
  1500. + (subtick_size
  1501. + (subtick_size > 0.0 ? multigrapher->half_line_width
  1502. : -multigrapher->half_line_width)),
  1503. YV (yval));
  1504. }
  1505. else
  1506. {
  1507. pl_fmove_r (multigrapher->plotter,
  1508. XN (multigrapher->x_axis.alt_other_axis_loc),
  1509. YV (yval));
  1510. pl_fcont_r (multigrapher->plotter,
  1511. XN (multigrapher->x_axis.alt_other_axis_loc)
  1512. - (subtick_size
  1513. + (subtick_size > 0.0 ? multigrapher->half_line_width
  1514. : -multigrapher->half_line_width)),
  1515. YV (yval));
  1516. }
  1517. break;
  1518. default: /* shouldn't happen */
  1519. break;
  1520. }
  1521. }
  1522. }
  1523. /* plot a horizontal dotted line at y = 0 */
  1524. if (multigrapher->grid_spec != AXES_AT_ORIGIN
  1525. && multigrapher->grid_spec != BOX
  1526. && multigrapher->y_axis.type == A_LINEAR
  1527. && multigrapher->y_trans.input_min * multigrapher->y_trans.input_max < 0.0)
  1528. {
  1529. pl_linemod_r (multigrapher->plotter, "dotted");
  1530. pl_fline_r (multigrapher->plotter,
  1531. XP(XSQ(0.0)), YV(0.0), XP(XSQ(1.0)), YV(0.0));
  1532. pl_linemod_r (multigrapher->plotter, "solid");
  1533. }
  1534. }
  1535. /* 5. DRAW LOGARITHMIC SUBSUBTICKS AND THEIR LABELS ON ABSCISSA */
  1536. /* first, draw normal logarithmic subsubticks if any */
  1537. if (multigrapher->grid_spec != NO_AXES && multigrapher->x_axis.have_normal_subsubticks
  1538. && !multigrapher->x_axis.user_specified_subsubticks && !multigrapher->x_axis.omit_ticks)
  1539. {
  1540. int i, m, imin, imax;
  1541. double xval, xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
  1542. /* compute an integer range (of powers of 10) large enough to include
  1543. the entire desired axis */
  1544. imin = (int)(floor (multigrapher->x_trans.input_min - FUZZ * xrange));
  1545. imax = (int)(ceil (multigrapher->x_trans.input_max + FUZZ * xrange));
  1546. for (i = imin; i < imax; i++)
  1547. {
  1548. for (m = 1; m <= 9 ; m++)
  1549. {
  1550. xval = i + log10 ((double)m);
  1551. /* Plot subsubtick and label, if desired. */
  1552. /* N.B. if tick is outside axis range, nothing will be printed */
  1553. plot_abscissa_log_subsubtick (multigrapher, xval);
  1554. }
  1555. }
  1556. }
  1557. /* second, draw user-specified logarithmic subsubticks instead, if any */
  1558. if (multigrapher->grid_spec != NO_AXES && multigrapher->x_axis.user_specified_subsubticks
  1559. && !multigrapher->x_axis.omit_ticks)
  1560. {
  1561. int i, imin, imax;
  1562. double xval, xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
  1563. /* compute an integer range large enough to include the entire
  1564. desired axis */
  1565. imin = (int)(floor (pow (10.0, multigrapher->x_trans.input_min - FUZZ * xrange)
  1566. / multigrapher->x_axis.subsubtick_spacing));
  1567. imax = (int)(ceil (pow (10.0, multigrapher->x_trans.input_max + FUZZ * xrange)
  1568. / multigrapher->x_axis.subsubtick_spacing));
  1569. /* draw user-specified subsubticks */
  1570. for (i = imin; i <= imax; i++)
  1571. {
  1572. xval = log10 (i * multigrapher->x_axis.subsubtick_spacing);
  1573. /* Plot subsubtick and label, if desired. */
  1574. /* N.B. if tick is outside axis range, nothing will be printed */
  1575. plot_abscissa_log_subsubtick (multigrapher, xval);
  1576. }
  1577. }
  1578. /* 6. DRAW LOGARITHMIC SUBSUBTICKS AND THEIR LABELS ON ORDINATE */
  1579. /* first, draw normal logarithmic subsubticks if any */
  1580. if (multigrapher->grid_spec != NO_AXES && multigrapher->y_axis.have_normal_subsubticks
  1581. && !multigrapher->y_axis.user_specified_subsubticks && !multigrapher->y_axis.omit_ticks)
  1582. {
  1583. int i, m, imin, imax;
  1584. double yval, yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
  1585. /* compute an integer range (of powers of 10) large enough to include
  1586. the entire desired axis */
  1587. imin = (int)(floor (multigrapher->y_trans.input_min - FUZZ * yrange));
  1588. imax = (int)(ceil (multigrapher->y_trans.input_max + FUZZ * yrange));
  1589. /* draw normal subticks */
  1590. for (i = imin; i < imax; i++)
  1591. {
  1592. for (m = 1; m <= 9; m++)
  1593. {
  1594. yval = i + log10 ((double)m);
  1595. /* Plot subsubtick and label, if desired. */
  1596. /* N.B. if tick is outside axis range, nothing will be printed */
  1597. plot_ordinate_log_subsubtick (multigrapher, yval);
  1598. }
  1599. }
  1600. }
  1601. /* second, draw user-specified logarithmic subsubticks instead, if any */
  1602. if (multigrapher->grid_spec != NO_AXES && multigrapher->y_axis.user_specified_subsubticks
  1603. && !multigrapher->y_axis.omit_ticks)
  1604. {
  1605. int i, imin, imax;
  1606. double yval, yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
  1607. /* compute an integer range large enough to include the entire
  1608. desired axis */
  1609. imin = (int)(floor (pow (10.0, multigrapher->y_trans.input_min - FUZZ * yrange)
  1610. / multigrapher->y_axis.subsubtick_spacing));
  1611. imax = (int)(ceil (pow (10.0, multigrapher->y_trans.input_max + FUZZ * yrange)
  1612. / multigrapher->y_axis.subsubtick_spacing));
  1613. /* draw user-specified subsubticks */
  1614. for (i = imin; i <= imax; i++)
  1615. {
  1616. yval = log10 (i * multigrapher->y_axis.subsubtick_spacing);
  1617. /* Plot subsubtick and label, if desired. */
  1618. /* N.B. if tick is outside axis range, nothing will be printed */
  1619. plot_ordinate_log_subsubtick (multigrapher, yval);
  1620. }
  1621. }
  1622. /* 7. DRAW THE ABSCISSA LABEL */
  1623. if ((multigrapher->grid_spec != NO_AXES)
  1624. && multigrapher->x_axis.label != NULL && multigrapher->x_axis.label != '\0')
  1625. {
  1626. double x_axis_font_size;
  1627. double xloc;
  1628. /* switch to our font for drawing x axis label and tick labels */
  1629. pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
  1630. x_axis_font_size = pl_ffontsize_r (multigrapher->plotter,
  1631. SS(multigrapher->x_axis.font_size));
  1632. if (multigrapher->grid_spec != AXES_AT_ORIGIN)
  1633. /* center the label on the axis */
  1634. xloc = 0.5 * (multigrapher->x_trans.input_max + multigrapher->x_trans.input_min);
  1635. else
  1636. {
  1637. if ((multigrapher->y_axis.other_axis_loc == multigrapher->y_trans.input_min)
  1638. || (multigrapher->y_axis.other_axis_loc == multigrapher->y_trans.input_max))
  1639. xloc = 0.5 * (multigrapher->x_trans.input_max + multigrapher->x_trans.input_min);
  1640. else
  1641. /* center label in the larger of the two halves */
  1642. xloc =
  1643. multigrapher->x_trans.input_max-multigrapher->x_axis.other_axis_loc >= multigrapher->x_axis.other_axis_loc-multigrapher->x_trans.input_min ?
  1644. 0.5 * (multigrapher->x_trans.input_max + multigrapher->x_axis.other_axis_loc) :
  1645. 0.5 * (multigrapher->x_axis.other_axis_loc + multigrapher->x_trans.input_min);
  1646. }
  1647. if (!multigrapher->y_axis.switch_axis_end) /* axis on bottom, label below it */
  1648. {
  1649. pl_fmove_r (multigrapher->plotter,
  1650. XV (xloc),
  1651. YN (multigrapher->y_axis.other_axis_loc)
  1652. - (SS ((multigrapher->tick_size >= 0.0 ? 0.875 : 2.125)
  1653. * fabs(multigrapher->tick_size))
  1654. + (6 * x_axis_font_size)/5
  1655. + multigrapher->half_line_width));
  1656. pl_alabel_r (multigrapher->plotter,
  1657. 'c', 't', multigrapher->x_axis.label);
  1658. }
  1659. else /* axis on top, label above it */
  1660. {
  1661. pl_fmove_r (multigrapher->plotter,
  1662. XV (xloc),
  1663. YN (multigrapher->y_axis.alt_other_axis_loc)
  1664. + (SS ((multigrapher->tick_size >= 0.0 ? 0.875 : 2.125)
  1665. * fabs(multigrapher->tick_size))
  1666. + (6 * x_axis_font_size)/5
  1667. + multigrapher->half_line_width));
  1668. pl_alabel_r (multigrapher->plotter,
  1669. 'c', 'b', multigrapher->x_axis.label);
  1670. }
  1671. }
  1672. /* 8. DRAW THE ORDINATE LABEL */
  1673. if ((multigrapher->grid_spec != NO_AXES)
  1674. && (multigrapher->y_axis.label != NULL && *(multigrapher->y_axis.label) != '\0'))
  1675. {
  1676. double y_axis_font_size;
  1677. double yloc;
  1678. /* switch to our font for drawing y axis label and tick labels */
  1679. pl_fontname_r (multigrapher->plotter, multigrapher->y_axis.font_name);
  1680. y_axis_font_size = pl_ffontsize_r (multigrapher->plotter,
  1681. SS(multigrapher->y_axis.font_size));
  1682. if (multigrapher->grid_spec != AXES_AT_ORIGIN)
  1683. /* center the label on the axis */
  1684. yloc = 0.5 * (multigrapher->y_trans.input_min + multigrapher->y_trans.input_max);
  1685. else
  1686. {
  1687. if ((multigrapher->x_axis.other_axis_loc == multigrapher->x_trans.input_min)
  1688. || (multigrapher->x_axis.other_axis_loc == multigrapher->x_trans.input_max))
  1689. yloc = 0.5 * (multigrapher->y_trans.input_min + multigrapher->y_trans.input_max);
  1690. else
  1691. /* center label in the larger of the two halves */
  1692. yloc =
  1693. multigrapher->y_trans.input_max-multigrapher->y_axis.other_axis_loc >= multigrapher->y_axis.other_axis_loc-multigrapher->y_trans.input_min ?
  1694. 0.5 * (multigrapher->y_trans.input_max + multigrapher->y_axis.other_axis_loc) :
  1695. 0.5 * (multigrapher->y_axis.other_axis_loc + multigrapher->y_trans.input_min);
  1696. }
  1697. /* a relic of temps perdus */
  1698. #define libplot_has_font_metrics 1
  1699. if (!multigrapher->x_axis.switch_axis_end)
  1700. {
  1701. pl_fmove_r (multigrapher->plotter,
  1702. XN (multigrapher->x_axis.other_axis_loc)
  1703. - (libplot_has_font_metrics ?
  1704. (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
  1705. * fabs(multigrapher->tick_size))
  1706. + 1.15 * multigrapher->y_axis.max_label_width
  1707. + 0.5 * y_axis_font_size
  1708. + multigrapher->half_line_width)
  1709. : (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
  1710. * fabs(multigrapher->tick_size)) /* backup */
  1711. + 1.0 * y_axis_font_size
  1712. + multigrapher->half_line_width)),
  1713. YV(yloc));
  1714. if (libplot_has_font_metrics
  1715. && !multigrapher->no_rotate_y_label) /* can rotate label */
  1716. {
  1717. pl_textangle_r (multigrapher->plotter, 90);
  1718. pl_alabel_r (multigrapher->plotter,
  1719. 'c', 'x', multigrapher->y_axis.label);
  1720. pl_textangle_r (multigrapher->plotter, 0);
  1721. }
  1722. else
  1723. /* non-rotated axis label, right justified */
  1724. pl_alabel_r (multigrapher->plotter,
  1725. 'r', 'c', multigrapher->y_axis.label);
  1726. }
  1727. else
  1728. {
  1729. pl_fmove_r (multigrapher->plotter,
  1730. XN (multigrapher->x_axis.alt_other_axis_loc)
  1731. + (libplot_has_font_metrics ?
  1732. (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
  1733. * fabs(multigrapher->tick_size))
  1734. + 1.15 * multigrapher->y_axis.max_label_width
  1735. + 0.5 * y_axis_font_size
  1736. + multigrapher->half_line_width)
  1737. : (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
  1738. * fabs(multigrapher->tick_size)) /* backup */
  1739. + 1.0 * y_axis_font_size
  1740. + multigrapher->half_line_width)),
  1741. YV(yloc));
  1742. if (libplot_has_font_metrics
  1743. && !multigrapher->no_rotate_y_label) /* can rotate label */
  1744. {
  1745. pl_textangle_r (multigrapher->plotter, 90);
  1746. pl_alabel_r (multigrapher->plotter,
  1747. 'c', 't', multigrapher->y_axis.label);
  1748. pl_textangle_r (multigrapher->plotter, 0);
  1749. }
  1750. else
  1751. /* non-rotated axis label, left justified */
  1752. pl_alabel_r (multigrapher->plotter,
  1753. 'l', 'c', multigrapher->y_axis.label);
  1754. }
  1755. }
  1756. /* END OF TASKS */
  1757. /* flush frame to device */
  1758. pl_flushpl_r (multigrapher->plotter);
  1759. pl_restorestate_r (multigrapher->plotter);
  1760. if (multigrapher->grid_spec != NO_AXES)
  1761. {
  1762. if (!tick_warning_printed &&
  1763. ((!multigrapher->x_axis.omit_ticks && multigrapher->x_axis.labelled_ticks <= 2)
  1764. || (!multigrapher->y_axis.omit_ticks && multigrapher->y_axis.labelled_ticks <= 2)))
  1765. {
  1766. fprintf (stderr,
  1767. "%s: the tick spacing is adjusted, as there were too few labelled axis ticks\n",
  1768. progname);
  1769. tick_warning_printed = true;
  1770. }
  1771. }
  1772. }
  1773. /* plot_abscissa_log_subsubtick() and plot_ordinate_log_subsubtick() are
  1774. called to plot both normal log subticks and special (user-requested)
  1775. ones */
  1776. /* ARGS: xval = log of location */
  1777. static void
  1778. plot_abscissa_log_subsubtick (Multigrapher *multigrapher, double xval)
  1779. {
  1780. double xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
  1781. /* there is no way you could use longer labels on tick marks! */
  1782. char labelbuf[2048];
  1783. double tick_size = SS(multigrapher->tick_size); /* for positioning labels */
  1784. double subsubtick_size = SS(multigrapher->subtick_size);
  1785. /* switch to our font for drawing x axis label and tick labels */
  1786. pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
  1787. pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->x_axis.font_size));
  1788. /* discard subsubtick locations outside plotting area */
  1789. if (xval < multigrapher->x_trans.input_min - FUZZ * xrange
  1790. || xval > multigrapher->x_trans.input_max + FUZZ * xrange)
  1791. return;
  1792. /* label subsubtick if it seems appropriate */
  1793. if (multigrapher->x_axis.user_specified_subsubticks)
  1794. {
  1795. print_tick_label (labelbuf,
  1796. &multigrapher->x_axis, &multigrapher->x_trans,
  1797. pow (10.0, xval));
  1798. if (!multigrapher->y_axis.switch_axis_end)
  1799. {
  1800. pl_fmove_r (multigrapher->plotter,
  1801. XV (xval),
  1802. YN (multigrapher->y_axis.other_axis_loc)
  1803. - ((tick_size >= 0 ? 0.75 : 1.75)
  1804. * fabs((double)tick_size)
  1805. + multigrapher->half_line_width));
  1806. pl_alabel_r (multigrapher->plotter, 'c', 't', labelbuf);
  1807. multigrapher->x_axis.labelled_ticks++;
  1808. }
  1809. else
  1810. {
  1811. pl_fmove_r (multigrapher->plotter,
  1812. XV (xval),
  1813. YN (multigrapher->y_axis.alt_other_axis_loc)
  1814. + ((tick_size >= 0 ? 0.75 : 1.75)
  1815. * fabs((double)tick_size)
  1816. + multigrapher->half_line_width));
  1817. pl_alabel_r (multigrapher->plotter, 'c', 'b', labelbuf);
  1818. multigrapher->x_axis.labelled_ticks++;
  1819. }
  1820. }
  1821. /* draw subsubtick */
  1822. switch (multigrapher->grid_spec)
  1823. {
  1824. case AXES_AND_BOX_AND_GRID:
  1825. pl_linemod_r (multigrapher->plotter, "dotted");
  1826. pl_fmove_r (multigrapher->plotter, XV (xval), YP(YSQ(0.0)));
  1827. pl_fcont_r (multigrapher->plotter, XV (xval), YP(YSQ(1.0)));
  1828. pl_linemod_r (multigrapher->plotter, "solid");
  1829. /* fall through */
  1830. case AXES_AND_BOX:
  1831. case BOX:
  1832. if (!multigrapher->y_axis.switch_axis_end)
  1833. {
  1834. pl_fmove_r (multigrapher->plotter,
  1835. XV (xval),
  1836. YN (multigrapher->y_axis.alt_other_axis_loc));
  1837. pl_fcont_r (multigrapher->plotter,
  1838. XV (xval),
  1839. YN (multigrapher->y_axis.alt_other_axis_loc)
  1840. - (subsubtick_size
  1841. + (subsubtick_size > 0.0
  1842. ? multigrapher->half_line_width
  1843. : -multigrapher->half_line_width)));
  1844. }
  1845. else
  1846. {
  1847. pl_fmove_r (multigrapher->plotter,
  1848. XV (xval),
  1849. YN (multigrapher->y_axis.other_axis_loc));
  1850. pl_fcont_r (multigrapher->plotter,
  1851. XV (xval),
  1852. YN (multigrapher->y_axis.other_axis_loc)
  1853. + (subsubtick_size
  1854. + (subsubtick_size > 0.0
  1855. ? multigrapher->half_line_width
  1856. : -multigrapher->half_line_width)));
  1857. }
  1858. /* fall through */
  1859. case AXES:
  1860. case AXES_AT_ORIGIN:
  1861. if (!multigrapher->y_axis.switch_axis_end)
  1862. {
  1863. pl_fmove_r (multigrapher->plotter,
  1864. XV (xval),
  1865. YN (multigrapher->y_axis.other_axis_loc));
  1866. pl_fcont_r (multigrapher->plotter,
  1867. XV (xval),
  1868. YN (multigrapher->y_axis.other_axis_loc)
  1869. + (subsubtick_size
  1870. + (subsubtick_size > 0.0
  1871. ? multigrapher->half_line_width
  1872. : -multigrapher->half_line_width)));
  1873. }
  1874. else
  1875. {
  1876. pl_fmove_r (multigrapher->plotter,
  1877. XV (xval),
  1878. YN (multigrapher->y_axis.alt_other_axis_loc));
  1879. pl_fcont_r (multigrapher->plotter,
  1880. XV (xval),
  1881. YN (multigrapher->y_axis.alt_other_axis_loc)
  1882. - (subsubtick_size
  1883. + (subsubtick_size > 0.0
  1884. ? multigrapher->half_line_width
  1885. : -multigrapher->half_line_width)));
  1886. }
  1887. break;
  1888. default: /* shouldn't happen */
  1889. break;
  1890. }
  1891. }
  1892. /* ARGS: yval = log of location */
  1893. static void
  1894. plot_ordinate_log_subsubtick (Multigrapher *multigrapher, double yval)
  1895. {
  1896. double yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
  1897. /* there is no way you could use longer labels on tick marks! */
  1898. char labelbuf[2048];
  1899. double tick_size = SS(multigrapher->tick_size); /* for positioning labels */
  1900. double subsubtick_size = SS(multigrapher->subtick_size);
  1901. /* switch to our font for drawing y axis label and tick labels */
  1902. pl_fontname_r (multigrapher->plotter, multigrapher->y_axis.font_name);
  1903. pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->y_axis.font_size));
  1904. /* discard subsubtick locations outside plotting area */
  1905. if (yval < multigrapher->y_trans.input_min - FUZZ * yrange
  1906. || yval > multigrapher->y_trans.input_max + FUZZ * yrange)
  1907. return;
  1908. /* label subsubtick if it seems appropriate */
  1909. if (multigrapher->y_axis.user_specified_subsubticks)
  1910. {
  1911. double new_width;
  1912. print_tick_label (labelbuf,
  1913. &multigrapher->y_axis, &multigrapher->y_trans,
  1914. pow (10.0, yval));
  1915. if (!multigrapher->x_axis.switch_axis_end)
  1916. {
  1917. pl_fmove_r (multigrapher->plotter,
  1918. XN(multigrapher->x_axis.other_axis_loc)
  1919. - ((tick_size >= 0 ? 0.75 : 1.75)
  1920. * fabs((double)tick_size)
  1921. + multigrapher->half_line_width),
  1922. YV (yval));
  1923. new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
  1924. pl_alabel_r (multigrapher->plotter, 'r', 'c', labelbuf);
  1925. multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
  1926. multigrapher->y_axis.labelled_ticks++;
  1927. }
  1928. else
  1929. {
  1930. pl_fmove_r (multigrapher->plotter,
  1931. XN(multigrapher->x_axis.alt_other_axis_loc)
  1932. + ((tick_size >= 0 ? 0.75 : 1.75)
  1933. * fabs((double)tick_size)
  1934. + multigrapher->half_line_width),
  1935. YV (yval));
  1936. new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
  1937. pl_alabel_r (multigrapher->plotter, 'l', 'c', labelbuf);
  1938. multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
  1939. multigrapher->y_axis.labelled_ticks++;
  1940. }
  1941. }
  1942. /* draw subsubtick */
  1943. switch (multigrapher->grid_spec)
  1944. {
  1945. case AXES_AND_BOX_AND_GRID:
  1946. pl_linemod_r (multigrapher->plotter, "dotted");
  1947. pl_fmove_r (multigrapher->plotter, XP(XSQ(0.0)), YV (yval));
  1948. pl_fcont_r (multigrapher->plotter, XP(XSQ(1.0)), YV (yval));
  1949. pl_linemod_r (multigrapher->plotter, "solid");
  1950. /* fall through */
  1951. case AXES_AND_BOX:
  1952. case BOX:
  1953. if (!multigrapher->x_axis.switch_axis_end)
  1954. {
  1955. pl_fmove_r (multigrapher->plotter,
  1956. XN (multigrapher->x_axis.alt_other_axis_loc),
  1957. YV (yval));
  1958. pl_fcont_r (multigrapher->plotter,
  1959. XN (multigrapher->x_axis.alt_other_axis_loc)
  1960. - (subsubtick_size
  1961. + (subsubtick_size > 0.0
  1962. ? multigrapher->half_line_width
  1963. : -multigrapher->half_line_width)),
  1964. YV (yval));
  1965. }
  1966. else
  1967. {
  1968. pl_fmove_r (multigrapher->plotter,
  1969. XN (multigrapher->x_axis.other_axis_loc),
  1970. YV (yval));
  1971. pl_fcont_r (multigrapher->plotter,
  1972. XN (multigrapher->x_axis.other_axis_loc)
  1973. + (subsubtick_size
  1974. + (subsubtick_size > 0.0
  1975. ? multigrapher->half_line_width
  1976. : -multigrapher->half_line_width)),
  1977. YV (yval));
  1978. }
  1979. /* fall through */
  1980. case AXES:
  1981. case AXES_AT_ORIGIN:
  1982. if (!multigrapher->x_axis.switch_axis_end)
  1983. {
  1984. pl_fmove_r (multigrapher->plotter,
  1985. XN (multigrapher->x_axis.other_axis_loc),
  1986. YV (yval));
  1987. pl_fcont_r (multigrapher->plotter,
  1988. XN (multigrapher->x_axis.other_axis_loc)
  1989. + (subsubtick_size
  1990. + (subsubtick_size > 0.0
  1991. ? multigrapher->half_line_width
  1992. : -multigrapher->half_line_width)),
  1993. YV (yval));
  1994. }
  1995. else
  1996. {
  1997. pl_fmove_r (multigrapher->plotter,
  1998. XN (multigrapher->x_axis.alt_other_axis_loc),
  1999. YV (yval));
  2000. pl_fcont_r (multigrapher->plotter,
  2001. XN (multigrapher->x_axis.alt_other_axis_loc)
  2002. - (subsubtick_size
  2003. + (multigrapher->tick_size > 0.0
  2004. ? multigrapher->half_line_width
  2005. : -multigrapher->half_line_width)),
  2006. YV (yval));
  2007. }
  2008. break;
  2009. default: /* shouldn't happen */
  2010. break;
  2011. }
  2012. }
  2013. /* set_line_style() maps from line modes to physical line modes. See
  2014. * explanation at head of file. */
  2015. static void
  2016. set_line_style (Multigrapher *multigrapher, int style, const char *line_color,
  2017. bool use_color)
  2018. {
  2019. if (!use_color) /* monochrome */
  2020. {
  2021. if (style > 0)
  2022. /* don't issue pl_linemod_r() if style<=0, since no polyline will
  2023. be drawn */
  2024. {
  2025. int i;
  2026. i = (style - 1) % NO_OF_LINEMODES;
  2027. pl_linemod_r (multigrapher->plotter, linemodes[i]);
  2028. }
  2029. /* use same color as used for plot frame */
  2030. pl_colorname_r (multigrapher->plotter, multigrapher->frame_color);
  2031. }
  2032. else /* color */
  2033. {
  2034. int i, j;
  2035. if (style > 0) /* solid lines, various colors */
  2036. {
  2037. if (line_color && line_color[0])
  2038. i = (style - 1) % NO_OF_LINEMODES;
  2039. else
  2040. {
  2041. i = ((style - 1) / NO_OF_LINEMODES) % NO_OF_LINEMODES;
  2042. j = (style - 1) % NO_OF_LINEMODES;
  2043. }
  2044. pl_linemod_r (multigrapher->plotter, linemodes[i]);
  2045. }
  2046. else if (style == 0) /* use first color, as if -m 1 was spec'd */
  2047. /* (no line will be drawn) */
  2048. j = 0;
  2049. else /* neg. pl_linemode_r (no line will be drawn)*/
  2050. j = (-style - 1) % (NO_OF_LINEMODES - 1);
  2051. pl_colorname_r (multigrapher->plotter,
  2052. line_color && line_color[0] ? line_color
  2053. : colorstyle[j]);
  2054. }
  2055. }
  2056. /* plot_point_array() calls plot_point() on each point in an array of
  2057. * points.
  2058. */
  2059. void
  2060. plot_point_array (Multigrapher *multigrapher, const Point *p, int length)
  2061. {
  2062. int index;
  2063. for (index = 0; index < length; index++)
  2064. plot_point (multigrapher, &(p[index]));
  2065. }
  2066. void
  2067. plot_legend (Multigrapher *multigrapher, Legend *legends, int length, double x, double y, double font_size)
  2068. {
  2069. if (length==0)
  2070. return;
  2071. pl_savestate_r (multigrapher->plotter);
  2072. pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
  2073. font_size = pl_ffontsize_r (multigrapher->plotter, SS(font_size));
  2074. double legend_width = 0.;
  2075. int i;
  2076. for (i=0; i!=length; ++i)
  2077. legend_width = fmax(legend_width, pl_flabelwidth_r(multigrapher->plotter, legends[i].label));
  2078. double posx1 = XP(XSQ(1.0)) - legend_width - font_size*0.5;
  2079. double posx0 = XP(XSQ(0.0)) + font_size*4.0;
  2080. x = x * posx1 + (1-x) * posx0;
  2081. double posy1 = YP(YSQ(1.0)) - font_size*1.0;
  2082. double posy0 = YP(YSQ(0.0)) + font_size*length;
  2083. y = y * posy1 + (1-y) * posy0;
  2084. double xunscale = 1/(XV (1.) - XV (0.));
  2085. double yunscale = 1/(YV (1.) - YV (0.));
  2086. double dimx = font_size * xunscale;
  2087. /* white background behind legend */
  2088. pl_savestate_r (multigrapher->plotter);
  2089. /* use user-specified background color (if any) instead of white */
  2090. if (pl_havecap_r (multigrapher->plotter, "SETTABLE_BACKGROUND") != 0
  2091. && multigrapher->bg_color)
  2092. pl_colorname_r (multigrapher->plotter, multigrapher->bg_color);
  2093. else
  2094. pl_colorname_r (multigrapher->plotter, "white");
  2095. pl_filltype_r (multigrapher->plotter, 1);
  2096. pl_fbox_r (multigrapher->plotter,
  2097. x /* left of text */ - 3.5*dimx - 0.25*font_size
  2098. /* left of marker */ - 2.5*dimx ,
  2099. y - font_size*(length-0.25),
  2100. x + legend_width + font_size*0.25,
  2101. y + font_size*0.5);
  2102. pl_restorestate_r (multigrapher->plotter);
  2103. for (i=0; i!=length; ++i)
  2104. {
  2105. /* legend marker */
  2106. Point *p = &(legends[i].point);
  2107. double ystep = -(i * font_size);
  2108. int symbol = p->symbol;
  2109. if (p->linemode > 0)
  2110. {
  2111. p->y = YVI(y) + ystep*yunscale;
  2112. p->pendown = false;
  2113. p->symbol = 0;
  2114. p->x = XVI(x) - 3.5*dimx;
  2115. plot_point (multigrapher, p);
  2116. p->pendown = true;
  2117. p->symbol = symbol;
  2118. p->x = XVI(x) - 2*dimx;
  2119. plot_point (multigrapher, p);
  2120. p->x = XVI(x) - 0.5*dimx;
  2121. p->symbol = 0;
  2122. plot_point (multigrapher, p);
  2123. p->pendown = false;
  2124. }
  2125. else
  2126. {
  2127. p->y = YVI(y) + ystep*yunscale;
  2128. p->pendown = false;
  2129. p->symbol = 0;
  2130. // @BUG not entirely clear: I get spurious dots if linemode is left to 0.
  2131. // $GRAPH --font-name HersheyCyrillic -C -T ps --reposition 0. 0. 1 -m 0 -S 74 0.05 --legend grey -c "#808080" data1 -W 0.005 --legend blue -c blue data2 --legend red%label -m 16 -c red data3 --place-legend 1 > plot.ps && evince plot.ps
  2132. if (p->linemode == 0)
  2133. p->linemode = -1;
  2134. p->x = XVI(x) - 1.0*dimx;
  2135. plot_point (multigrapher, p);
  2136. p->pendown = true;
  2137. p->symbol = symbol;
  2138. plot_point (multigrapher, p);
  2139. p->pendown = false;
  2140. }
  2141. /* legend text */
  2142. pl_fmove_r (multigrapher->plotter, x, y+ystep);
  2143. pl_colorname_r (multigrapher->plotter, "black");
  2144. pl_alabel_r (multigrapher->plotter, 'l', 'c', legends[i].label);
  2145. }
  2146. pl_restorestate_r (multigrapher->plotter);
  2147. }
  2148. /* plot_point() plots a single point, including the appropriate symbol and
  2149. * errorbar(s) if any. It may call either pl_fcont_r() or pl_fmove_r(),
  2150. * depending on whether the pendown flag is set or not. Gnuplot-style
  2151. * clipping (clip mode = 0,1,2) is supported.
  2152. *
  2153. * plot_point() makes heavy use of the multigrapher->x_trans and
  2154. * multigrapher->y_trans structures, which specify the linear
  2155. * transformation from user coordinates to device coordinates. It also
  2156. * updates the multigrapher's internal state variables. */
  2157. void
  2158. plot_point (Multigrapher *multigrapher, const Point *point)
  2159. {
  2160. double local_x0, local_y0, local_x1, local_y1;
  2161. int clipval;
  2162. /* If new polyline is beginning, take its line style, color/monochrome
  2163. attribute, and line width and fill fraction attributes from the first
  2164. point of the polyline. We assume all such attribute fields are the
  2165. same for all points in the polyline (our point reader arranges this
  2166. for us). */
  2167. if (!(point->pendown) || multigrapher->first_point_of_polyline)
  2168. {
  2169. int intfill;
  2170. set_line_style (multigrapher, point->linemode, point->line_color,
  2171. point->use_color);
  2172. /* N.B. linewidth < 0.0 means use libplot default */
  2173. pl_flinewidth_r (multigrapher->plotter,
  2174. point->line_width * (double)PLOT_SIZE);
  2175. if (point->fill_fraction < 0.0)
  2176. intfill = 0; /* transparent */
  2177. else /* guaranteed to be <= 1.0 */
  2178. intfill = 1 + IROUND((1.0 - point->fill_fraction) * 0xfffe);
  2179. pl_filltype_r (multigrapher->plotter, intfill);
  2180. }
  2181. /* determine endpoints of new line segment (for the first point of a
  2182. polyline, use a zero-length line segment) */
  2183. if (multigrapher->first_point_of_polyline)
  2184. {
  2185. local_x0 = point->x;
  2186. local_y0 = point->y;
  2187. }
  2188. else
  2189. {
  2190. local_x0 = multigrapher->oldpoint_x;
  2191. local_y0 = multigrapher->oldpoint_y;
  2192. }
  2193. local_x1 = point->x;
  2194. local_y1 = point->y;
  2195. /* save current point for use as endpoint of next line segment */
  2196. multigrapher->oldpoint_x = point->x;
  2197. multigrapher->oldpoint_y = point->y;
  2198. /* apply Cohen-Sutherland clipper to new line segment */
  2199. clipval = clip_line (multigrapher,
  2200. &local_x0, &local_y0, &local_x1, &local_y1);
  2201. if (!(clipval & ACCEPTED)) /* rejected in toto */
  2202. {
  2203. pl_fmove_r (multigrapher->plotter,
  2204. XV (point->x), YV (point->y)); /* move with pen up */
  2205. multigrapher->first_point_of_polyline = false;
  2206. return;
  2207. }
  2208. /* not rejected, ideally move with pen down */
  2209. if (point->pendown && (point->linemode > 0))
  2210. {
  2211. switch (multigrapher->clip_mode) /* gnuplot style clipping (0,1, or 2) */
  2212. {
  2213. case 0:
  2214. if ((clipval & CLIPPED_FIRST) || (clipval & CLIPPED_SECOND))
  2215. /* clipped on at least one end, so move with pen up */
  2216. pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
  2217. else
  2218. /* line segment within box, so move with pen down */
  2219. {
  2220. if (!multigrapher->first_point_of_polyline)
  2221. pl_fcont_r (multigrapher->plotter,
  2222. XV (point->x), YV (point->y));
  2223. else
  2224. pl_fmove_r (multigrapher->plotter,
  2225. XV (point->x), YV (point->y));
  2226. }
  2227. break;
  2228. case 1:
  2229. default:
  2230. if ((clipval & CLIPPED_FIRST) && (clipval & CLIPPED_SECOND))
  2231. /* both OOB, so move with pen up */
  2232. pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
  2233. else
  2234. /* at most one point is OOB */
  2235. {
  2236. if (clipval & CLIPPED_FIRST) /*current pt. OOB, new pt. not OOB*/
  2237. {
  2238. if (!multigrapher->first_point_of_polyline)
  2239. {
  2240. /* move to clipped current point, draw line segment */
  2241. pl_fmove_r (multigrapher->plotter,
  2242. XV (local_x0), YV (local_y0));
  2243. pl_fcont_r (multigrapher->plotter,
  2244. XV (point->x), YV (point->y));
  2245. }
  2246. else
  2247. pl_fmove_r (multigrapher->plotter,
  2248. XV (point->x), YV (point->y));
  2249. }
  2250. else /* current point not OOB, new point OOB */
  2251. {
  2252. if (!multigrapher->first_point_of_polyline)
  2253. {
  2254. /* draw line segment to clipped new point */
  2255. pl_fcont_r (multigrapher->plotter,
  2256. XV (local_x1), YV (local_y1));
  2257. /* N.B. lib's notion of position now differs from ours */
  2258. }
  2259. else
  2260. pl_fmove_r (multigrapher->plotter,
  2261. XV (point->x), YV (point->y));
  2262. }
  2263. }
  2264. break;
  2265. case 2:
  2266. if ((clipval & CLIPPED_FIRST) || multigrapher->first_point_of_polyline)
  2267. /* move to clipped current point if necc. */
  2268. pl_fmove_r (multigrapher->plotter, XV (local_x0), YV (local_y0));
  2269. /* draw line segment to clipped new point */
  2270. pl_fcont_r (multigrapher->plotter, XV (local_x1), YV (local_y1));
  2271. if (clipval & CLIPPED_SECOND)
  2272. /* new point OOB, so move to new point, breaking polyline */
  2273. pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
  2274. break;
  2275. }
  2276. }
  2277. else /* linemode=0 or pen up; so move with pen up */
  2278. pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
  2279. multigrapher->first_point_of_polyline = false;
  2280. /* if target point is OOB, return without plotting symbol or errorbar */
  2281. if (clipval & CLIPPED_SECOND)
  2282. return;
  2283. /* plot symbol and errorbar, doing a pl_savestate_r()--pl_restorestate()
  2284. to keep from breaking the polyline under construction (if any) */
  2285. if (point->symbol >= 32) /* yow, a character */
  2286. {
  2287. /* will do a font change, so save & restore state */
  2288. pl_savestate_r (multigrapher->plotter);
  2289. plot_errorbar (multigrapher, point);
  2290. pl_fontname_r (multigrapher->plotter, point->symbol_font_name);
  2291. pl_fmarker_r (multigrapher->plotter, XV(point->x), YV(point->y),
  2292. point->symbol, SS(point->symbol_size));
  2293. pl_restorestate_r (multigrapher->plotter);
  2294. }
  2295. else if (point->symbol > 0) /* a marker symbol */
  2296. {
  2297. if (point->linemode > 0)
  2298. /* drawing a line, so (to keep from breaking it) save & restore state*/
  2299. {
  2300. pl_savestate_r (multigrapher->plotter);
  2301. plot_errorbar (multigrapher, point); /* may or may not have one */
  2302. pl_fmarker_r (multigrapher->plotter, XV(point->x), YV(point->y),
  2303. point->symbol, SS(point->symbol_size));
  2304. pl_restorestate_r (multigrapher->plotter);
  2305. }
  2306. else
  2307. /* not drawing a line, so just place the marker */
  2308. {
  2309. plot_errorbar (multigrapher, point);
  2310. pl_fmarker_r (multigrapher->plotter, XV(point->x), YV(point->y),
  2311. point->symbol, SS(point->symbol_size));
  2312. }
  2313. }
  2314. else if (point->symbol == 0 && point->linemode == 0)
  2315. /* backward compatibility: -m 0 (even with -S 0) plots a dot */
  2316. {
  2317. plot_errorbar (multigrapher, point);
  2318. pl_fmarker_r (multigrapher->plotter,
  2319. XV(point->x), YV(point->y), M_DOT, SS(point->symbol_size));
  2320. }
  2321. else /* no symbol, but may be an errorbar */
  2322. plot_errorbar (multigrapher, point);
  2323. return;
  2324. }
  2325. /* clip_line() takes two points, the endpoints of a line segment, and
  2326. * destructively passes back two points: the endpoints of the line segment
  2327. * clipped by Cohen-Sutherland to the rectangular plotting area. Return
  2328. * value contains bitfields ACCEPTED, CLIPPED_FIRST, and CLIPPED_SECOND.
  2329. */
  2330. static int
  2331. clip_line (Multigrapher *multigrapher, double *x0_p, double *y0_p, double *x1_p, double *y1_p)
  2332. {
  2333. double x0 = *x0_p;
  2334. double y0 = *y0_p;
  2335. double x1 = *x1_p;
  2336. double y1 = *y1_p;
  2337. outcode outcode0 = compute_outcode (multigrapher, x0, y0, true);
  2338. outcode outcode1 = compute_outcode (multigrapher, x1, y1, true);
  2339. bool accepted;
  2340. int clipval = 0;
  2341. for ( ; ; )
  2342. {
  2343. if (!(outcode0 | outcode1)) /* accept */
  2344. {
  2345. accepted = true;
  2346. break;
  2347. }
  2348. else if (outcode0 & outcode1) /* reject */
  2349. {
  2350. accepted = false;
  2351. break;
  2352. }
  2353. else
  2354. {
  2355. /* at least one endpoint is outside; choose one that is */
  2356. outcode outcode_out = (outcode0 ? outcode0 : outcode1);
  2357. double x, y; /* intersection with clip edge */
  2358. if (outcode_out & RIGHT)
  2359. {
  2360. x = multigrapher->x_trans.input_max;
  2361. y = isinf (x0) ? y1 : y0 + (y1 - y0) * (x - x0) / (x1 - x0);
  2362. }
  2363. else if (outcode_out & LEFT)
  2364. {
  2365. x = multigrapher->x_trans.input_min;
  2366. y = isinf (x0) ? y1 : y0 + (y1 - y0) * (x - x0) / (x1 - x0);
  2367. }
  2368. else if (outcode_out & TOP)
  2369. {
  2370. y = multigrapher->y_trans.input_max;
  2371. x = isinf (y0) ? x1 : x0 + (x1 - x0) * (y - y0) / (y1 - y0);
  2372. }
  2373. else
  2374. {
  2375. y = multigrapher->y_trans.input_min;
  2376. x = isinf (y0) ? x1 : x0 + (x1 - x0) * (y - y0) / (y1 - y0);
  2377. }
  2378. if (outcode_out == outcode0)
  2379. {
  2380. x0 = x;
  2381. y0 = y;
  2382. outcode0 = compute_outcode (multigrapher, x0, y0, true);
  2383. }
  2384. else
  2385. {
  2386. x1 = x;
  2387. y1 = y;
  2388. outcode1 = compute_outcode (multigrapher, x1, y1, true);
  2389. }
  2390. }
  2391. }
  2392. if (accepted)
  2393. {
  2394. clipval |= ACCEPTED;
  2395. if ((x0 != *x0_p) || (y0 != *y0_p))
  2396. clipval |= CLIPPED_FIRST;
  2397. if ((x1 != *x1_p) || (y1 != *y1_p))
  2398. clipval |= CLIPPED_SECOND;
  2399. *x0_p = x0;
  2400. *y0_p = y0;
  2401. *x1_p = x1;
  2402. *y1_p = y1;
  2403. }
  2404. return clipval;
  2405. }
  2406. /* Compute usual Cohen-Sutherland outcode, containing bitfields LEFT,
  2407. RIGHT, BOTTOM, TOP. Nine possibilities:
  2408. {LEFT, interior, RIGHT} x {BOTTOM, interior, TOP}.
  2409. The `tolerant' flag specifies how we handle points on the boundary. */
  2410. static outcode
  2411. compute_outcode (Multigrapher *multigrapher, double x, double y, bool tolerant)
  2412. {
  2413. outcode code = 0;
  2414. double xfuzz = FUZZ * multigrapher->x_trans.input_range;
  2415. double yfuzz = FUZZ * multigrapher->y_trans.input_range;
  2416. int sign = (tolerant == true ? 1 : -1);
  2417. if (x > multigrapher->x_trans.input_max + sign * xfuzz)
  2418. code |= RIGHT;
  2419. else if (x < multigrapher->x_trans.input_min - sign * xfuzz)
  2420. code |= LEFT;
  2421. if (y > multigrapher->y_trans.input_max + sign * yfuzz)
  2422. code |= TOP;
  2423. else if (y < multigrapher->y_trans.input_min - sign * yfuzz)
  2424. code |= BOTTOM;
  2425. return code;
  2426. }
  2427. static void
  2428. transpose_portmanteau (int *val)
  2429. {
  2430. bool xtrue, ytrue;
  2431. int newval;
  2432. xtrue = ((*val & X_AXIS) ? true : false);
  2433. ytrue = ((*val & Y_AXIS) ? true : false);
  2434. newval = (xtrue ? Y_AXIS : 0) | (ytrue ? X_AXIS : 0);
  2435. *val = newval;
  2436. }
  2437. static void
  2438. plot_errorbar (Multigrapher *multigrapher, const Point *p)
  2439. {
  2440. if (p->have_x_errorbar || p->have_y_errorbar)
  2441. /* save & restore state, since we invoke pl_linemod_r() */
  2442. {
  2443. pl_savestate_r (multigrapher->plotter);
  2444. pl_linemod_r (multigrapher->plotter, "solid");
  2445. if (p->have_x_errorbar)
  2446. {
  2447. pl_fline_r (multigrapher->plotter,
  2448. XV(p->xmin), YV(p->y) - 0.5 * SS(p->symbol_size),
  2449. XV(p->xmin), YV(p->y) + 0.5 * SS(p->symbol_size));
  2450. pl_fline_r (multigrapher->plotter,
  2451. XV(p->xmin), YV(p->y), XV(p->xmax), YV(p->y));
  2452. pl_fline_r (multigrapher->plotter,
  2453. XV(p->xmax), YV(p->y) - 0.5 * SS(p->symbol_size),
  2454. XV(p->xmax), YV(p->y) + 0.5 * SS(p->symbol_size));
  2455. }
  2456. if (p->have_y_errorbar)
  2457. {
  2458. pl_fline_r (multigrapher->plotter,
  2459. XV(p->x) - 0.5 * SS(p->symbol_size), YV(p->ymin),
  2460. XV(p->x) + 0.5 * SS(p->symbol_size), YV(p->ymin));
  2461. pl_fline_r (multigrapher->plotter,
  2462. XV(p->x), YV(p->ymin), XV(p->x), YV(p->ymax));
  2463. pl_fline_r (multigrapher->plotter,
  2464. XV(p->x) - 0.5 * SS(p->symbol_size), YV(p->ymax),
  2465. XV(p->x) + 0.5 * SS(p->symbol_size), YV(p->ymax));
  2466. }
  2467. pl_restorestate_r (multigrapher->plotter);
  2468. }
  2469. }
  2470. /* An alternative means of ending a polyline in progress. Rather than
  2471. ending it by passing plot_point() a point with the `pendown' flag not
  2472. set, one may call this function. This yields faster response in
  2473. real-time work; e.g. in reader.c it is called by read_and_plot_file()
  2474. after all dataset(s) have been read from the file and plotted. */
  2475. void
  2476. end_polyline_and_flush (Multigrapher *multigrapher)
  2477. {
  2478. pl_endpath_r (multigrapher->plotter);
  2479. pl_flushpl_r (multigrapher->plotter);
  2480. multigrapher->first_point_of_polyline = true;
  2481. }
  2482. /*
  2483. Local Variables:
  2484. c-file-style: "gnu"
  2485. tab-width: 8
  2486. End:
  2487. */