draw.c revision c166fba9
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 42#include <X11/Xos.h> 43#include <X11/IntrinsicP.h> 44#include <X11/StringDefs.h> 45#include <stdio.h> 46#include <ctype.h> 47#include <math.h> 48#include "DviP.h" 49#include <stdlib.h> 50 51#ifndef M_PI 52#define M_PI 3.14159265358979323846264338327950 53#endif 54 55/* the following are for use in the spline approximation algorithm */ 56 57typedef struct Point { 58 double x; 59 double y; 60 struct Point *next; 61} Point; 62 63#define ITERATIONS 10 /* iterations to approximate spline */ 64 65#define midx(p,q) ((p->x + q->x) / 2) /* mid x point on pq */ 66#define midy(p,q) ((p->y + q->y) / 2) /* mid y point on pq */ 67 68#define length(p,q) sqrt(((q->x - p->x)*(q->x - p->x)) \ 69 + ((q->y - p->y)*(q->y - p->y))) /* length of pq */ 70 71static Point *spline = (Point *)NULL; /* head of spline linked list */ 72 73static void ApproxSpline(int n); 74static void DeletePoint(Point *p); 75static void DrawSplineSegments(DviWidget dw); 76static int GetSpline(const char *s); 77static void InsertPoint(Point *p, Point *q); 78static void LineApprox(Point *p1, Point *p2, Point *p3); 79static Point * MakePoint(double x, double y); 80 81void 82HorizontalMove(DviWidget dw, int delta) 83{ 84 dw->dvi.state->x += delta; 85} 86 87void 88HorizontalGoto(DviWidget dw, int NewPosition) 89{ 90 dw->dvi.state->x = NewPosition; 91} 92 93void 94VerticalMove(DviWidget dw, int delta) 95{ 96 dw->dvi.state->y += delta; 97} 98 99void 100VerticalGoto(DviWidget dw, int NewPosition) 101{ 102 dw->dvi.state->y = NewPosition; 103} 104 105#ifdef USE_XFT 106static void 107DrawText (DviWidget dw) 108{ 109 int i; 110 XftFont *font; 111 112 font = dw->dvi.cache.font; 113 for (i = 0; i <= dw->dvi.cache.index; i++) 114 { 115 if (dw->dvi.cache.cache[i].font) 116 font = dw->dvi.cache.cache[i].font; 117 XftDrawString8 (dw->dvi.draw, &dw->dvi.black, 118 font, 119 dw->dvi.cache.cache[i].x, 120 dw->dvi.cache.start_y, 121 (unsigned char *) dw->dvi.cache.cache[i].chars, 122 dw->dvi.cache.cache[i].nchars); 123 } 124} 125#endif 126 127void 128FlushCharCache (DviWidget dw) 129{ 130 int xx, yx; 131 132 xx = ToX(dw, dw->dvi.state->x); 133 yx = ToX(dw, dw->dvi.state->y); 134 if (dw->dvi.cache.char_index != 0) 135 { 136#ifdef USE_XFT 137 DrawText (dw); 138#else 139 XDrawText (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, 140 dw->dvi.cache.start_x, dw->dvi.cache.start_y, 141 dw->dvi.cache.cache, dw->dvi.cache.index + 1); 142#endif 143 } 144 dw->dvi.cache.index = 0; 145 dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE; 146 if (dw->dvi.noPolyText) 147 dw->dvi.cache.max = 1; 148 dw->dvi.cache.char_index = 0; 149 dw->dvi.cache.cache[0].nchars = 0; 150 dw->dvi.cache.start_x = dw->dvi.cache.x = xx; 151 dw->dvi.cache.start_y = dw->dvi.cache.y = yx; 152} 153 154void 155SetGCForDraw (DviWidget dw) 156{ 157 int lw; 158 if (dw->dvi.state->line_style != dw->dvi.line_style || 159 dw->dvi.state->line_width != dw->dvi.line_width) 160 { 161 lw = ToX(dw, dw->dvi.state->line_width); 162 if (lw <= 1) 163 lw = 0; 164 XSetLineAttributes (XtDisplay (dw), dw->dvi.normal_GC, 165 lw, LineSolid, CapButt, JoinMiter); 166 dw->dvi.line_style = dw->dvi.state->line_style; 167 dw->dvi.line_width = dw->dvi.state->line_width; 168 } 169} 170 171void 172DrawLine (DviWidget dw, int x, int y) 173{ 174 if (dw->dvi.display_enable) 175 XDrawLine (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, 176 ToX(dw, dw->dvi.state->x), ToX(dw, dw->dvi.state->y), 177 ToX(dw, dw->dvi.state->x + x), ToX(dw,dw->dvi.state->y + y)); 178 dw->dvi.state->x += x; 179 dw->dvi.state->y += y; 180} 181 182void 183DrawCircle (DviWidget dw, int diameter) 184{ 185 if (dw->dvi.display_enable) 186 XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, 187 ToX(dw, dw->dvi.state->x), 188 ToX(dw, dw->dvi.state->y - (diameter / 2)), 189 ToX(dw, diameter), ToX(dw, diameter), 0, 360 * 64); 190 dw->dvi.state->x += diameter; 191} 192 193void 194DrawEllipse (DviWidget dw, int a, int b) 195{ 196 if (dw->dvi.display_enable) 197 XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, 198 ToX(dw, dw->dvi.state->x), ToX(dw, dw->dvi.state->y - (b / 2)), 199 ToX(dw,a), ToX(dw,b), 0, 360 * 64); 200 dw->dvi.state->x += a; 201} 202 203 204/* Convert angle in degrees to 64ths of a degree */ 205 206static int 207ConvertAngle(int theta) 208{ 209 return(theta * 64); 210} 211 212void 213DrawArc (DviWidget dw, int x0, int y0, int x1, int y1) 214{ 215 int xc, yc, x2, y2, r; 216 int angle1, angle2; 217 218 /* centre */ 219 xc = dw->dvi.state->x + x0; 220 yc = dw->dvi.state->y + y0; 221 222 /* to */ 223 x2 = xc + x1; 224 y2 = yc + y1; 225 226 dw->dvi.state->x = x2; 227 dw->dvi.state->y = y2; 228 229 if (dw->dvi.display_enable) { 230 231 /* radius */ 232 r = (int)sqrt((float) x1 * x1 + (float) y1 * y1); 233 234 /* start and finish angles */ 235 if (x0 == 0) { 236 if (y0 >= 0) 237 angle1 = 90; 238 else 239 angle1 = 270; 240 } 241 else { 242 angle1 = (int) (atan((double)(y0) / (double)(x0)) * 180 / M_PI); 243 if (x0 > 0) 244 angle1 = 180 - angle1; 245 else 246 angle1 = -angle1; 247 } 248 249 if (x1 == 0) { 250 if (y1 <= 0) 251 angle2 = 90; 252 else 253 angle2 = 270; 254 } 255 else { 256 angle2 = (int) (atan((double)(y1) / (double)(x1)) * 180 / M_PI); 257 if (x1 < 0) 258 angle2 = 180 - angle2; 259 else 260 angle2 = -angle2; 261 } 262 263 if (angle1 < 0) 264 angle1 += 360; 265 if (angle2 < 0) 266 angle2 += 360; 267 268 if (angle2 < angle1) 269 angle1 -= 360; 270 angle2 = angle2 - angle1; 271 272 angle1 = ConvertAngle(angle1); 273 angle2 = ConvertAngle(angle2); 274 275 XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, 276 ToX(dw, xc - r), ToX(dw, yc - r), 277 ToX(dw, 2 * r), ToX(dw, 2 * r), 278 angle1, angle2); 279 } 280} 281 282/* copy next non-blank string from p to temp, update p */ 283 284static const char * 285getstr(const char *p, char *temp) 286{ 287 while (*p == ' ' || *p == '\t' || *p == '\n') 288 p++; 289 if (*p == '\0') { 290 temp[0] = 0; 291 return((char *)NULL); 292 } 293 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0') 294 *temp++ = *p++; 295 *temp = '\0'; 296 return(p); 297} 298 299 300/* Draw a spline by approximating with short lines. */ 301 302/*ARGSUSED*/ 303void 304DrawSpline (DviWidget dw, const char *s, int len) 305{ 306 int n; 307 308 /* get coordinate pairs into spline linked list */ 309 if ((n = GetSpline(s)) <= 0) 310 return; 311 312 ApproxSpline(n); 313 314 DrawSplineSegments(dw); 315} 316 317 318/* Parse string s to create a linked list of Point's with spline */ 319/* as its head. Return the number of coordinate pairs found. */ 320 321static int 322GetSpline(const char *s) 323{ 324 double x, y, x1, y1; 325 int n = 0; 326 Point *pt; 327 const char *p = s; 328 char d[10]; 329 330 if (!*p) 331 return(n); 332 333 pt = spline = MakePoint(0.0, 0.0); 334 n = 1; 335 x = y = 0.0; 336 p = s; 337 while (p && *p) { 338 if ((p = getstr(p, d)) == (const char *)NULL) 339 break; 340 x1 = x + atof(d); 341 if ((p = getstr(p, d)) == (const char *)NULL) 342 break; 343 y1 = y + atof(d); 344 pt->next = MakePoint(x1, y1); 345 pt = pt->next; 346 x = pt->x; 347 y = pt->y; 348 n++; 349 } 350 351 /* number of pairs of points */ 352 353 return(n); 354} 355 356/* Approximate a spline by lines generated by iterations of the */ 357/* approximation algorithm from the original n points in the spline. */ 358 359static void 360ApproxSpline(int n) 361{ 362 int mid, j; 363 Point *p1, *p2, *p3, *p; 364 365 if (n < 3) 366 return; 367 368 /* number of mid-points to calculate */ 369 mid = n - 3; 370 371 /* remember original points are stored as an array of n points */ 372 /* so I can index it directly to calculate mid-points only. */ 373 if (mid > 0) { 374 p = spline->next; 375 j = 1; 376 while (j < n-2) { 377 p1 = p; 378 p = p->next; 379 p2 = p; 380 InsertPoint(p1, MakePoint(midx(p1, p2), midy(p1, p2))); 381 j++; 382 } 383 } 384 385 /* Now approximate curve by line segments. */ 386 /* There *should* be the correct number of points now! */ 387 388 p = spline; 389 while (p != (Point *)NULL) { 390 p1 = p; 391 if ((p = p->next) == (Point *)NULL) 392 break; 393 p2 = p; 394 if ((p = p->next) == (Point *)NULL) 395 break; 396 p3 = p; /* This point becomes first point of next curve */ 397 398 LineApprox(p1, p2, p3); 399 } 400} 401 402 403/* p1, p2, and p3 are initially 3 *consecutive* points on the curve. */ 404/* For each adjacent pair of points find the mid-point, insert this */ 405/* in the linked list, delete the first of the two used (unless it */ 406/* is the first for this curve). Repeat this ITERATIONS times. */ 407 408/*ARGSUSED*/ 409static void 410LineApprox(Point *p1, Point *p2, Point *p3) 411{ 412 Point *p4, *p; 413 int reps = ITERATIONS; 414 415 while (reps) { 416 for (p = p1; p != (Point *)NULL && p != p3; ) { 417 InsertPoint(p, p4 = MakePoint( midx(p,p->next), midy(p,p->next) )); 418 if (p != p1) 419 DeletePoint(p); 420 p = p4->next; /* skip inserted point! */ 421 } 422 reps--; 423 } 424} 425 426 427/* Traverse the linked list, calling DrawLine to approximate the */ 428/* spline curve. Rounding errors are taken into account so that */ 429/* the "curve" is continuous, and ends up where expected. */ 430 431static void 432DrawSplineSegments(DviWidget dw) 433{ 434 Point *p, *q; 435 double x1, y1; 436 int dx, dy; 437 double xpos, ypos; 438 439 p = spline; 440 dx = dy = 0; 441 442 /* save the start position */ 443 444 xpos = dw->dvi.state->x; 445 ypos = dw->dvi.state->y; 446 447 x1 = y1 = 0.0; 448 449 while (p != (Point *)NULL) { 450 dx = p->x - x1 + 0.5; 451 dy = p->y - y1 + 0.5; 452 DrawLine (dw, dx, dy); 453 454 x1 = p->x; 455 y1 = p->y; 456 dw->dvi.state->x = xpos + x1; 457 dw->dvi.state->y = ypos + y1; 458 459 q = p; 460 p = p->next; 461 XtFree((char *)q); 462 } 463 spline = (Point *)NULL; 464} 465 466 467/* Malloc memory for a Point, and initialise the elements to x, y, NULL */ 468/* Return a pointer to the new Point. */ 469 470static Point * 471MakePoint(double x, double y) 472{ 473 Point *p; 474 475 p = (Point *) XtMalloc (sizeof (Point)); 476 p->x = x; 477 p->y = y; 478 p->next = (Point *)NULL; 479 480 return(p); 481} 482 483 484/* Insert point q in linked list after point p. */ 485 486static void 487InsertPoint(Point *p, Point *q) 488{ 489 /* point q to the next point */ 490 q->next = p->next; 491 492 /* point p to new inserted one */ 493 p->next = q; 494} 495 496/* Delete point p from the linked list. */ 497 498static void 499DeletePoint(Point *p) 500{ 501 Point *tmp; 502 503 tmp = p->next; 504 p->x = p->next->x; 505 p->y = p->next->y; 506 p->next = p->next->next; 507 XtFree((char *)tmp); 508} 509