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