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