1/* 2 * 3Copyright (c) 1991 X Consortium 4 5Permission is hereby granted, free of charge, to any person obtaining a copy 6of this software and associated documentation files (the "Software"), to deal 7in the Software without restriction, including without limitation the rights 8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9copies of the Software, and to permit persons to whom the Software is 10furnished to do so, subject to the following conditions: 11 12The above copyright notice and this permission notice shall be included in 13all copies or substantial portions of the Software. 14 15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22Except as contained in this notice, the name of the X Consortium shall not be 23used in advertising or otherwise to promote the sale, use or other dealings 24in this Software without prior written authorization from the X Consortium. 25 * 26 */ 27 28/* 29 * draw.c 30 * 31 * accept dvi function calls and translate to X 32 */ 33 34/* 35 Support for ditroff drawing commands added: lines, circles, ellipses, 36 arcs and splines. Splines are approximated as short lines by iterating 37 a simple approximation algorithm. This seems good enough for previewing. 38 39 David Evans <dre@cs.nott.ac.uk>, 14th March, 1990 40*/ 41#ifdef HAVE_CONFIG_H 42# include "config.h" 43#endif 44 45#include <X11/Xos.h> 46#include <X11/IntrinsicP.h> 47#include <X11/StringDefs.h> 48#include <stdio.h> 49#include <ctype.h> 50#include <math.h> 51#include "DviP.h" 52#include <stdlib.h> 53 54#ifndef M_PI 55#define M_PI 3.14159265358979323846264338327950 56#endif 57 58/* the following are for use in the spline approximation algorithm */ 59 60typedef struct Point { 61 double x; 62 double y; 63 struct Point *next; 64} Point; 65 66#define ITERATIONS 10 /* iterations to approximate spline */ 67 68#define midx(p,q) ((p->x + q->x) / 2) /* mid x point on pq */ 69#define midy(p,q) ((p->y + q->y) / 2) /* mid y point on pq */ 70 71#define length(p,q) sqrt(((q->x - p->x)*(q->x - p->x)) \ 72 + ((q->y - p->y)*(q->y - p->y))) /* length of pq */ 73 74static Point *spline = (Point *) NULL; /* head of spline linked list */ 75 76static void ApproxSpline(int n); 77static void DeletePoint(Point *p); 78static void DrawSplineSegments(DviWidget dw); 79static int GetSpline(const char *s); 80static void InsertPoint(Point *p, Point *q); 81static void LineApprox(Point *p1, Point *p2, Point *p3); 82static Point * MakePoint(double x, double y); 83 84#ifdef USE_XFT 85static void 86DrawText(DviWidget dw) 87{ 88 XftFont *font = dw->dvi.cache.font; 89 90 for (int i = 0; i <= dw->dvi.cache.index; i++) { 91 if (dw->dvi.cache.cache[i].font) 92 font = dw->dvi.cache.cache[i].font; 93 XftDrawString8(dw->dvi.draw, &dw->dvi.black, 94 font, 95 dw->dvi.cache.cache[i].x, 96 dw->dvi.cache.start_y, 97 (unsigned char *) dw->dvi.cache.cache[i].chars, 98 dw->dvi.cache.cache[i].nchars); 99 } 100} 101#endif 102 103void 104FlushCharCache(DviWidget dw) 105{ 106 int xx = ToX(dw, dw->dvi.state->x); 107 int yx = ToX(dw, dw->dvi.state->y); 108 109 if (dw->dvi.cache.char_index != 0) { 110#ifdef USE_XFT 111 DrawText(dw); 112#else 113 XDrawText(XtDisplay(dw), XtWindow(dw), dw->dvi.normal_GC, 114 dw->dvi.cache.start_x, dw->dvi.cache.start_y, 115 dw->dvi.cache.cache, dw->dvi.cache.index + 1); 116#endif 117 } 118 dw->dvi.cache.index = 0; 119 dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE; 120 if (dw->dvi.noPolyText) 121 dw->dvi.cache.max = 1; 122 dw->dvi.cache.char_index = 0; 123 dw->dvi.cache.cache[0].nchars = 0; 124 dw->dvi.cache.start_x = dw->dvi.cache.x = xx; 125 dw->dvi.cache.start_y = dw->dvi.cache.y = yx; 126} 127 128void 129SetGCForDraw(DviWidget dw) 130{ 131 if (dw->dvi.state->line_style != dw->dvi.line_style || 132 dw->dvi.state->line_width != dw->dvi.line_width) { 133 int lw = ToX(dw, dw->dvi.state->line_width); 134 135 if (lw <= 1) 136 lw = 0; 137 XSetLineAttributes(XtDisplay(dw), dw->dvi.normal_GC, 138 lw, LineSolid, CapButt, JoinMiter); 139 dw->dvi.line_style = dw->dvi.state->line_style; 140 dw->dvi.line_width = dw->dvi.state->line_width; 141 } 142} 143 144void 145DrawLine(DviWidget dw, int x, int y) 146{ 147 if (dw->dvi.display_enable) 148 XDrawLine(XtDisplay(dw), XtWindow(dw), dw->dvi.normal_GC, 149 ToX(dw, dw->dvi.state->x), ToX(dw, dw->dvi.state->y), 150 ToX(dw, dw->dvi.state->x + x), ToX(dw, dw->dvi.state->y + y)); 151 dw->dvi.state->x += x; 152 dw->dvi.state->y += y; 153} 154 155void 156DrawCircle(DviWidget dw, int diameter) 157{ 158 if (dw->dvi.display_enable) 159 XDrawArc(XtDisplay(dw), XtWindow(dw), dw->dvi.normal_GC, 160 ToX(dw, dw->dvi.state->x), 161 ToX(dw, dw->dvi.state->y - (diameter / 2)), 162 ToX(dw, diameter), ToX(dw, diameter), 0, 360 * 64); 163 dw->dvi.state->x += diameter; 164} 165 166void 167DrawEllipse(DviWidget dw, int a, int b) 168{ 169 if (dw->dvi.display_enable) 170 XDrawArc(XtDisplay(dw), XtWindow(dw), dw->dvi.normal_GC, 171 ToX(dw, dw->dvi.state->x), ToX(dw, dw->dvi.state->y - (b / 2)), 172 ToX(dw, a), ToX(dw, b), 0, 360 * 64); 173 dw->dvi.state->x += a; 174} 175 176/* Convert angle in degrees to 64ths of a degree */ 177 178static int 179ConvertAngle(int theta) 180{ 181 return (theta * 64); 182} 183 184void 185DrawArc(DviWidget dw, int x0, int y0, int x1, int y1) 186{ 187 /* centre */ 188 int xc = dw->dvi.state->x + x0; 189 int yc = dw->dvi.state->y + y0; 190 191 /* to */ 192 int x2 = xc + x1; 193 int y2 = yc + y1; 194 195 dw->dvi.state->x = x2; 196 dw->dvi.state->y = y2; 197 198 if (dw->dvi.display_enable) { 199 int angle1, angle2; 200 201 /* radius */ 202 int r = (int) sqrt((float) x1 * x1 + (float) y1 * y1); 203 204 /* start and finish angles */ 205 if (x0 == 0) { 206 if (y0 >= 0) 207 angle1 = 90; 208 else 209 angle1 = 270; 210 } 211 else { 212 angle1 = (int) (atan((double) (y0) / (double) (x0)) * 180 / M_PI); 213 if (x0 > 0) 214 angle1 = 180 - angle1; 215 else 216 angle1 = -angle1; 217 } 218 219 if (x1 == 0) { 220 if (y1 <= 0) 221 angle2 = 90; 222 else 223 angle2 = 270; 224 } 225 else { 226 angle2 = (int) (atan((double) (y1) / (double) (x1)) * 180 / M_PI); 227 if (x1 < 0) 228 angle2 = 180 - angle2; 229 else 230 angle2 = -angle2; 231 } 232 233 if (angle1 < 0) 234 angle1 += 360; 235 if (angle2 < 0) 236 angle2 += 360; 237 238 if (angle2 < angle1) 239 angle1 -= 360; 240 angle2 = angle2 - angle1; 241 242 angle1 = ConvertAngle(angle1); 243 angle2 = ConvertAngle(angle2); 244 245 XDrawArc(XtDisplay(dw), XtWindow(dw), dw->dvi.normal_GC, 246 ToX(dw, xc - r), ToX(dw, yc - r), 247 ToX(dw, 2 * r), ToX(dw, 2 * r), angle1, angle2); 248 } 249} 250 251/* copy next non-blank string from p to temp, update p */ 252 253static const char * 254getstr(const char *p, char *temp, size_t temp_size) 255{ 256 while (*p == ' ' || *p == '\t' || *p == '\n') 257 p++; 258 if (*p == '\0') { 259 temp[0] = 0; 260 return ((char *) NULL); 261 } 262 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') { 263 /* leave room for trailing NIL byte */ 264 if (temp_size > 1) { 265 *temp++ = *p++; 266 temp_size--; 267 } 268 } 269 *temp = '\0'; 270 return (p); 271} 272 273/* Draw a spline by approximating with short lines. */ 274 275/*ARGSUSED*/ 276void 277DrawSpline(DviWidget dw, const char *s, int len) 278{ 279 int n; 280 281 /* get coordinate pairs into spline linked list */ 282 if ((n = GetSpline(s)) <= 0) 283 return; 284 285 ApproxSpline(n); 286 287 DrawSplineSegments(dw); 288} 289 290 291/* Parse string s to create a linked list of Point's with spline */ 292/* as its head. Return the number of coordinate pairs found. */ 293 294static int 295GetSpline(const char *s) 296{ 297 double x, y; 298 int n = 0; 299 Point *pt; 300 const char *p = s; 301 char d[10]; 302 303 if (!*p) 304 return (n); 305 306 pt = spline = MakePoint(0.0, 0.0); 307 n = 1; 308 x = y = 0.0; 309 p = s; 310 while (p && *p) { 311 double x1, y1; 312 313 if ((p = getstr(p, d, sizeof(d))) == (const char *) NULL) 314 break; 315 x1 = x + atof(d); 316 if ((p = getstr(p, d, sizeof(d))) == (const char *) NULL) 317 break; 318 y1 = y + atof(d); 319 pt->next = MakePoint(x1, y1); 320 pt = pt->next; 321 x = pt->x; 322 y = pt->y; 323 n++; 324 } 325 326 /* number of pairs of points */ 327 328 return (n); 329} 330 331/* Approximate a spline by lines generated by iterations of the */ 332/* approximation algorithm from the original n points in the spline. */ 333 334static void 335ApproxSpline(int n) 336{ 337 int mid; 338 Point *p1, *p2, *p3, *p; 339 340 if (n < 3) 341 return; 342 343 /* number of mid-points to calculate */ 344 mid = n - 3; 345 346 /* remember original points are stored as an array of n points */ 347 /* so I can index it directly to calculate mid-points only. */ 348 if (mid > 0) { 349 int j; 350 351 p = spline->next; 352 j = 1; 353 while (j < n - 2) { 354 p1 = p; 355 p = p->next; 356 p2 = p; 357 InsertPoint(p1, MakePoint(midx(p1, p2), midy(p1, p2))); 358 j++; 359 } 360 } 361 362 /* Now approximate curve by line segments. */ 363 /* There *should* be the correct number of points now! */ 364 365 p = spline; 366 while (p != (Point *) NULL) { 367 p1 = p; 368 if ((p = p->next) == (Point *) NULL) 369 break; 370 p2 = p; 371 if ((p = p->next) == (Point *) NULL) 372 break; 373 p3 = p; /* This point becomes first point of next curve */ 374 375 LineApprox(p1, p2, p3); 376 } 377} 378 379/* p1, p2, and p3 are initially 3 *consecutive* points on the curve. */ 380/* For each adjacent pair of points find the mid-point, insert this */ 381/* in the linked list, delete the first of the two used (unless it */ 382/* is the first for this curve). Repeat this ITERATIONS times. */ 383 384/*ARGSUSED*/ 385static void 386LineApprox(Point *p1, Point *p2, Point *p3) 387{ 388 int reps = ITERATIONS; 389 390 while (reps) { 391 for (Point *p = p1; p != (Point *) NULL && p != p3;) { 392 Point *p4 = MakePoint(midx(p, p->next), midy(p, p->next)); 393 394 InsertPoint(p, p4); 395 if (p != p1) 396 DeletePoint(p); 397 p = p4->next; /* skip inserted point! */ 398 } 399 reps--; 400 } 401} 402 403 404/* Traverse the linked list, calling DrawLine to approximate the */ 405/* spline curve. Rounding errors are taken into account so that */ 406/* the "curve" is continuous, and ends up where expected. */ 407 408static void 409DrawSplineSegments(DviWidget dw) 410{ 411 Point *p = spline; 412 413 /* save the start position */ 414 double xpos = dw->dvi.state->x; 415 double ypos = dw->dvi.state->y; 416 417 double x1 = 0.0, y1 = 0.0; 418 419 while (p != (Point *) NULL) { 420 int dx = p->x - x1 + 0.5; 421 int dy = p->y - y1 + 0.5; 422 423 DrawLine(dw, dx, dy); 424 425 x1 = p->x; 426 y1 = p->y; 427 dw->dvi.state->x = xpos + x1; 428 dw->dvi.state->y = ypos + y1; 429 430 Point *q = p; 431 432 p = p->next; 433 XtFree((char *) q); 434 } 435 spline = (Point *) NULL; 436} 437 438/* Malloc memory for a Point, and initialise the elements to x, y, NULL */ 439/* Return a pointer to the new Point. */ 440 441static Point * 442MakePoint(double x, double y) 443{ 444 Point *p = (Point *) XtMalloc(sizeof(Point)); 445 446 p->x = x; 447 p->y = y; 448 p->next = (Point *) NULL; 449 450 return (p); 451} 452 453/* Insert point q in linked list after point p. */ 454 455static void 456InsertPoint(Point *p, Point *q) 457{ 458 /* point q to the next point */ 459 q->next = p->next; 460 461 /* point p to new inserted one */ 462 p->next = q; 463} 464 465/* Delete point p from the linked list. */ 466 467static void 468DeletePoint(Point *p) 469{ 470 Point *tmp = p->next; 471 472 p->x = p->next->x; 473 p->y = p->next->y; 474 p->next = p->next->next; 475 XtFree((char *) tmp); 476} 477