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