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