Graphics_linesAndAreas.cpp 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. /* Graphics_linesAndAreas.cpp
  2. *
  3. * Copyright (C) 1992-2011,2012,2013,2014,2015,2017 Paul Boersma, 2013 Tom Naughton
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "GraphicsP.h"
  19. /* Normally on, because e.g. the intensity contour in the Sound window should not run through the play buttons: */
  20. #define FUNCTIONS_ARE_CLIPPED 1
  21. #define LINE_WIDTH_IN_PIXELS(me) ( my resolution > 192 ? my lineWidth * (my resolution / 192.0) : my lineWidth )
  22. #define ORDER_DC { double temp; if (x1DC > x2DC) temp = x1DC, x1DC = x2DC, x2DC = temp; \
  23. if (yIsZeroAtTheTop == (y2DC > y1DC)) temp = y1DC, y1DC = y2DC, y2DC = temp; }
  24. static void psPrepareLine (GraphicsPostscript me) {
  25. double lineWidth_pixels = LINE_WIDTH_IN_PIXELS (me);
  26. if (my lineType == Graphics_DOTTED)
  27. my d_printf (my d_file, "[%ld %ld] 0 setdash\n", (long_not_integer) (my resolution / 100), (long_not_integer) (my resolution / 75 + lineWidth_pixels));
  28. else if (my lineType == Graphics_DASHED)
  29. my d_printf (my d_file, "[%ld %ld] 0 setdash\n", (long_not_integer) (my resolution / 25), (long_not_integer) (my resolution / 50 + lineWidth_pixels));
  30. else if (my lineType == Graphics_DASHED_DOTTED)
  31. my d_printf (my d_file, "[%ld %ld %ld %ld] 0 setdash\n",
  32. (long_not_integer) (my resolution / 100), (long_not_integer) (my resolution / 60 + lineWidth_pixels),
  33. (long_not_integer) (my resolution / 25), (long_not_integer) (my resolution / 60 + lineWidth_pixels));
  34. if (my lineWidth != 1.0)
  35. my d_printf (my d_file, "%g setlinewidth\n", lineWidth_pixels);
  36. }
  37. static void psRevertLine (GraphicsPostscript me) {
  38. if (my lineType != Graphics_DRAWN)
  39. my d_printf (my d_file, "[] 0 setdash\n");
  40. if (my lineWidth != 1.0)
  41. my d_printf (my d_file, "%g setlinewidth\n", my resolution > 192 ? my resolution / 192.0 : 1.0); // 0.375 point
  42. }
  43. #if cairo
  44. #if ALLOW_GDK_DRAWING
  45. static void gdkPrepareLine (GraphicsScreen me) {
  46. gdk_gc_set_line_attributes (my d_gdkGraphicsContext, my lineWidth,
  47. my lineType >= Graphics_DOTTED ? GDK_LINE_ON_OFF_DASH : GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
  48. }
  49. static void gdkRevertLine (GraphicsScreen me) {
  50. if (my lineType >= Graphics_DOTTED) {
  51. gdk_gc_set_line_attributes (my d_gdkGraphicsContext, my lineWidth, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
  52. }
  53. }
  54. #endif
  55. static void cairoPrepareLine (GraphicsScreen me) {
  56. if (! my d_cairoGraphicsContext) return;
  57. double dotted_line [] { 2.0, 2.0 };
  58. double dashed_line [] { 6.0, 2.0 };
  59. double dashed_dotted_line [] { 6.0, 2.0, 2.0, 2.0 };
  60. cairo_save (my d_cairoGraphicsContext);
  61. switch (my lineType) {
  62. case Graphics_DOTTED:
  63. cairo_set_dash (my d_cairoGraphicsContext, dotted_line, 2, 1.0);
  64. break;
  65. case Graphics_DASHED:
  66. cairo_set_dash (my d_cairoGraphicsContext, dashed_line, 2, 1.0);
  67. break;
  68. case Graphics_DASHED_DOTTED:
  69. cairo_set_dash (my d_cairoGraphicsContext, dashed_dotted_line, 4, 1.0);
  70. break;
  71. }
  72. cairo_set_line_width (my d_cairoGraphicsContext, my lineWidth);
  73. }
  74. static void cairoRevertLine (GraphicsScreen me) {
  75. if (! my d_cairoGraphicsContext) return;
  76. if (my lineType >= Graphics_DOTTED) {
  77. cairo_set_dash (my d_cairoGraphicsContext, nullptr, 0, 0);
  78. }
  79. cairo_restore (my d_cairoGraphicsContext);
  80. }
  81. #elif gdi
  82. #define MY_BRUSH SelectPen (d_gdiGraphicsContext, GetStockPen (NULL_PEN)), SelectBrush (d_gdiGraphicsContext, d_winBrush);
  83. #define DEFAULT SelectPen (d_gdiGraphicsContext, GetStockPen (BLACK_PEN)), SelectBrush (d_gdiGraphicsContext, GetStockBrush (NULL_BRUSH));
  84. static void winPrepareLine (GraphicsScreen me) {
  85. HPEN newPen;
  86. int lineWidth_pixels = LINE_WIDTH_IN_PIXELS (me) + 0.5;
  87. if (! lineWidth_pixels) lineWidth_pixels = 1;
  88. my d_fatNonSolid = my lineType != Graphics_DRAWN && lineWidth_pixels > 1;
  89. if (Melder_debug == 10) {
  90. LOGBRUSH brush;
  91. brush. lbStyle = BS_SOLID;
  92. brush. lbColor = my d_winForegroundColour;
  93. brush. lbHatch = my lineType == Graphics_DRAWN ? 0 : my lineType == Graphics_DOTTED ? PS_DOT : my lineType == Graphics_DASHED ? PS_DASH : PS_DASHDOT;
  94. if (my lineType == Graphics_DRAWN) {
  95. newPen = ExtCreatePen (PS_GEOMETRIC, lineWidth_pixels, & brush, 0, nullptr);
  96. } else {
  97. DWORD style [] = { 36, 33 };
  98. newPen = ExtCreatePen (PS_GEOMETRIC | PS_USERSTYLE, lineWidth_pixels, & brush, 2, style);
  99. }
  100. } else {
  101. /*newPen = CreatePen (my lineType == Graphics_DRAWN ? PS_SOLID :
  102. my lineType == Graphics_DOTTED ? PS_DOT : my lineType == Graphics_DASHED ? PS_DASH : PS_DASHDOT,
  103. my fatNonSolid ? 1 : lineWidth_pixels, my foregroundColour);*/
  104. LOGPEN pen;
  105. pen. lopnStyle = my lineType == Graphics_DRAWN ? PS_SOLID : my lineType == Graphics_DOTTED ? PS_DOT : my lineType == Graphics_DASHED ? PS_DASH : PS_DASHDOT;
  106. pen. lopnWidth. x = my d_fatNonSolid ? 1 : lineWidth_pixels;
  107. pen. lopnWidth. y = 0;
  108. pen. lopnColor = my d_winForegroundColour | 0x02000000;
  109. newPen = CreatePenIndirect (& pen);
  110. }
  111. SelectPen (my d_gdiGraphicsContext, newPen);
  112. DeletePen (my d_winPen);
  113. my d_winPen = newPen;
  114. }
  115. #elif quartz
  116. static void quartzPrepareLine (GraphicsScreen me) {
  117. CGContextSetLineJoin (my d_macGraphicsContext, kCGLineJoinBevel); // much faster than kCGLineJoinRound
  118. if (my duringXor) {
  119. CGContextSetBlendMode (my d_macGraphicsContext, kCGBlendModeDifference);
  120. CGContextSetAllowsAntialiasing (my d_macGraphicsContext, false);
  121. CGContextSetRGBStrokeColor (my d_macGraphicsContext, 1.0, 1.0, 1.0, 1.0);
  122. } else {
  123. CGContextSetRGBStrokeColor (my d_macGraphicsContext, my d_macColour.red / 65536.0, my d_macColour.green / 65536.0, my d_macColour.blue / 65536.0, 1.0);
  124. }
  125. double lineWidth_pixels = LINE_WIDTH_IN_PIXELS (me);
  126. CGContextSetLineWidth (my d_macGraphicsContext, lineWidth_pixels);
  127. CGFloat lengths [4];
  128. if (my lineType == Graphics_DOTTED)
  129. lengths [0] = my resolution > 192 ? my resolution / 100.0 : 2,
  130. lengths [1] = my resolution > 192 ? my resolution / 75.0 + lineWidth_pixels : 2;
  131. if (my lineType == Graphics_DASHED)
  132. lengths [0] = my resolution > 192 ? my resolution / 25 : 6,
  133. lengths [1] = my resolution > 192 ? my resolution / 50.0 + lineWidth_pixels : 2;
  134. if (my lineType == Graphics_DASHED_DOTTED)
  135. lengths [0] = my resolution > 192 ? my resolution / 25 : 6,
  136. lengths [1] = my resolution > 192 ? my resolution / 50.0 + lineWidth_pixels : 2;
  137. lengths [2] = my resolution > 192 ? my resolution / 100.0 : 2;
  138. lengths [3] = my resolution > 192 ? my resolution / 50.0 + lineWidth_pixels : 2;
  139. CGContextSetLineDash (my d_macGraphicsContext, 0.0, my lineType == Graphics_DRAWN ? nullptr : lengths, my lineType == 0 ? 0 : my lineType == Graphics_DASHED_DOTTED ? 4 : 2);
  140. }
  141. static void quartzRevertLine (GraphicsScreen me) {
  142. if (my duringXor) {
  143. CGContextSetBlendMode (my d_macGraphicsContext, kCGBlendModeNormal);
  144. CGContextSetAllowsAntialiasing (my d_macGraphicsContext, true);
  145. }
  146. }
  147. static void quartzPrepareFill (GraphicsScreen me) {
  148. CGContextSetAlpha (my d_macGraphicsContext, 1.0);
  149. CGContextSetBlendMode (my d_macGraphicsContext, kCGBlendModeNormal);
  150. CGContextSetRGBFillColor (my d_macGraphicsContext, my d_macColour.red / 65536.0, my d_macColour.green / 65536.0, my d_macColour.blue / 65536.0, 1.0);
  151. }
  152. #endif
  153. /* First level. */
  154. void structGraphicsScreen :: v_polyline (integer numberOfPoints, double *xyDC, bool close) {
  155. #if cairo
  156. if (duringXor) {
  157. #if ALLOW_GDK_DRAWING
  158. gdkPrepareLine (this);
  159. for (integer i = 0; i < numberOfPoints - 1; i ++) {
  160. gdk_draw_line (our d_window, our d_gdkGraphicsContext,
  161. xyDC [i + i], xyDC [i + i + 1], xyDC [i + i + 2], xyDC [i + i + 3]);
  162. }
  163. gdkRevertLine (this);
  164. gdk_flush ();
  165. #endif
  166. } else {
  167. if (our d_cairoGraphicsContext == nullptr) return;
  168. cairoPrepareLine (this);
  169. // cairo_new_path (d_cairoGraphicsContext); // move_to() automatically creates a new path
  170. cairo_move_to (our d_cairoGraphicsContext, xyDC [0], xyDC [1]);
  171. for (integer i = 1; i < numberOfPoints; i ++) {
  172. cairo_line_to (our d_cairoGraphicsContext, xyDC [i + i], xyDC [i + i + 1]);
  173. }
  174. if (close) cairo_close_path (our d_cairoGraphicsContext);
  175. cairo_stroke (our d_cairoGraphicsContext);
  176. cairoRevertLine (this);
  177. }
  178. #elif gdi
  179. if (our d_useGdiplus && 0) {
  180. Gdiplus::Graphics dcplus (our d_gdiGraphicsContext);
  181. Gdiplus::Point *points = Melder_malloc (Gdiplus::Point, numberOfPoints + close);
  182. if (points) {
  183. for (integer i = 0; i < numberOfPoints; i ++) {
  184. points [i]. X = *xyDC, xyDC ++;
  185. points [i]. Y = *xyDC, xyDC ++;
  186. }
  187. if (close)
  188. points [numberOfPoints] = points [0];
  189. #define LINE_WIDTH_IN_PIXELS2(hjhkj) ( our resolution > 192 ? our lineWidth * (our resolution / 192.0) : our lineWidth )
  190. Gdiplus::Pen pen (Gdiplus::Color (255,0,0,0), LINE_WIDTH_IN_PIXELS2 (this) + 0.5);
  191. float dotted_line [] = { 2, 2 };
  192. float dashed_line [] = { 6, 2 };
  193. float dashed_dotted_line [] = { 6, 2, 2, 2 };
  194. switch (our lineType) {
  195. case Graphics_DOTTED:
  196. pen. SetDashPattern (dotted_line, 2);
  197. break;
  198. case Graphics_DASHED:
  199. pen. SetDashPattern (dashed_line, 2);
  200. break;
  201. case Graphics_DASHED_DOTTED:
  202. pen. SetDashPattern (dashed_dotted_line, 4);
  203. break;
  204. }
  205. dcplus. DrawLines (& pen, points, numberOfPoints + close);
  206. Melder_free (points);
  207. }
  208. } else {
  209. winPrepareLine (this);
  210. POINT *points = Melder_malloc (POINT, numberOfPoints + close);
  211. if (points) {
  212. for (integer i = 0; i < numberOfPoints; i ++) {
  213. points [i]. x = *xyDC, xyDC ++;
  214. points [i]. y = *xyDC, xyDC ++;
  215. }
  216. if (close)
  217. points [numberOfPoints] = points [0];
  218. Polyline (our d_gdiGraphicsContext, points, numberOfPoints + close);
  219. if (our d_fatNonSolid) {
  220. for (integer i = 0; i < numberOfPoints; i ++)
  221. points [i]. x -= 1;
  222. if (close)
  223. points [numberOfPoints] = points [0];
  224. Polyline (our d_gdiGraphicsContext, points, numberOfPoints + close);
  225. for (integer i = 0; i < numberOfPoints; i ++) {
  226. points [i]. x += 1;
  227. points [i]. y -= 1;
  228. }
  229. if (close)
  230. points [numberOfPoints] = points [0];
  231. Polyline (our d_gdiGraphicsContext, points, numberOfPoints + close);
  232. }
  233. Melder_free (points);
  234. }
  235. DEFAULT
  236. }
  237. #elif quartz
  238. GraphicsQuartz_initDraw (this);
  239. quartzPrepareLine (this);
  240. CGContextBeginPath (our d_macGraphicsContext);
  241. trace (U"starting point ", xyDC [0], U" ", xyDC [1]);
  242. CGContextMoveToPoint (our d_macGraphicsContext, xyDC [0], xyDC [1]); // starts a new subpath
  243. for (integer i = 1; i < numberOfPoints; i ++) {
  244. CGContextAddLineToPoint (our d_macGraphicsContext, xyDC [i + i], xyDC [i + i + 1]);
  245. }
  246. if (close)
  247. CGContextClosePath (our d_macGraphicsContext); // closes only the subpath
  248. CGContextStrokePath (our d_macGraphicsContext);
  249. quartzRevertLine (this);
  250. GraphicsQuartz_exitDraw (this);
  251. #endif
  252. }
  253. void structGraphicsPostscript :: v_polyline (integer numberOfPoints, double *xyDC, bool close) {
  254. integer nn = 2 * numberOfPoints;
  255. psPrepareLine (this);
  256. our d_printf (our d_file, "N %.7g %.7g moveto\n", xyDC [0], xyDC [1]);
  257. for (integer i = 2; i < nn; i += 2) {
  258. double dx = xyDC [i] - xyDC [i - 2], dy = xyDC [i + 1] - xyDC [i - 1];
  259. our d_printf (our d_file, "%.7g %.7g L\n", dx, dy);
  260. }
  261. if (close)
  262. our d_printf (our d_file, "closepath ");
  263. our d_printf (our d_file, "stroke\n");
  264. psRevertLine (this);
  265. }
  266. void structGraphicsScreen :: v_fillArea (integer numberOfPoints, double *xyDC) {
  267. #if cairo
  268. if (our d_cairoGraphicsContext == nullptr) return;
  269. // cairo_new_path (our d_cairoGraphicsContext); // move_to() automatically creates a new path
  270. cairo_move_to (our d_cairoGraphicsContext, xyDC [0], xyDC [1]);
  271. for (integer i = 1; i < numberOfPoints; i ++)
  272. cairo_line_to (our d_cairoGraphicsContext, xyDC [i + i], xyDC [i + i + 1]);
  273. cairo_close_path (our d_cairoGraphicsContext);
  274. cairo_fill (our d_cairoGraphicsContext);
  275. #elif gdi
  276. MY_BRUSH
  277. BeginPath (our d_gdiGraphicsContext);
  278. MoveToEx (our d_gdiGraphicsContext, xyDC [0], xyDC [1], nullptr);
  279. for (integer i = 1; i < numberOfPoints; i ++)
  280. LineTo (our d_gdiGraphicsContext, xyDC [i + i], xyDC [i + i + 1]);
  281. EndPath (our d_gdiGraphicsContext);
  282. FillPath (our d_gdiGraphicsContext);
  283. DEFAULT
  284. #elif quartz
  285. GraphicsQuartz_initDraw (this);
  286. quartzPrepareFill (this);
  287. CGContextBeginPath (our d_macGraphicsContext);
  288. CGContextMoveToPoint (our d_macGraphicsContext, xyDC [0], xyDC [1]);
  289. for (integer i = 1; i < numberOfPoints; i ++) {
  290. CGContextAddLineToPoint (our d_macGraphicsContext, xyDC [i + i], xyDC [i + i + 1]);
  291. }
  292. CGContextFillPath (our d_macGraphicsContext);
  293. GraphicsQuartz_exitDraw (this);
  294. #endif
  295. }
  296. void structGraphicsPostscript :: v_fillArea (integer numberOfPoints, double *xyDC) {
  297. integer nn = numberOfPoints + numberOfPoints;
  298. d_printf (d_file, "N %.7g %.7g M\n", xyDC [0], xyDC [1]);
  299. for (integer i = 2; i < nn; i += 2) {
  300. d_printf (d_file, "%.7g %.7g L\n", xyDC [i] - xyDC [i - 2], xyDC [i + 1] - xyDC [i - 1]);
  301. }
  302. d_printf (d_file, "closepath fill\n");
  303. }
  304. /* Second level. */
  305. void structGraphicsScreen :: v_rectangle (double x1DC, double x2DC, double y1DC, double y2DC) {
  306. ORDER_DC
  307. #if cairo
  308. if (! d_cairoGraphicsContext) return;
  309. double width = x2DC - x1DC, height = y1DC - y2DC;
  310. if (width <= 0.0 || height <= 0.0) return;
  311. cairoPrepareLine (this);
  312. cairo_rectangle (d_cairoGraphicsContext, x1DC, y2DC, width, height);
  313. cairo_stroke (d_cairoGraphicsContext);
  314. cairoRevertLine (this);
  315. #elif gdi
  316. winPrepareLine (this);
  317. Rectangle (our d_gdiGraphicsContext, x1DC, y2DC, x2DC + 1.0, y1DC + 1.0);
  318. DEFAULT
  319. #elif quartz
  320. GraphicsQuartz_initDraw (this);
  321. quartzPrepareLine (this);
  322. CGContextStrokeRect (d_macGraphicsContext, CGRectMake (x1DC, y2DC, x2DC - x1DC, y1DC - y2DC));
  323. quartzRevertLine (this);
  324. GraphicsQuartz_exitDraw (this);
  325. #else
  326. double xyDC [8];
  327. xyDC [0] = x1DC; xyDC [1] = y1DC;
  328. xyDC [2] = x2DC; xyDC [3] = y1DC;
  329. xyDC [4] = x2DC; xyDC [5] = y2DC;
  330. xyDC [6] = x1DC; xyDC [7] = y2DC;
  331. v_polyline (5, & xyDC [0], true);
  332. #endif
  333. }
  334. void structGraphicsPostscript :: v_rectangle (double x1DC, double x2DC, double y1DC, double y2DC) {
  335. psPrepareLine (this);
  336. d_printf (d_file, "N %.7g %.7g M %.7g %.7g lineto %.7g %.7g lineto %.7g %.7g lineto closepath stroke\n",
  337. x1DC, y1DC, x2DC, y1DC, x2DC, y2DC, x1DC, y2DC);
  338. psRevertLine (this);
  339. }
  340. void structGraphicsScreen :: v_fillRectangle (double x1DC, double x2DC, double y1DC, double y2DC) {
  341. ORDER_DC
  342. #if cairo
  343. if (! d_cairoGraphicsContext) return;
  344. double width = x2DC - x1DC + 1.0, height = y1DC - y2DC + 1.0;
  345. if (width <= 0.0 || height <= 0.0) return;
  346. trace (U"x1DC ", x1DC, U", x2DC ", x2DC, U", y1DC ", y1DC, U", y2DC ", y2DC);
  347. cairo_rectangle (d_cairoGraphicsContext, round (x1DC), round (y2DC), round (width), round (height));
  348. cairo_fill (d_cairoGraphicsContext);
  349. #elif gdi
  350. RECT rect;
  351. rect. left = x1DC, rect. right = x2DC, rect. top = y2DC, rect. bottom = y1DC; /* Superfluous? */
  352. MY_BRUSH
  353. Rectangle (d_gdiGraphicsContext, x1DC, y2DC, x2DC + 1.0, y1DC + 1.0);
  354. DEFAULT
  355. #elif quartz
  356. GraphicsQuartz_initDraw (this);
  357. quartzPrepareFill (this);
  358. CGContextFillRect (d_macGraphicsContext, CGRectMake (x1DC, y2DC, x2DC - x1DC, y1DC - y2DC));
  359. GraphicsQuartz_exitDraw (this);
  360. #else
  361. double xyDC [10];
  362. xyDC [0] = x1DC; xyDC [1] = y1DC;
  363. xyDC [2] = x2DC; xyDC [3] = y1DC;
  364. xyDC [4] = x2DC; xyDC [5] = y2DC;
  365. xyDC [6] = x1DC; xyDC [7] = y2DC;
  366. xyDC [8] = x1DC; xyDC [9] = y1DC;
  367. v_fillArea (5, & xyDC [0]);
  368. #endif
  369. }
  370. void structGraphicsPostscript :: v_fillRectangle (double x1DC, double x2DC, double y1DC, double y2DC) {
  371. d_printf (d_file,
  372. "N %.7g %.7g M %.7g %.7g lineto %.7g %.7g lineto %.7g %.7g lineto closepath fill\n",
  373. x1DC, y1DC, x2DC, y1DC, x2DC, y2DC, x1DC, y2DC);
  374. }
  375. void structGraphicsScreen :: v_circle (double xDC, double yDC, double rDC) {
  376. #if cairo
  377. if (duringXor) {
  378. #if ALLOW_GDK_DRAWING
  379. gdkPrepareLine (this);
  380. gdk_draw_arc (d_window, d_gdkGraphicsContext, false, xDC - rDC, yDC - rDC, rDC + rDC, rDC + rDC, 0, 360 * 64);
  381. gdkRevertLine (this);
  382. gdk_flush ();
  383. #endif
  384. } else {
  385. if (! d_cairoGraphicsContext) return;
  386. cairoPrepareLine (this);
  387. cairo_new_path (d_cairoGraphicsContext);
  388. cairo_arc (d_cairoGraphicsContext, xDC, yDC, rDC, 0.0, 2.0 * M_PI);
  389. cairo_stroke (d_cairoGraphicsContext);
  390. cairoRevertLine (this);
  391. }
  392. #elif gdi
  393. winPrepareLine (this);
  394. Ellipse (d_gdiGraphicsContext, xDC - rDC, yDC - rDC, xDC + rDC + 1.0, yDC + rDC + 1.0);
  395. DEFAULT
  396. #elif quartz
  397. GraphicsQuartz_initDraw (this);
  398. quartzPrepareLine (this);
  399. CGContextBeginPath (d_macGraphicsContext);
  400. CGContextAddArc (d_macGraphicsContext, xDC, yDC, rDC, 0.0, NUM2pi, 0);
  401. CGContextStrokePath (d_macGraphicsContext);
  402. quartzRevertLine (this);
  403. GraphicsQuartz_exitDraw (this);
  404. #endif
  405. }
  406. void structGraphicsPostscript :: v_circle (double xDC, double yDC, double rDC) {
  407. psPrepareLine (this);
  408. d_printf (d_file, "N %ld %ld %ld C\n", (long_not_integer) xDC, (long_not_integer) yDC, (long_not_integer) rDC);
  409. psRevertLine (this);
  410. }
  411. void structGraphicsScreen :: v_ellipse (double x1DC, double x2DC, double y1DC, double y2DC) {
  412. ORDER_DC
  413. #if cairo
  414. if (! d_cairoGraphicsContext) return;
  415. cairoPrepareLine (this);
  416. cairo_new_path (d_cairoGraphicsContext);
  417. cairo_save (d_cairoGraphicsContext);
  418. cairo_translate (d_cairoGraphicsContext, 0.5 * (x2DC + x1DC), 0.5 * (y2DC + y1DC));
  419. cairo_scale (d_cairoGraphicsContext, 0.5 * (x2DC - x1DC), 0.5 * (y2DC - y1DC));
  420. cairo_arc (d_cairoGraphicsContext, 0.0, 0.0, 1.0, 0.0, 2 * M_PI);
  421. cairo_restore (d_cairoGraphicsContext);
  422. cairo_stroke (d_cairoGraphicsContext);
  423. cairoRevertLine (this);
  424. #elif gdi
  425. winPrepareLine (this);
  426. Ellipse (d_gdiGraphicsContext, x1DC, y2DC, x2DC + 1, y1DC + 1);
  427. DEFAULT
  428. #elif quartz
  429. GraphicsQuartz_initDraw (this);
  430. quartzPrepareLine (this);
  431. NSCAssert (d_macGraphicsContext, @"nil context");
  432. CGContextBeginPath (d_macGraphicsContext);
  433. CGContextSaveGState (d_macGraphicsContext);
  434. CGContextTranslateCTM (d_macGraphicsContext, 0.5 * (x2DC + x1DC), 0.5 * (y2DC + y1DC));
  435. CGContextScaleCTM (d_macGraphicsContext, 0.5 * (x2DC - x1DC), 0.5 * (y2DC - y1DC));
  436. CGContextAddArc (d_macGraphicsContext, 0.0, 0.0, 1.0, 0.0, NUM2pi, 0);
  437. CGContextScaleCTM (d_macGraphicsContext, 2.0 / (x2DC - x1DC), 2.0 / (y2DC - y1DC));
  438. CGContextStrokePath (d_macGraphicsContext);
  439. CGContextRestoreGState (d_macGraphicsContext);
  440. quartzRevertLine (this);
  441. GraphicsQuartz_exitDraw (this);
  442. #endif
  443. }
  444. void structGraphicsPostscript :: v_ellipse (double x1DC, double x2DC, double y1DC, double y2DC) {
  445. if (x1DC != x2DC && y1DC != y2DC) { // prevent division by zero
  446. psPrepareLine (this);
  447. /* To draw an ellipse, we will have to 'translate' and 'scale' and draw a circle. */
  448. /* However, we have to scale back before the actual 'stroke', */
  449. /* because we want the normal line thickness; */
  450. /* So we cannot use 'gsave' and 'grestore', which clear the path (Cookbook 3). */
  451. d_printf (d_file, "gsave %.7g %.7g translate %.7g %.7g scale N 0 0 1 0 360 arc\n"
  452. " %.7g %.7g scale stroke grestore\n",
  453. 0.5 * (x2DC + x1DC), 0.5 * (y2DC + y1DC), 0.5 * (x2DC - x1DC), 0.5 * (y2DC - y1DC),
  454. 2.0 / (x2DC - x1DC), 2.0 / (y2DC - y1DC));
  455. psRevertLine (this);
  456. }
  457. }
  458. void structGraphicsScreen :: v_arc (double xDC, double yDC, double rDC, double fromAngle, double toAngle) {
  459. #if cairo
  460. if (! d_cairoGraphicsContext) return;
  461. cairoPrepareLine (this);
  462. cairo_new_path (d_cairoGraphicsContext);
  463. cairo_arc (d_cairoGraphicsContext, xDC, yDC, rDC, -toAngle * (M_PI / 180.0), -fromAngle * (M_PI / 180.0));
  464. cairo_stroke (d_cairoGraphicsContext);
  465. cairoRevertLine (this);
  466. #elif gdi
  467. int arcAngle = (int) toAngle - (int) fromAngle;
  468. POINT pt;
  469. if (arcAngle < 0.0) arcAngle += 360;
  470. winPrepareLine (this);
  471. MoveToEx (d_gdiGraphicsContext, xDC + rDC * cos (NUMpi / 180 * fromAngle), yDC - rDC * sin (NUMpi / 180 * fromAngle), & pt);
  472. AngleArc (d_gdiGraphicsContext, xDC, yDC, rDC, fromAngle, arcAngle);
  473. DEFAULT
  474. #elif quartz
  475. GraphicsQuartz_initDraw (this);
  476. quartzPrepareLine (this);
  477. CGContextBeginPath (d_macGraphicsContext);
  478. CGContextAddArc (d_macGraphicsContext, xDC, yDC, rDC, NUM2pi - NUMpi / 180 * toAngle, NUM2pi - NUMpi / 180 * fromAngle, 0);
  479. CGContextStrokePath (d_macGraphicsContext);
  480. quartzRevertLine (this);
  481. GraphicsQuartz_exitDraw (this);
  482. #endif
  483. }
  484. void structGraphicsPostscript :: v_arc (double xDC, double yDC, double rDC, double fromAngle, double toAngle) {
  485. psPrepareLine (this);
  486. d_printf (d_file, "N %.7g %.7g %.7g %.7g %.7g arc stroke\n", xDC, yDC, rDC, fromAngle, toAngle);
  487. psRevertLine (this);
  488. }
  489. /* Third level. */
  490. void structGraphicsScreen :: v_fillCircle (double xDC, double yDC, double rDC) {
  491. #if cairo
  492. if (! d_cairoGraphicsContext) return;
  493. cairo_new_path (d_cairoGraphicsContext);
  494. cairo_arc (d_cairoGraphicsContext, xDC, yDC, rDC, 0, 2 * M_PI);
  495. cairo_fill (d_cairoGraphicsContext);
  496. #elif gdi
  497. MY_BRUSH
  498. /*
  499. * NT cannot fill circles that span less than five pixels...
  500. */
  501. Ellipse (d_gdiGraphicsContext, xDC - rDC - 1.0, yDC - rDC - 1.0, xDC + rDC + 1.0, yDC + rDC + 1.0);
  502. DEFAULT
  503. #elif quartz
  504. GraphicsQuartz_initDraw (this);
  505. quartzPrepareFill (this);
  506. CGContextBeginPath (d_macGraphicsContext);
  507. CGContextAddArc (d_macGraphicsContext, xDC, yDC, rDC, 0.0, NUM2pi, 0);
  508. CGContextFillPath (d_macGraphicsContext);
  509. GraphicsQuartz_exitDraw (this);
  510. #else
  511. v_circle (xDC, yDC, rDC);
  512. #endif
  513. }
  514. void structGraphicsPostscript :: v_fillCircle (double xDC, double yDC, double rDC) {
  515. d_printf (d_file, "N %.7g %.7g %.7g FC\n", xDC, yDC, rDC);
  516. }
  517. void structGraphicsScreen :: v_fillEllipse (double x1DC, double x2DC, double y1DC, double y2DC) {
  518. ORDER_DC
  519. #if cairo
  520. if (! d_cairoGraphicsContext) return;
  521. cairo_new_path (d_cairoGraphicsContext);
  522. cairo_save (d_cairoGraphicsContext);
  523. cairo_translate (d_cairoGraphicsContext, 0.5 * (x2DC + x1DC), 0.5 * (y2DC + y1DC));
  524. cairo_scale (d_cairoGraphicsContext, 0.5 * (x2DC - x1DC), 0.5 * (y2DC - y1DC));
  525. cairo_arc (d_cairoGraphicsContext, 0.0, 0.0, 1.0, 0.0, 2.0 * M_PI);
  526. cairo_restore (d_cairoGraphicsContext);
  527. cairo_fill (d_cairoGraphicsContext);
  528. #elif gdi
  529. MY_BRUSH
  530. Ellipse (d_gdiGraphicsContext, x1DC, y2DC, x2DC + 1.0, y1DC + 1.0);
  531. DEFAULT
  532. #elif quartz
  533. GraphicsQuartz_initDraw (this);
  534. quartzPrepareFill (this);
  535. NSCAssert (d_macGraphicsContext, @"nil context");
  536. CGContextBeginPath (d_macGraphicsContext);
  537. CGContextSaveGState (d_macGraphicsContext);
  538. CGContextTranslateCTM (d_macGraphicsContext, 0.5 * (x2DC + x1DC), 0.5 * (y2DC + y1DC));
  539. CGContextScaleCTM (d_macGraphicsContext, 0.5 * (x2DC - x1DC), 0.5 * (y2DC - y1DC));
  540. CGContextAddArc (d_macGraphicsContext, 0.0, 0.0, 1.0, 0.0, NUM2pi, 0);
  541. CGContextScaleCTM (d_macGraphicsContext, 2.0 / (x2DC - x1DC), 2.0 / (y2DC - y1DC));
  542. CGContextFillPath (d_macGraphicsContext);
  543. CGContextRestoreGState (d_macGraphicsContext);
  544. GraphicsQuartz_exitDraw (this);
  545. #else
  546. v_ellipse (x1DC, x2DC, y1DC, y2DC);
  547. #endif
  548. }
  549. void structGraphicsPostscript :: v_fillEllipse (double x1DC, double x2DC, double y1DC, double y2DC) {
  550. d_printf (d_file, "gsave %.7g %.7g translate %.7g %.7g scale N 0 0 1 FC grestore\n",
  551. (x2DC + x1DC) / 2.0, (y2DC + y1DC) / 2.0, (x2DC - x1DC) / 2.0, (y2DC - y1DC) / 2.0);
  552. }
  553. void structGraphicsScreen :: v_button (double x1DC, double x2DC, double y1DC, double y2DC) {
  554. ORDER_DC
  555. #if cairo
  556. if (x2DC <= x1DC || y1DC <= y2DC) return;
  557. cairo_save (d_cairoGraphicsContext);
  558. #if 0
  559. if (d_drawingArea) {
  560. // clip to drawing area
  561. int w, h;
  562. #if ALLOW_GDK_DRAWING
  563. gdk_drawable_get_size (d_window, & w, & h);
  564. #else
  565. w = gdk_window_get_width (d_window);
  566. h = gdk_window_get_height (d_window);
  567. #endif
  568. cairo_rectangle (d_cairoGraphicsContext, 0, 0, w, h);
  569. cairo_clip (d_cairoGraphicsContext);
  570. }
  571. #endif
  572. cairo_set_line_width (d_cairoGraphicsContext, 1.0);
  573. double left = x1DC - 0.5, right = x2DC - 0.5, top = y2DC + 0.5, bottom = y1DC + 0.5;
  574. double width = right - left, height = bottom - top;
  575. cairo_set_source_rgb (d_cairoGraphicsContext, 0.1, 0.1, 0.1); // dark grey
  576. cairo_rectangle (d_cairoGraphicsContext, left, top, width, height);
  577. cairo_stroke (d_cairoGraphicsContext);
  578. left ++, right --, top ++, bottom --, width -= 2, height -= 2;
  579. if (width > 0 && height > 0) {
  580. cairo_set_source_rgb (d_cairoGraphicsContext, 0.3, 0.3, 0.3);
  581. cairo_move_to (d_cairoGraphicsContext, left + 1, bottom);
  582. cairo_line_to (d_cairoGraphicsContext, right, bottom);
  583. cairo_line_to (d_cairoGraphicsContext, right, top + 1);
  584. cairo_stroke (d_cairoGraphicsContext);
  585. cairo_set_source_rgb (d_cairoGraphicsContext, 1.0, 1.0, 1.0);
  586. cairo_move_to (d_cairoGraphicsContext, left, bottom);
  587. cairo_line_to (d_cairoGraphicsContext, left, top);
  588. cairo_line_to (d_cairoGraphicsContext, right, top);
  589. cairo_stroke (d_cairoGraphicsContext);
  590. left += 0.5, right -= 0.5, top += 0.5, bottom -= 0.5, width -= 1, height -= 1;
  591. if (width > 0 && height > 0) {
  592. cairo_set_source_rgb (d_cairoGraphicsContext, 0.65, 0.65, 0.65);
  593. cairo_rectangle (d_cairoGraphicsContext, left, top, width, height);
  594. cairo_fill (d_cairoGraphicsContext);
  595. }
  596. }
  597. cairo_restore (d_cairoGraphicsContext);
  598. #elif quartz
  599. double width = x2DC - x1DC, height = y1DC - y2DC;
  600. if (width <= 0 || height <= 0) return;
  601. /*
  602. * This is pixel-precise drawing, and may therefore be different on retina displays than on 100 dpi displays.
  603. */
  604. #if 1
  605. bool isRetinaDisplay = [[d_macView window] backingScaleFactor] == 2.0;
  606. #else
  607. bool isRetinaDisplay = false;
  608. #endif
  609. GraphicsQuartz_initDraw (this);
  610. CGContextSetLineWidth (d_macGraphicsContext, 1.0);
  611. CGContextSetAllowsAntialiasing (d_macGraphicsContext, false); // because we want to draw by pixel
  612. CGFloat red = 0.3, green = 0.3, blue = 0.2;
  613. CGContextSetRGBStrokeColor (d_macGraphicsContext, red, green, blue, 1.0);
  614. if (! isRetinaDisplay)
  615. x1DC --;
  616. x1DC += 0.5, x2DC -= 0.5, y1DC -= 0.5, y2DC += 0.5, width = x2DC - x1DC, height = y1DC - y2DC;
  617. CGRect rect = CGRectMake (x1DC, y2DC, width, height);
  618. CGContextAddRect (d_macGraphicsContext, rect);
  619. CGContextStrokePath (d_macGraphicsContext);
  620. x1DC ++, x2DC --, y1DC --, y2DC ++, width = x2DC - x1DC, height = y1DC - y2DC;
  621. if (width > 0 && height > 0) {
  622. red = 0.5, green = 0.5, blue = 0.4;
  623. CGContextSetRGBStrokeColor (d_macGraphicsContext, red, green, blue, 1.0);
  624. CGContextMoveToPoint (d_macGraphicsContext, x1DC, y1DC);
  625. CGContextAddLineToPoint (d_macGraphicsContext, x2DC, y1DC);
  626. CGContextMoveToPoint (d_macGraphicsContext, x2DC, y1DC);
  627. CGContextAddLineToPoint (d_macGraphicsContext, x2DC, y2DC);
  628. CGContextStrokePath (d_macGraphicsContext);
  629. red = 1.0, green = 1.0, blue = 0.9;
  630. CGContextSetRGBStrokeColor (d_macGraphicsContext, red, green, blue, 1.0);
  631. CGContextMoveToPoint (d_macGraphicsContext, x1DC, y1DC);
  632. CGContextAddLineToPoint (d_macGraphicsContext, x1DC, y2DC);
  633. CGContextMoveToPoint (d_macGraphicsContext, x1DC, y2DC);
  634. CGContextAddLineToPoint (d_macGraphicsContext, x2DC, y2DC);
  635. CGContextStrokePath (d_macGraphicsContext);
  636. if (width > 2 && height > 2) {
  637. if (! isRetinaDisplay) x1DC ++, width = x2DC - x1DC, height = y1DC - y2DC;
  638. red = 0.75, green = 0.75, blue = 0.65;
  639. CGContextSetRGBFillColor (d_macGraphicsContext, red, green, blue, 1.0);
  640. rect = CGRectMake (x1DC, y2DC, width, height);
  641. CGContextFillRect (d_macGraphicsContext, rect);
  642. }
  643. }
  644. CGContextSetAllowsAntialiasing (d_macGraphicsContext, true);
  645. CGContextSetLineDash (d_macGraphicsContext, 0, nullptr, 0);
  646. GraphicsQuartz_exitDraw (this);
  647. #elif gdi
  648. RECT rect;
  649. rect. left = x1DC, rect. right = x2DC, rect. top = y2DC, rect. bottom = y1DC;
  650. DrawEdge (d_gdiGraphicsContext, & rect, EDGE_RAISED, BF_RECT);
  651. SelectPen (d_gdiGraphicsContext, GetStockPen (NULL_PEN));
  652. SelectBrush (d_gdiGraphicsContext, GetStockBrush (LTGRAY_BRUSH));
  653. Rectangle (d_gdiGraphicsContext, x1DC + 1, y2DC + 1, x2DC - 1, y1DC - 1);
  654. SelectPen (d_gdiGraphicsContext, GetStockPen (BLACK_PEN));
  655. SelectBrush (d_gdiGraphicsContext, GetStockBrush (NULL_BRUSH));
  656. #endif
  657. }
  658. void structGraphics :: v_roundedRectangle (double x1DC, double x2DC, double y1DC, double y2DC, double r) {
  659. double dy = yIsZeroAtTheTop ? - r : r;
  660. double xyDC [4];
  661. ORDER_DC
  662. xyDC [0] = x1DC + r;
  663. xyDC [1] = y1DC;
  664. xyDC [2] = x2DC - r;
  665. xyDC [3] = y1DC;
  666. v_polyline (2, xyDC, false);
  667. v_arc (x2DC - r, y1DC + dy, r, -90.0, 0.0);
  668. xyDC [0] = x2DC;
  669. xyDC [1] = y1DC + dy;
  670. xyDC [2] = x2DC;
  671. xyDC [3] = y2DC - dy;
  672. v_polyline (2, xyDC, false);
  673. v_arc (x2DC - r, y2DC - dy, r, 0.0, 90.0);
  674. xyDC [0] = x2DC - r;
  675. xyDC [1] = y2DC;
  676. xyDC [2] = x1DC + r;
  677. xyDC [3] = y2DC;
  678. v_polyline (2, xyDC, false);
  679. v_arc (x1DC + r, y2DC - dy, r, 90.0, 180.0);
  680. xyDC [0] = x1DC;
  681. xyDC [1] = y2DC - dy;
  682. xyDC [2] = x1DC;
  683. xyDC [3] = y1DC + dy;
  684. v_polyline (2, xyDC, false);
  685. v_arc (x1DC + r, y1DC + dy, r, 180.0, 270.0);
  686. }
  687. void structGraphicsScreen :: v_roundedRectangle (double x1DC, double x2DC, double y1DC, double y2DC, double r) {
  688. #if gdi
  689. double dy = yIsZeroAtTheTop ? - r : r, xyDC [4];
  690. ORDER_DC
  691. winPrepareLine (this);
  692. RoundRect (d_gdiGraphicsContext, x1DC, y2DC, x2DC + 1.0, y1DC + 1.0, r + r, r + r);
  693. DEFAULT
  694. return;
  695. #else
  696. GraphicsScreen_Parent :: v_roundedRectangle (x1DC, x2DC, y1DC, y2DC, r);
  697. #endif
  698. }
  699. /* Fourth level. */
  700. void structGraphics :: v_fillRoundedRectangle (double x1DC, double x2DC, double y1DC, double y2DC, double r) {
  701. double dy = yIsZeroAtTheTop ? - r : r;
  702. ORDER_DC
  703. v_fillCircle (x2DC - r, y1DC + dy, r);
  704. v_fillCircle (x2DC - r, y2DC - dy, r);
  705. v_fillCircle (x1DC + r, y2DC - dy, r);
  706. v_fillCircle (x1DC + r, y1DC + dy, r);
  707. v_fillRectangle (x1DC, x2DC, y1DC + dy, y2DC - dy);
  708. v_fillRectangle (x1DC + r, x2DC - r, y1DC, y2DC);
  709. }
  710. /* Fifth level. */
  711. #define wdx(x) ((x) * my scaleX + my deltaX)
  712. #define wdy(y) ((y) * my scaleY + my deltaY)
  713. void Graphics_polyline (Graphics me, integer numberOfPoints, double *xWC, double *yWC) { // base 0
  714. if (numberOfPoints == 0) return;
  715. double *xyDC;
  716. try {
  717. xyDC = Melder_malloc (double, 2 * numberOfPoints);
  718. } catch (MelderError) {
  719. /*
  720. * Out of memory: silently refuse to draw.
  721. */
  722. Melder_clearError ();
  723. return;
  724. }
  725. for (integer i = 0; i < numberOfPoints; i ++) {
  726. xyDC [i + i] = wdx (xWC [i]);
  727. xyDC [i + i + 1] = wdy (yWC [i]);
  728. }
  729. my v_polyline (numberOfPoints, xyDC, false);
  730. Melder_free (xyDC);
  731. if (my recording) {
  732. op (POLYLINE, 1 + 2 * numberOfPoints);
  733. put (numberOfPoints);
  734. mput (numberOfPoints, & xWC [0])
  735. mput (numberOfPoints, & yWC [0])
  736. }
  737. }
  738. void Graphics_polyline_closed (Graphics me, integer numberOfPoints, double *xWC, double *yWC) { // base 0
  739. if (numberOfPoints == 0) return;
  740. double *xyDC;
  741. try {
  742. xyDC = Melder_malloc (double, 2 * numberOfPoints);
  743. } catch (MelderError) {
  744. /*
  745. * Out of memory: silently refuse to draw.
  746. */
  747. Melder_clearError ();
  748. return;
  749. }
  750. for (integer i = 0; i < numberOfPoints; i ++) {
  751. xyDC [i + i] = wdx (xWC [i]);
  752. xyDC [i + i + 1] = wdy (yWC [i]);
  753. }
  754. my v_polyline (numberOfPoints, xyDC, true);
  755. Melder_free (xyDC);
  756. if (my recording) {
  757. op (POLYLINE_CLOSED, 1 + 2 * numberOfPoints);
  758. put (numberOfPoints);
  759. mput (numberOfPoints, & xWC [0])
  760. mput (numberOfPoints, & yWC [0])
  761. }
  762. }
  763. void Graphics_line (Graphics me, double x1WC, double y1WC, double x2WC, double y2WC) {
  764. double xyDC [4];
  765. trace (x1WC, U" ", y1WC, U" ", x2WC, U" ", y2WC);
  766. xyDC [0] = wdx (x1WC);
  767. xyDC [1] = wdy (y1WC);
  768. xyDC [2] = wdx (x2WC);
  769. xyDC [3] = wdy (y2WC);
  770. my v_polyline (2, xyDC, false);
  771. if (my recording) { op (LINE, 4); put (x1WC); put (y1WC); put (x2WC); put (y2WC); }
  772. }
  773. void Graphics_fillArea (Graphics me, integer numberOfPoints, double *xWC, double *yWC) {
  774. double *xyDC;
  775. try {
  776. xyDC = Melder_malloc (double, 2 * numberOfPoints);
  777. } catch (MelderError) {
  778. /*
  779. * Out of memory: silently refuse to draw.
  780. */
  781. Melder_clearError ();
  782. return;
  783. }
  784. for (integer i = 0; i < numberOfPoints; i ++) {
  785. xyDC [i + i] = wdx (xWC [i]);
  786. xyDC [i + i + 1] = wdy (yWC [i]);
  787. }
  788. my v_fillArea (numberOfPoints, xyDC);
  789. Melder_free (xyDC);
  790. if (my recording) {
  791. op (FILL_AREA, 1 + 2 * numberOfPoints);
  792. put (numberOfPoints);
  793. mput (numberOfPoints, & xWC [0])
  794. mput (numberOfPoints, & yWC [0])
  795. }
  796. }
  797. #define MACRO_Graphics_function(TYPE) \
  798. integer x1DC, x2DC; \
  799. integer clipy1 = wdy (my d_y1WC), clipy2 = wdy (my d_y2WC); \
  800. double dx, offsetX, translation, scale; \
  801. integer n = ix2 - ix1 + 1; \
  802. \
  803. if (ix2 <= ix1 || my scaleX == 0.0) return; \
  804. \
  805. dx = (x2WC - x1WC) / (n - 1); \
  806. offsetX = x1WC - ix1 * dx; \
  807. /* xDC = wdx (offsetX + i * dx) */ \
  808. translation = wdx (offsetX); \
  809. scale = dx * my scaleX; \
  810. x1DC = translation + ix1 * scale; \
  811. x2DC = translation + ix2 * scale; \
  812. if (n > (x2DC - x1DC + 1) * 2) { /* Optimize: draw one vertical line for each device x coordinate. */ \
  813. integer numberOfPixels = x2DC - x1DC + 1, k = 0; \
  814. integer numberOfPointsActuallyDrawn = numberOfPixels * 2; \
  815. double *xyDC; \
  816. TYPE lastMini; \
  817. if (numberOfPointsActuallyDrawn < 1) return; \
  818. xyDC = Melder_malloc_f (double, 2 * numberOfPointsActuallyDrawn); \
  819. for (integer i = 0; i < numberOfPixels; i ++) { \
  820. integer j, jmin = ix1 + i / scale, jmax = ix1 + (i + 1) / scale; \
  821. TYPE mini, maxi; \
  822. integer minDC, maxDC; \
  823. if (jmin > ix2) jmin = ix2; \
  824. if (jmax > ix2) jmax = ix2; \
  825. mini = yWC [STAGGER (jmin)], maxi = mini; \
  826. for (j = jmin + 1; j <= jmax; j ++) { /* One point overlap. */ \
  827. TYPE value = yWC [STAGGER (j)]; \
  828. if (value > maxi) maxi = value; \
  829. else if (value < mini) mini = value; \
  830. } \
  831. minDC = wdy (mini); \
  832. maxDC = wdy (maxi); \
  833. if (my yIsZeroAtTheTop) { \
  834. if (minDC > clipy1) minDC = clipy1; \
  835. if (maxDC > clipy1) maxDC = clipy1; \
  836. if (maxDC < clipy2) maxDC = clipy2; \
  837. if (minDC < clipy2) minDC = clipy2; \
  838. } else { \
  839. if (minDC < clipy1) minDC = clipy1; \
  840. if (maxDC < clipy1) maxDC = clipy1; \
  841. if (maxDC > clipy2) maxDC = clipy2; \
  842. if (minDC > clipy2) minDC = clipy2; \
  843. } \
  844. if (i == 0) { \
  845. if (yWC [STAGGER (jmin)] < yWC [STAGGER (jmax)]) { \
  846. xyDC [k ++] = x1DC; \
  847. xyDC [k ++] = minDC; \
  848. xyDC [k ++] = x1DC; \
  849. xyDC [k ++] = maxDC; \
  850. } else { \
  851. xyDC [k ++] = x1DC; \
  852. xyDC [k ++] = maxDC; \
  853. xyDC [k ++] = x1DC; \
  854. xyDC [k ++] = minDC; \
  855. } \
  856. } else if (minDC == xyDC [k - 1]) { \
  857. xyDC [k ++] = x1DC + i; \
  858. xyDC [k ++] = maxDC; \
  859. } else if (maxDC == xyDC [k - 1]) { \
  860. xyDC [k ++] = x1DC + i; \
  861. xyDC [k ++] = minDC; \
  862. } else if (mini > lastMini) { \
  863. xyDC [k ++] = x1DC + i; \
  864. xyDC [k ++] = minDC; \
  865. xyDC [k ++] = x1DC + i; \
  866. xyDC [k ++] = maxDC; \
  867. } else { \
  868. xyDC [k ++] = x1DC + i; \
  869. xyDC [k ++] = maxDC; \
  870. xyDC [k ++] = x1DC + i; \
  871. xyDC [k ++] = minDC; \
  872. } \
  873. lastMini = mini; \
  874. } \
  875. if (k > 1) my v_polyline (k / 2, xyDC, false); \
  876. Melder_free (xyDC); \
  877. } else { /* Normal. */ \
  878. double *xyDC = Melder_malloc_f (double, 2 * n); \
  879. for (integer i = 0; i < n; i ++) { \
  880. integer ix = ix1 + i; \
  881. integer value = wdy (yWC [STAGGER (ix)]); \
  882. xyDC [i + i] = translation + ix * scale; \
  883. if (my yIsZeroAtTheTop) { \
  884. if (FUNCTIONS_ARE_CLIPPED && value > clipy1) value = clipy1; \
  885. if (FUNCTIONS_ARE_CLIPPED && value < clipy2) value = clipy2; \
  886. } else { \
  887. if (FUNCTIONS_ARE_CLIPPED && value < clipy1) value = clipy1; \
  888. if (FUNCTIONS_ARE_CLIPPED && value > clipy2) value = clipy2; \
  889. } \
  890. xyDC [i + i + 1] = value; \
  891. } \
  892. my v_polyline (n, xyDC, false); \
  893. Melder_free (xyDC); \
  894. }
  895. void Graphics_function (Graphics me, double yWC [], integer ix1, integer ix2, double x1WC, double x2WC) {
  896. #define STAGGER(i) (i)
  897. MACRO_Graphics_function (double)
  898. #undef STAGGER
  899. if (my recording) { op (FUNCTION, 3 + n); put (n); put (x1WC); put (x2WC); mput (n, & yWC [ix1]) }
  900. }
  901. void Graphics_function16 (Graphics me, int16 yWC [], int stagger, integer ix1, integer ix2, double x1WC, double x2WC) {
  902. if (stagger == 1) {
  903. #define STAGGER(i) ((i) + (i))
  904. MACRO_Graphics_function (int16)
  905. #undef STAGGER
  906. } else if (stagger > 1) {
  907. #define STAGGER(i) ((stagger + 1) * (i))
  908. MACRO_Graphics_function (int16)
  909. #undef STAGGER
  910. } else {
  911. #define STAGGER(i) (i)
  912. MACRO_Graphics_function (int16)
  913. #undef STAGGER
  914. }
  915. }
  916. void Graphics_rectangle (Graphics me, double x1WC, double x2WC, double y1WC, double y2WC) {
  917. my v_rectangle (wdx (x1WC), wdx (x2WC), wdy (y1WC), wdy (y2WC));
  918. if (my recording) { op (RECTANGLE, 4); put (x1WC); put (x2WC); put (y1WC); put (y2WC); }
  919. }
  920. void Graphics_fillRectangle (Graphics me, double x1WC, double x2WC, double y1WC, double y2WC) {
  921. my v_fillRectangle (wdx (x1WC), wdx (x2WC), wdy (y1WC), wdy (y2WC));
  922. if (my recording) { op (FILL_RECTANGLE, 4); put (x1WC); put (x2WC); put (y1WC); put (y2WC); }
  923. }
  924. void Graphics_roundedRectangle (Graphics me, double x1WC, double x2WC, double y1WC, double y2WC, double r_mm) {
  925. my v_roundedRectangle (wdx (x1WC), wdx (x2WC), wdy (y1WC), wdy (y2WC), Melder_iceiling (r_mm * my resolution / 25.4));
  926. if (my recording) { op (ROUNDED_RECTANGLE, 5); put (x1WC); put (x2WC); put (y1WC); put (y2WC); put (r_mm); }
  927. }
  928. void Graphics_fillRoundedRectangle (Graphics me, double x1WC, double x2WC, double y1WC, double y2WC, double r_mm) {
  929. my v_fillRoundedRectangle (wdx (x1WC), wdx (x2WC), wdy (y1WC), wdy (y2WC), Melder_iceiling (r_mm * my resolution / 25.4));
  930. if (my recording) { op (FILL_ROUNDED_RECTANGLE, 5); put (x1WC); put (x2WC); put (y1WC); put (y2WC); put (r_mm); }
  931. }
  932. void Graphics_button (Graphics me, double x1WC, double x2WC, double y1WC, double y2WC) {
  933. my v_button (wdx (x1WC), wdx (x2WC), wdy (y1WC), wdy (y2WC));
  934. if (my recording) { op (BUTTON, 4); put (x1WC); put (x2WC); put (y1WC); put (y2WC); }
  935. }
  936. void Graphics_innerRectangle (Graphics me, double x1WC, double x2WC, double y1WC, double y2WC) {
  937. int dy = my yIsZeroAtTheTop ? -1 : 1;
  938. my v_rectangle (wdx (x1WC) + 1, wdx (x2WC) - 1, wdy (y1WC) + dy, wdy (y2WC) - dy);
  939. if (my recording) { op (INNER_RECTANGLE, 4); put (x1WC); put (x2WC); put (y1WC); put (y2WC); }
  940. }
  941. void Graphics_circle (Graphics me, double xWC, double yWC, double rWC) {
  942. my v_circle (wdx (xWC), wdy (yWC), my scaleX * rWC);
  943. if (my recording) { op (CIRCLE, 3); put (xWC); put (yWC); put (rWC); }
  944. }
  945. void Graphics_circle_mm (Graphics me, double xWC, double yWC, double diameter) {
  946. my v_circle (wdx (xWC), wdy (yWC), 0.5 * diameter * my resolution / 25.4);
  947. if (my recording) { op (CIRCLE_MM, 3); put (xWC); put (yWC); put (diameter); }
  948. }
  949. void Graphics_fillCircle (Graphics me, double xWC, double yWC, double rWC) {
  950. my v_fillCircle (wdx (xWC), wdy (yWC), Melder_iceiling (my scaleX * rWC));
  951. if (my recording) { op (FILL_CIRCLE, 3); put (xWC); put (yWC); put (rWC); }
  952. }
  953. void Graphics_fillCircle_mm (Graphics me, double xWC, double yWC, double diameter) {
  954. my v_fillCircle (wdx (xWC), wdy (yWC), Melder_iceiling (0.5 * diameter * my resolution / 25.4));
  955. if (my recording) { op (FILL_CIRCLE_MM, 3); put (xWC); put (yWC); put (diameter); }
  956. }
  957. void Graphics_speckle (Graphics me, double xWC, double yWC) {
  958. my v_fillCircle (wdx (xWC), wdy (yWC), Melder_iceiling (0.5 * my speckleSize * my resolution / 25.4));
  959. if (my recording) { op (SPECKLE, 2); put (xWC); put (yWC); }
  960. }
  961. void Graphics_rectangle_mm (Graphics me, double xWC, double yWC, double horSide, double vertSide) {
  962. integer xDC = wdx (xWC), yDC = wdy (yWC);
  963. integer halfHorSide = Melder_iceiling (0.5 * horSide * my resolution / 25.4);
  964. integer halfVertSide = Melder_iceiling (0.5 * vertSide * my resolution / 25.4);
  965. if (my yIsZeroAtTheTop) {
  966. my v_rectangle (xDC - halfHorSide, xDC + halfHorSide, yDC + halfVertSide, yDC - halfVertSide);
  967. } else {
  968. my v_rectangle (xDC - halfHorSide, xDC + halfHorSide, yDC - halfVertSide, yDC + halfVertSide);
  969. }
  970. if (my recording) { op (RECTANGLE_MM, 4); put (xWC); put (yWC); put (horSide); put (vertSide); }
  971. }
  972. void Graphics_fillRectangle_mm (Graphics me, double xWC, double yWC, double horSide, double vertSide) {
  973. integer xDC = wdx (xWC), yDC = wdy (yWC);
  974. integer halfHorSide = Melder_iceiling (0.5 * horSide * my resolution / 25.4);
  975. integer halfVertSide = Melder_iceiling (0.5 * vertSide * my resolution / 25.4);
  976. if (my yIsZeroAtTheTop) {
  977. my v_fillRectangle (xDC - halfHorSide, xDC + halfHorSide, yDC + halfVertSide, yDC - halfVertSide);
  978. } else {
  979. my v_fillRectangle (xDC - halfHorSide, xDC + halfHorSide, yDC - halfVertSide, yDC + halfVertSide);
  980. }
  981. if (my recording) { op (FILL_RECTANGLE_MM, 4); put (xWC); put (yWC); put (horSide); put (vertSide); }
  982. }
  983. void Graphics_ellipse (Graphics me, double x1, double x2, double y1, double y2) {
  984. my v_ellipse (wdx (x1), wdx (x2), wdy (y1), wdy (y2));
  985. if (my recording) { op (ELLIPSE, 4); put (x1); put (x2); put (y1); put (y2); }
  986. }
  987. void Graphics_fillEllipse (Graphics me, double x1, double x2, double y1, double y2) {
  988. my v_fillEllipse (wdx (x1), wdx (x2), wdy (y1), wdy (y2));
  989. if (my recording) { op (FILL_ELLIPSE, 4); put (x1); put (x2); put (y1); put (y2); }
  990. }
  991. void Graphics_arc (Graphics me, double xWC, double yWC, double rWC, double fromAngle, double toAngle) {
  992. my v_arc (wdx (xWC), wdy (yWC), my scaleX * rWC, fromAngle, toAngle);
  993. if (my recording) { op (ARC, 5); put (xWC); put (yWC); put (rWC); put (fromAngle); put (toAngle); }
  994. }
  995. void Graphics_fillArc (Graphics me, double xWC, double yWC, double rWC, double fromAngle, double toAngle) {
  996. my v_arc (wdx (xWC), wdy (yWC), my scaleX * rWC, fromAngle, toAngle); // NYI v_fillArc
  997. if (my recording) { op (FILL_ARC, 5); put (xWC); put (yWC); put (rWC); put (fromAngle); put (toAngle); }
  998. }
  999. /* Arrows. */
  1000. void structGraphics :: v_arrowHead (double xDC, double yDC, double angle) {
  1001. (void) xDC;
  1002. (void) yDC;
  1003. (void) angle;
  1004. }
  1005. void structGraphicsScreen :: v_arrowHead (double xDC, double yDC, double angle) {
  1006. #if cairo
  1007. if (! our d_cairoGraphicsContext) return;
  1008. double size = 10.0 * our resolution * our arrowSize / 75.0; // TODO: die 75 zou dat niet de scherm resolutie moeten worden?
  1009. cairo_new_path (our d_cairoGraphicsContext);
  1010. cairo_move_to (our d_cairoGraphicsContext, xDC + cos ((angle + 160.0) * NUMpi / 180.0) * size, yDC - sin ((angle + 160.0) * NUMpi / 180.0) * size);
  1011. cairo_line_to (our d_cairoGraphicsContext, xDC, yDC);
  1012. cairo_line_to (our d_cairoGraphicsContext, xDC + cos ((angle - 160.0) * NUMpi / 180.0) * size, yDC - sin ((angle - 160.0) * NUMpi / 180.0) * size);
  1013. cairo_close_path (our d_cairoGraphicsContext);
  1014. cairo_fill (our d_cairoGraphicsContext);
  1015. #elif gdi
  1016. double size = 10.0 * our resolution * our arrowSize / 72.0;
  1017. MY_BRUSH
  1018. BeginPath (our d_gdiGraphicsContext);
  1019. MoveToEx (our d_gdiGraphicsContext, xDC + cos ((angle + 160.0) * NUMpi / 180.0) * size, yDC - sin ((angle + 160.0) * NUMpi / 180.0) * size, nullptr);
  1020. LineTo (our d_gdiGraphicsContext, xDC, yDC);
  1021. LineTo (our d_gdiGraphicsContext, xDC + cos ((angle - 160.0) * NUMpi / 180.0) * size, yDC - sin ((angle - 160.0) * NUMpi / 180.0) * size);
  1022. EndPath (our d_gdiGraphicsContext);
  1023. FillPath (our d_gdiGraphicsContext);
  1024. DEFAULT
  1025. #elif quartz
  1026. GraphicsQuartz_initDraw (this);
  1027. quartzPrepareFill (this);
  1028. NSCAssert (our d_macGraphicsContext, @"nil context");
  1029. CGContextSaveGState (our d_macGraphicsContext);
  1030. CGContextBeginPath (our d_macGraphicsContext);
  1031. CGContextTranslateCTM (our d_macGraphicsContext, xDC, yDC);
  1032. CGContextRotateCTM (our d_macGraphicsContext, - angle * NUMpi / 180.0);
  1033. CGContextMoveToPoint (our d_macGraphicsContext, 0.0, 0.0);
  1034. double size = 10.0 * our resolution * our arrowSize / 72.0;
  1035. double radius = our resolution * our arrowSize / 30.0;
  1036. CGContextAddArc (our d_macGraphicsContext, - size, 0.0, radius, - NUMpi / 3.0, NUMpi / 3.0, 0);
  1037. CGContextAddLineToPoint (our d_macGraphicsContext, 0.0, 0.0);
  1038. CGContextFillPath (our d_macGraphicsContext);
  1039. CGContextRestoreGState (our d_macGraphicsContext);
  1040. GraphicsQuartz_exitDraw (this);
  1041. #endif
  1042. }
  1043. void structGraphicsPostscript :: v_arrowHead (double xDC, double yDC, double angle) {
  1044. double length = resolution * arrowSize / 10.0, radius = resolution * arrowSize / 30.0;
  1045. d_printf (d_file, "gsave %.7g %.7g translate %.7g rotate\n"
  1046. "N 0 0 M %.7g 0 %.7g -60 60 arc closepath fill grestore\n", xDC, yDC, angle, - length, radius);
  1047. }
  1048. void Graphics_arrow (Graphics me, double x1WC, double y1WC, double x2WC, double y2WC) {
  1049. double angle = (180.0 / NUMpi) * atan2 ((wdy (y2WC) - wdy (y1WC)) * (my yIsZeroAtTheTop ? -1.0 : 1.0), wdx (x2WC) - wdx (x1WC));
  1050. double size = my screen ? 10.0 * my resolution * my arrowSize / 72.0 : my resolution * my arrowSize / 10;
  1051. double xyDC [4];
  1052. xyDC [0] = wdx (x1WC);
  1053. xyDC [1] = wdy (y1WC);
  1054. xyDC [2] = wdx (x2WC) + (my screen ? 0.7 : 0.6) * cos ((angle - 180.0) * NUMpi / 180.0) * size;
  1055. xyDC [3] = wdy (y2WC) + (my yIsZeroAtTheTop ? -1.0 : 1.0) * (my screen ? 0.7 : 0.6) * sin ((angle - 180.0) * NUMpi / 180.0) * size;
  1056. my v_polyline (2, xyDC, false);
  1057. my v_arrowHead (wdx (x2WC), wdy (y2WC), angle);
  1058. if (my recording) { op (ARROW, 4); put (x1WC); put (y1WC); put (x2WC); put (y2WC); }
  1059. }
  1060. void Graphics_doubleArrow (Graphics me, double x1WC, double y1WC, double x2WC, double y2WC) {
  1061. double angle = (180.0 / NUMpi) * atan2 ((wdy (y2WC) - wdy (y1WC)) * (my yIsZeroAtTheTop ? -1.0 : 1.0), wdx (x2WC) - wdx (x1WC));
  1062. double size = my screen ? 10.0 * my resolution * my arrowSize / 72.0 : my resolution * my arrowSize / 10.0;
  1063. double xyDC [4];
  1064. xyDC [0] = wdx (x1WC) + (my screen ? 0.7 : 0.6) * cos (angle * NUMpi / 180.0) * size;
  1065. xyDC [1] = wdy (y1WC) + (my yIsZeroAtTheTop ? -1.0 : 1.0) * (my screen ? 0.7 : 0.6) * sin (angle * NUMpi / 180.0) * size;
  1066. xyDC [2] = wdx (x2WC) + (my screen ? 0.7 : 0.6) * cos ((angle - 180) * NUMpi / 180.0) * size;
  1067. xyDC [3] = wdy (y2WC) + (my yIsZeroAtTheTop ? -1.0 : 1.0) * (my screen ? 0.7 : 0.6) * sin ((angle - 180.0) * NUMpi / 180.0) * size;
  1068. my v_polyline (2, xyDC, false);
  1069. my v_arrowHead (wdx (x1WC), wdy (y1WC), angle + 180.0);
  1070. //my v_polyline (2, xyDC);
  1071. my v_arrowHead (wdx (x2WC), wdy (y2WC), angle);
  1072. if (my recording) { op (DOUBLE_ARROW, 4); put (x1WC); put (y1WC); put (x2WC); put (y2WC); }
  1073. }
  1074. void Graphics_arcArrow (Graphics me, double xWC, double yWC, double rWC,
  1075. double fromAngle, double toAngle, int arrowAtStart, int arrowAtEnd)
  1076. {
  1077. my v_arc (wdx (xWC), wdy (yWC), my scaleX * rWC, fromAngle, toAngle);
  1078. if (arrowAtStart) my v_arrowHead (
  1079. wdx (xWC + rWC * cos (fromAngle * (NUMpi / 180.0))),
  1080. wdy (yWC + rWC * sin (fromAngle * (NUMpi / 180.0))), fromAngle - 90.0);
  1081. if (arrowAtEnd) my v_arrowHead (
  1082. wdx (xWC + rWC * cos (toAngle * (NUMpi / 180.0))),
  1083. wdy (yWC + rWC * sin (toAngle * (NUMpi / 180.0))), toAngle + 90.0);
  1084. if (my recording)
  1085. { op (ARC_ARROW, 7); put (xWC); put (yWC); put (rWC);
  1086. put (fromAngle); put (toAngle); put (arrowAtStart); put (arrowAtEnd); }
  1087. }
  1088. /* Output attributes. */
  1089. void Graphics_setLineType (Graphics me, int lineType) {
  1090. my lineType = lineType;
  1091. if (my recording) { op (SET_LINE_TYPE, 1); put (lineType); }
  1092. }
  1093. void Graphics_setLineWidth (Graphics me, double lineWidth) {
  1094. my lineWidth = lineWidth;
  1095. if (my recording) { op (SET_LINE_WIDTH, 1); put (lineWidth); }
  1096. }
  1097. void Graphics_setArrowSize (Graphics me, double arrowSize) {
  1098. my arrowSize = arrowSize;
  1099. if (my recording) { op (SET_ARROW_SIZE, 1); put (arrowSize); }
  1100. }
  1101. void Graphics_setSpeckleSize (Graphics me, double speckleSize) {
  1102. my speckleSize = speckleSize;
  1103. if (my recording) { op (SET_SPECKLE_SIZE, 1); put (speckleSize); }
  1104. }
  1105. /* Inquiries. */
  1106. int Graphics_inqLineType (Graphics me) { return my lineType; }
  1107. double Graphics_inqLineWidth (Graphics me) { return my lineWidth; }
  1108. double Graphics_inqArrowSize (Graphics me) { return my arrowSize; }
  1109. double Graphics_inqSpeckleSize (Graphics me) { return my speckleSize; }
  1110. /* End of file Graphics_linesAndAreas.cpp */