1 //      Routines dealing with line generation.
2 //
3 // Copyright (C) 2004  Maurice LeBrun
4 //
5 // This file is part of PLplot.
6 //
7 // PLplot is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU Library General Public License as published
9 // by the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
11 //
12 // PLplot is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU Library General Public License for more details.
16 //
17 // You should have received a copy of the GNU Library General Public License
18 // along with PLplot; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 //
21 //
22 
23 #include "plplotP.h"
24 
25 #define INSIDE( ix, iy )    ( BETW( ix, xmin, xmax ) && BETW( iy, ymin, ymax ) )
26 
27 static PLINT xline[PL_MAXPOLY], yline[PL_MAXPOLY];
28 
29 static PLINT lastx = PL_UNDEFINED, lasty = PL_UNDEFINED;
30 
31 // Function prototypes
32 
33 // Draws a polyline within the clip limits.
34 
35 static void
36 pllclp( PLINT *x, PLINT *y, PLINT npts );
37 
38 // General line-drawing routine.  Takes line styles into account.
39 
40 static void
41 genlin( short *x, short *y, PLINT npts );
42 
43 // Draws a dashed line to the specified point from the previous one.
44 
45 static void
46 grdashline( short *x, short *y );
47 
48 // Determines if a point is inside a polygon or not
49 
50 // Interpolate between two points in n steps
51 
52 static PLFLT *
53 interpolate_between( int n, PLFLT a, PLFLT b );
54 
55 //--------------------------------------------------------------------------
56 // void pljoin()
57 //
58 // Draws a line segment from (x1, y1) to (x2, y2).
59 //--------------------------------------------------------------------------
60 
61 void
c_pljoin(PLFLT x1,PLFLT y1,PLFLT x2,PLFLT y2)62 c_pljoin( PLFLT x1, PLFLT y1, PLFLT x2, PLFLT y2 )
63 {
64     plP_movwor( x1, y1 );
65     plP_drawor( x2, y2 );
66 }
67 
68 //--------------------------------------------------------------------------
69 // void plline()
70 //
71 // Draws line segments connecting a series of points.
72 //--------------------------------------------------------------------------
73 
74 void
c_plline(PLINT n,PLFLT_VECTOR x,PLFLT_VECTOR y)75 c_plline( PLINT n, PLFLT_VECTOR x, PLFLT_VECTOR y )
76 {
77     if ( plsc->level < 3 )
78     {
79         plabort( "plline: Please set up window first" );
80         return;
81     }
82     plP_drawor_poly( x, y, n );
83 }
84 
85 //--------------------------------------------------------------------------
86 // void plpath()
87 //
88 // Draws a line segment from (x1, y1) to (x2, y2).  If a coordinate
89 // transform is defined then break the line up in to n pieces to approximate
90 // the path.  Otherwise it simply calls pljoin().
91 //--------------------------------------------------------------------------
92 
93 void
c_plpath(PLINT n,PLFLT x1,PLFLT y1,PLFLT x2,PLFLT y2)94 c_plpath( PLINT n, PLFLT x1, PLFLT y1, PLFLT x2, PLFLT y2 )
95 {
96     PLFLT *xs, *ys;
97 
98     if ( plsc->coordinate_transform == NULL )
99     {
100         // No transform, so fall back on pljoin for a normal straight line
101         pljoin( x1, y1, x2, y2 );
102     }
103     else
104     {
105         // Approximate the path in transformed space with a sequence of line
106         // segments.
107         xs = interpolate_between( n, x1, x2 );
108         ys = interpolate_between( n, y1, y2 );
109         if ( xs == NULL || ys == NULL )
110         {
111             plexit( "c_plpath: Insufficient memory" );
112             return;
113         }
114         plline( n, xs, ys );
115         // plP_interpolate allocates memory, so we have to free it here.
116         free( xs );
117         free( ys );
118     }
119 }
120 
121 //--------------------------------------------------------------------------
122 // void plline3(n, x, y, z)
123 //
124 // Draws a line in 3 space.  You must first set up the viewport, the
125 // 2d viewing window (in world coordinates), and the 3d normalized
126 // coordinate box.  See x18c.c for more info.
127 //
128 // This version adds clipping against the 3d bounding box specified in plw3d
129 //--------------------------------------------------------------------------
130 void
c_plline3(PLINT n,PLFLT_VECTOR x,PLFLT_VECTOR y,PLFLT_VECTOR z)131 c_plline3( PLINT n, PLFLT_VECTOR x, PLFLT_VECTOR y, PLFLT_VECTOR z )
132 {
133     int   i;
134     PLFLT vmin[3], vmax[3], zscale;
135 
136     if ( plsc->level < 3 )
137     {
138         plabort( "plline3: Please set up window first" );
139         return;
140     }
141 
142     // get the bounding box in 3d
143     plP_gdom( &vmin[0], &vmax[0], &vmin[1], &vmax[1] );
144     plP_grange( &zscale, &vmin[2], &vmax[2] );
145 
146     // interate over the vertices
147     for ( i = 0; i < n - 1; i++ )
148     {
149         PLFLT p0[3], p1[3];
150         int   axis;
151 
152         // copy the end points of the segment to allow clipping
153         p0[0] = x[i]; p0[1] = y[i]; p0[2] = z[i];
154         p1[0] = x[i + 1]; p1[1] = y[i + 1]; p1[2] = z[i + 1];
155 
156         // check against each axis of the bounding box
157         for ( axis = 0; axis < 3; axis++ )
158         {
159             if ( p0[axis] < vmin[axis] ) // first out
160             {
161                 if ( p1[axis] < vmin[axis] )
162                 {
163                     break; // both endpoints out so quit
164                 }
165                 else
166                 {
167                     int   j;
168                     // interpolate to find intersection with box
169                     PLFLT t = ( vmin[axis] - p0[axis] ) / ( p1[axis] - p0[axis] );
170                     p0[axis] = vmin[axis];
171                     for ( j = 1; j < 3; j++ )
172                     {
173                         int k = ( axis + j ) % 3;
174                         p0[k] = ( 1 - t ) * p0[k] + t * p1[k];
175                     }
176                 }
177             }
178             else if ( p1[axis] < vmin[axis] ) // second out
179             {
180                 int   j;
181                 // interpolate to find intersection with box
182                 PLFLT t = ( vmin[axis] - p0[axis] ) / ( p1[axis] - p0[axis] );
183                 p1[axis] = vmin[axis];
184                 for ( j = 1; j < 3; j++ )
185                 {
186                     int k = ( axis + j ) % 3;
187                     p1[k] = ( 1 - t ) * p0[k] + t * p1[k];
188                 }
189             }
190             if ( p0[axis] > vmax[axis] ) // first out
191             {
192                 if ( p1[axis] > vmax[axis] )
193                 {
194                     break; // both out so quit
195                 }
196                 else
197                 {
198                     int   j;
199                     // interpolate to find intersection with box
200                     PLFLT t = ( vmax[axis] - p0[axis] ) / ( p1[axis] - p0[axis] );
201                     p0[axis] = vmax[axis];
202                     for ( j = 1; j < 3; j++ )
203                     {
204                         int k = ( axis + j ) % 3;
205                         p0[k] = ( 1 - t ) * p0[k] + t * p1[k];
206                     }
207                 }
208             }
209             else if ( p1[axis] > vmax[axis] ) // second out
210             {
211                 int   j;
212                 // interpolate to find intersection with box
213                 PLFLT t = ( vmax[axis] - p0[axis] ) / ( p1[axis] - p0[axis] );
214                 p1[axis] = vmax[axis];
215                 for ( j = 1; j < 3; j++ )
216                 {
217                     int k = ( axis + j ) % 3;
218                     p1[k] = ( 1 - t ) * p0[k] + t * p1[k];
219                 }
220             }
221         }
222         // if we made it to here without "break"ing out of the loop, the
223         // remaining segment is visible
224         if ( axis == 3 ) //  not clipped away
225         {
226             PLFLT u0, v0, u1, v1;
227             u0 = plP_wcpcx( plP_w3wcx( p0[0], p0[1], p0[2] ) );
228             v0 = plP_wcpcy( plP_w3wcy( p0[0], p0[1], p0[2] ) );
229             u1 = plP_wcpcx( plP_w3wcx( p1[0], p1[1], p1[2] ) );
230             v1 = plP_wcpcy( plP_w3wcy( p1[0], p1[1], p1[2] ) );
231             plP_movphy( (PLINT) u0, (PLINT) v0 );
232             plP_draphy( (PLINT) u1, (PLINT) v1 );
233         }
234     }
235     return;
236 }
237 //--------------------------------------------------------------------------
238 // void plpoly3( n, x, y, z, draw, ifcc )
239 //
240 // Draws a polygon in 3 space.  This differs from plline3() in that
241 // this attempts to determine if the polygon is viewable.  If the back
242 // of polygon is facing the viewer, then it isn't drawn.  If this
243 // isn't what you want, then use plline3 instead.
244 //
245 // n specifies the number of points.  They are assumed to be in a
246 // plane, and the directionality of the plane is determined from the
247 // first three points.  Additional points do not /have/ to lie on the
248 // plane defined by the first three, but if they do not, then the
249 // determiniation of visibility obviously can't be 100% accurate...
250 // So if you're 3 space polygons are too far from planar, consider
251 // breaking them into smaller polygons.  "3 points define a plane" :-).
252 //
253 // For ifcc == 1, the directionality of the polygon is determined by assuming
254 // the points are laid out in counter-clockwise order.
255 //
256 // For ifcc == 0, the directionality of the polygon is determined by assuming
257 // the points are laid out in clockwise order.
258 //
259 // BUGS:  If one of the first two segments is of zero length, or if
260 // they are colinear, the calculation of visibility has a 50/50 chance
261 // of being correct.  Avoid such situations :-).  See x18c for an
262 // example of this problem.  (Search for "20.1").
263 //--------------------------------------------------------------------------
264 
265 void
c_plpoly3(PLINT n,PLFLT_VECTOR x,PLFLT_VECTOR y,PLFLT_VECTOR z,PLBOOL_VECTOR draw,PLBOOL ifcc)266 c_plpoly3( PLINT n, PLFLT_VECTOR x, PLFLT_VECTOR y, PLFLT_VECTOR z, PLBOOL_VECTOR draw, PLBOOL ifcc )
267 {
268     int   i;
269     PLFLT vmin[3], vmax[3], zscale;
270     PLFLT u1, v1, u2, v2, u3, v3;
271     PLFLT c;
272 
273     if ( plsc->level < 3 )
274     {
275         plabort( "plpoly3: Please set up window first" );
276         return;
277     }
278 
279     if ( n < 3 )
280     {
281         plabort( "plpoly3: Must specify at least 3 points" );
282         return;
283     }
284 
285 // Now figure out which side this is.
286 
287     u1 = plP_wcpcx( plP_w3wcx( x[0], y[0], z[0] ) );
288     v1 = plP_wcpcy( plP_w3wcy( x[0], y[0], z[0] ) );
289 
290     u2 = plP_wcpcx( plP_w3wcx( x[1], y[1], z[1] ) );
291     v2 = plP_wcpcy( plP_w3wcy( x[1], y[1], z[1] ) );
292 
293     u3 = plP_wcpcx( plP_w3wcx( x[2], y[2], z[2] ) );
294     v3 = plP_wcpcy( plP_w3wcy( x[2], y[2], z[2] ) );
295 
296     c = ( u1 - u2 ) * ( v3 - v2 ) - ( v1 - v2 ) * ( u3 - u2 );
297 
298     if ( c * ( 1 - 2 * ABS( ifcc ) ) < 0. )
299         return;
300 
301     // get the bounding box in 3d
302     plP_gdom( &vmin[0], &vmax[0], &vmin[1], &vmax[1] );
303     plP_grange( &zscale, &vmin[2], &vmax[2] );
304 
305     // interate over the vertices
306     for ( i = 0; i < n - 1; i++ )
307     {
308         PLFLT p0[3], p1[3];
309         int   axis;
310 
311         // copy the end points of the segment to allow clipping
312         p0[0] = x[i]; p0[1] = y[i]; p0[2] = z[i];
313         p1[0] = x[i + 1]; p1[1] = y[i + 1]; p1[2] = z[i + 1];
314 
315         // check against each axis of the bounding box
316         for ( axis = 0; axis < 3; axis++ )
317         {
318             if ( p0[axis] < vmin[axis] ) // first out
319             {
320                 if ( p1[axis] < vmin[axis] )
321                 {
322                     break; // both endpoints out so quit
323                 }
324                 else
325                 {
326                     int   j;
327                     // interpolate to find intersection with box
328                     PLFLT t = ( vmin[axis] - p0[axis] ) / ( p1[axis] - p0[axis] );
329                     p0[axis] = vmin[axis];
330                     for ( j = 1; j < 3; j++ )
331                     {
332                         int k = ( axis + j ) % 3;
333                         p0[k] = ( 1 - t ) * p0[k] + t * p1[k];
334                     }
335                 }
336             }
337             else if ( p1[axis] < vmin[axis] ) // second out
338             {
339                 int   j;
340                 // interpolate to find intersection with box
341                 PLFLT t = ( vmin[axis] - p0[axis] ) / ( p1[axis] - p0[axis] );
342                 p1[axis] = vmin[axis];
343                 for ( j = 1; j < 3; j++ )
344                 {
345                     int k = ( axis + j ) % 3;
346                     p1[k] = ( 1 - t ) * p0[k] + t * p1[k];
347                 }
348             }
349             if ( p0[axis] > vmax[axis] ) // first out
350             {
351                 if ( p1[axis] > vmax[axis] )
352                 {
353                     break; // both out so quit
354                 }
355                 else
356                 {
357                     int   j;
358                     // interpolate to find intersection with box
359                     PLFLT t = ( vmax[axis] - p0[axis] ) / ( p1[axis] - p0[axis] );
360                     p0[axis] = vmax[axis];
361                     for ( j = 1; j < 3; j++ )
362                     {
363                         int k = ( axis + j ) % 3;
364                         p0[k] = ( 1 - t ) * p0[k] + t * p1[k];
365                     }
366                 }
367             }
368             else if ( p1[axis] > vmax[axis] ) // second out
369             {
370                 int   j;
371                 // interpolate to find intersection with box
372                 PLFLT t = ( vmax[axis] - p0[axis] ) / ( p1[axis] - p0[axis] );
373                 p1[axis] = vmax[axis];
374                 for ( j = 1; j < 3; j++ )
375                 {
376                     int k = ( axis + j ) % 3;
377                     p1[k] = ( 1 - t ) * p0[k] + t * p1[k];
378                 }
379             }
380         }
381         // if we made it to here without "break"ing out of the loop, the
382         // remaining segment is visible
383         if ( axis == 3 && draw[i] ) //  not clipped away
384         {
385             u1 = plP_wcpcx( plP_w3wcx( p0[0], p0[1], p0[2] ) );
386             v1 = plP_wcpcy( plP_w3wcy( p0[0], p0[1], p0[2] ) );
387             u2 = plP_wcpcx( plP_w3wcx( p1[0], p1[1], p1[2] ) );
388             v2 = plP_wcpcy( plP_w3wcy( p1[0], p1[1], p1[2] ) );
389             plP_movphy( (PLINT) u1, (PLINT) v1 );
390             plP_draphy( (PLINT) u2, (PLINT) v2 );
391         }
392     }
393     return;
394 }
395 
396 //--------------------------------------------------------------------------
397 // void plstyl()
398 //
399 // Set up a new line style of "nms" elements, with mark and space
400 // lengths given by arrays "mark" and "space".
401 //--------------------------------------------------------------------------
402 
403 void
c_plstyl(PLINT nms,PLINT_VECTOR mark,PLINT_VECTOR space)404 c_plstyl( PLINT nms, PLINT_VECTOR mark, PLINT_VECTOR space )
405 {
406     short int i;
407     short int flag;
408 
409     if ( plsc->level < 1 )
410     {
411         plabort( "plstyl: Please call plinit first" );
412         return;
413     }
414     if ( ( nms < 0 ) || ( nms > 10 ) )
415     {
416         plabort( "plstyl: Broken lines cannot have <0 or >10 elements" );
417         return;
418     }
419     flag = 1;
420     for ( i = 0; i < nms; i++ )
421     {
422         if ( ( mark[i] < 0 ) || ( space[i] < 0 ) )
423         {
424             plabort( "plstyl: Mark and space lengths must be > 0" );
425             return;
426         }
427         if ( ( mark[i] != 0 ) || ( space[i] != 0 ) )
428         {
429             flag = 0;
430         }
431     }
432     // Check for blank style
433     if ( ( nms > 0 ) && ( flag == 1 ) )
434     {
435         plabort( "plstyl: At least one mark or space must be > 0" );
436         return;
437     }
438 
439     plsc->nms = nms;
440     for ( i = 0; i < nms; i++ )
441     {
442         plsc->mark[i]  = mark[i];
443         plsc->space[i] = space[i];
444     }
445 
446     plsc->curel   = 0;
447     plsc->pendn   = 1;
448     plsc->timecnt = 0;
449     plsc->alarm   = nms > 0 ? mark[0] : 0;
450 }
451 
452 //--------------------------------------------------------------------------
453 // void plP_movphy()
454 //
455 // Move to physical coordinates (x,y).
456 //--------------------------------------------------------------------------
457 
458 void
plP_movphy(PLINT x,PLINT y)459 plP_movphy( PLINT x, PLINT y )
460 {
461     plsc->currx = x;
462     plsc->curry = y;
463 }
464 
465 //--------------------------------------------------------------------------
466 // void plP_draphy()
467 //
468 // Draw to physical coordinates (x,y).
469 //--------------------------------------------------------------------------
470 
471 void
plP_draphy(PLINT x,PLINT y)472 plP_draphy( PLINT x, PLINT y )
473 {
474     xline[0] = plsc->currx;
475     xline[1] = x;
476     yline[0] = plsc->curry;
477     yline[1] = y;
478 
479     pllclp( xline, yline, 2 );
480 }
481 
482 //--------------------------------------------------------------------------
483 // void plP_movwor()
484 //
485 // Move to world coordinates (x,y).
486 //--------------------------------------------------------------------------
487 
488 void
plP_movwor(PLFLT x,PLFLT y)489 plP_movwor( PLFLT x, PLFLT y )
490 {
491     PLFLT xt, yt;
492     TRANSFORM( x, y, &xt, &yt );
493 
494     plsc->currx = plP_wcpcx( xt );
495     plsc->curry = plP_wcpcy( yt );
496 }
497 
498 //--------------------------------------------------------------------------
499 // void plP_drawor()
500 //
501 // Draw to world coordinates (x,y).
502 //--------------------------------------------------------------------------
503 
504 void
plP_drawor(PLFLT x,PLFLT y)505 plP_drawor( PLFLT x, PLFLT y )
506 {
507     PLFLT xt, yt;
508     TRANSFORM( x, y, &xt, &yt );
509 
510     xline[0] = plsc->currx;
511     xline[1] = plP_wcpcx( xt );
512     yline[0] = plsc->curry;
513     yline[1] = plP_wcpcy( yt );
514 
515     pllclp( xline, yline, 2 );
516 }
517 
518 //--------------------------------------------------------------------------
519 // void plP_draphy_poly()
520 //
521 // Draw polyline in physical coordinates.
522 // Need to draw buffers in increments of (PL_MAXPOLY-1) since the
523 // last point must be repeated (for solid lines).
524 //--------------------------------------------------------------------------
525 
526 void
plP_draphy_poly(PLINT * x,PLINT * y,PLINT n)527 plP_draphy_poly( PLINT *x, PLINT *y, PLINT n )
528 {
529     PLINT i, j, ib, ilim;
530 
531     for ( ib = 0; ib < n; ib += PL_MAXPOLY - 1 )
532     {
533         ilim = MIN( PL_MAXPOLY, n - ib );
534 
535         for ( i = 0; i < ilim; i++ )
536         {
537             j        = ib + i;
538             xline[i] = x[j];
539             yline[i] = y[j];
540         }
541         pllclp( xline, yline, ilim );
542     }
543 }
544 
545 //--------------------------------------------------------------------------
546 // void plP_drawor_poly()
547 //
548 // Draw polyline in world coordinates.
549 // Need to draw buffers in increments of (PL_MAXPOLY-1) since the
550 // last point must be repeated (for solid lines).
551 //--------------------------------------------------------------------------
552 
553 void
plP_drawor_poly(PLFLT_VECTOR x,PLFLT_VECTOR y,PLINT n)554 plP_drawor_poly( PLFLT_VECTOR x, PLFLT_VECTOR y, PLINT n )
555 {
556     PLINT i, j, ib, ilim;
557     PLFLT xt, yt;
558 
559     for ( ib = 0; ib < n; ib += PL_MAXPOLY - 1 )
560     {
561         ilim = MIN( PL_MAXPOLY, n - ib );
562 
563         for ( i = 0; i < ilim; i++ )
564         {
565             j = ib + i;
566             TRANSFORM( x[j], y[j], &xt, &yt );
567             xline[i] = plP_wcpcx( xt );
568             yline[i] = plP_wcpcy( yt );
569         }
570         pllclp( xline, yline, ilim );
571     }
572 }
573 
574 //--------------------------------------------------------------------------
575 // void pllclp()
576 //
577 // Draws a polyline within the clip limits.
578 // Merely a front-end to plP_pllclp().
579 //--------------------------------------------------------------------------
580 
581 static void
pllclp(PLINT * x,PLINT * y,PLINT npts)582 pllclp( PLINT *x, PLINT *y, PLINT npts )
583 {
584     plP_pllclp( x, y, npts, plsc->clpxmi, plsc->clpxma,
585         plsc->clpymi, plsc->clpyma, genlin );
586 }
587 
588 //--------------------------------------------------------------------------
589 // void plP_pllclp()
590 //
591 // Draws a polyline within the clip limits.
592 //
593 // (AM)
594 // Wanted to change the type of xclp, yclp to avoid overflows!
595 // But that changes the type for the drawing routines too!
596 //--------------------------------------------------------------------------
597 
598 void
plP_pllclp(PLINT * x,PLINT * y,PLINT npts,PLINT xmin,PLINT xmax,PLINT ymin,PLINT ymax,void (* draw)(short *,short *,PLINT))599 plP_pllclp( PLINT *x, PLINT *y, PLINT npts,
600             PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax,
601             void ( *draw )( short *, short *, PLINT ) )
602 {
603     PLINT x1, x2, y1, y2;
604     PLINT i, iclp = 0;
605 
606     short _xclp[PL_MAXPOLY], _yclp[PL_MAXPOLY];
607     short *xclp = NULL, *yclp = NULL;
608     int   drawable;
609 
610     if ( npts < PL_MAXPOLY )
611     {
612         xclp = _xclp;
613         yclp = _yclp;
614     }
615     else
616     {
617         if ( ( ( xclp = (short *) malloc( (size_t) npts * sizeof ( short ) ) ) == NULL ) ||
618              ( ( yclp = (short *) malloc( (size_t) npts * sizeof ( short ) ) ) == NULL ) )
619         {
620             plexit( "plP_pllclp: Insufficient memory" );
621         }
622     }
623 
624     for ( i = 0; i < npts - 1; i++ )
625     {
626         x1 = x[i];
627         x2 = x[i + 1];
628         y1 = y[i];
629         y2 = y[i + 1];
630 
631         drawable = ( INSIDE( x1, y1 ) && INSIDE( x2, y2 ) );
632         if ( !drawable )
633             drawable = !plP_clipline( &x1, &y1, &x2, &y2,
634                 xmin, xmax, ymin, ymax );
635 
636         if ( drawable )
637         {
638 // First point of polyline.
639 
640             if ( iclp == 0 )
641             {
642                 xclp[iclp] = (short) x1;
643                 yclp[iclp] = (short) y1;
644                 iclp++;
645                 xclp[iclp] = (short) x2;
646                 yclp[iclp] = (short) y2;
647             }
648 
649 // Not first point.  Check if first point of this segment matches up to
650 // previous point, and if so, add it to the current polyline buffer.
651 
652             else if ( x1 == xclp[iclp] && y1 == yclp[iclp] )
653             {
654                 iclp++;
655                 xclp[iclp] = (short) x2;
656                 yclp[iclp] = (short) y2;
657             }
658 
659 // Otherwise it's time to start a new polyline
660 
661             else
662             {
663                 if ( iclp + 1 >= 2 )
664                     ( *draw )( xclp, yclp, iclp + 1 );
665                 iclp       = 0;
666                 xclp[iclp] = (short) x1;
667                 yclp[iclp] = (short) y1;
668                 iclp++;
669                 xclp[iclp] = (short) x2;
670                 yclp[iclp] = (short) y2;
671             }
672         }
673     }
674 
675 // Handle remaining polyline
676 
677     if ( iclp + 1 >= 2 )
678         ( *draw )( xclp, yclp, iclp + 1 );
679 
680     plsc->currx = x[npts - 1];
681     plsc->curry = y[npts - 1];
682 
683     if ( xclp != _xclp )
684     {
685         free( xclp );
686         free( yclp );
687     }
688 }
689 
690 //--------------------------------------------------------------------------
691 // int plP_clipline()
692 //
693 // Get clipped endpoints
694 //--------------------------------------------------------------------------
695 
696 int
plP_clipline(PLINT * p_x1,PLINT * p_y1,PLINT * p_x2,PLINT * p_y2,PLINT xmin,PLINT xmax,PLINT ymin,PLINT ymax)697 plP_clipline( PLINT *p_x1, PLINT *p_y1, PLINT *p_x2, PLINT *p_y2,
698               PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax )
699 {
700     PLINT  t, dx, dy, flipx, flipy;
701     double dydx = 0, dxdy = 0;
702 
703 // If both points are outside clip region with no hope of intersection,
704 // return with an error
705 
706     if ( ( *p_x1 <= xmin && *p_x2 <= xmin ) ||
707          ( *p_x1 >= xmax && *p_x2 >= xmax ) ||
708          ( *p_y1 <= ymin && *p_y2 <= ymin ) ||
709          ( *p_y1 >= ymax && *p_y2 >= ymax ) )
710         return 1;
711 
712 // If one of the coordinates is not finite then return with an error
713     if ( ( *p_x1 == PLINT_MIN ) || ( *p_y1 == PLINT_MIN ) ||
714          ( *p_x2 == PLINT_MIN ) || ( *p_y2 == PLINT_MIN ) )
715         return 1;
716 
717     flipx = 0;
718     flipy = 0;
719 
720     if ( *p_x2 < *p_x1 )
721     {
722         *p_x1 = 2 * xmin - *p_x1;
723         *p_x2 = 2 * xmin - *p_x2;
724         xmax  = 2 * xmin - xmax;
725         t     = xmax;
726         xmax  = xmin;
727         xmin  = t;
728         flipx = 1;
729     }
730 
731     if ( *p_y2 < *p_y1 )
732     {
733         *p_y1 = 2 * ymin - *p_y1;
734         *p_y2 = 2 * ymin - *p_y2;
735         ymax  = 2 * ymin - ymax;
736         t     = ymax;
737         ymax  = ymin;
738         ymin  = t;
739         flipy = 1;
740     }
741 
742     dx = *p_x2 - *p_x1;
743     dy = *p_y2 - *p_y1;
744 
745     if ( dx != 0 && dy != 0 )
746     {
747         dydx = (double) dy / (double) dx;
748         dxdy = 1. / dydx;
749     }
750 
751     if ( *p_x1 < xmin )
752     {
753         if ( dx != 0 && dy != 0 )
754             *p_y1 = *p_y1 + ROUND( ( xmin - *p_x1 ) * dydx );
755         *p_x1 = xmin;
756     }
757 
758     if ( *p_y1 < ymin )
759     {
760         if ( dx != 0 && dy != 0 )
761             *p_x1 = *p_x1 + ROUND( ( ymin - *p_y1 ) * dxdy );
762         *p_y1 = ymin;
763     }
764 
765     if ( *p_x1 >= xmax || *p_y1 >= ymax )
766         return 1;
767 
768     if ( *p_y2 > ymax )
769     {
770         if ( dx != 0 && dy != 0 )
771             *p_x2 = *p_x2 - ROUND( ( *p_y2 - ymax ) * dxdy );
772         *p_y2 = ymax;
773     }
774 
775     if ( *p_x2 > xmax )
776     {
777         if ( dx != 0 && dy != 0 )
778             *p_y2 = *p_y2 - ROUND( ( *p_x2 - xmax ) * dydx );
779         *p_x2 = xmax;
780     }
781 
782     if ( flipx )
783     {
784         *p_x1 = 2 * xmax - *p_x1;
785         *p_x2 = 2 * xmax - *p_x2;
786     }
787 
788     if ( flipy )
789     {
790         *p_y1 = 2 * ymax - *p_y1;
791         *p_y2 = 2 * ymax - *p_y2;
792     }
793 
794     return 0;
795 }
796 
797 //--------------------------------------------------------------------------
798 // void genlin()
799 //
800 // General line-drawing routine.  Takes line styles into account.
801 // If only 2 points are in the polyline, it is more efficient to use
802 // plP_line() rather than plP_polyline().
803 //--------------------------------------------------------------------------
804 
805 static void
genlin(short * x,short * y,PLINT npts)806 genlin( short *x, short *y, PLINT npts )
807 {
808 // Check for solid line
809 
810     if ( plsc->nms == 0 )
811     {
812         if ( npts == 2 )
813             plP_line( x, y );
814         else
815             plP_polyline( x, y, npts );
816     }
817 
818 // Right now dashed lines don't use polyline capability -- this
819 // should be improved
820 
821     else
822     {
823         PLINT i;
824 
825         // Call escape sequence to draw dashed lines, only for drivers
826         // that have this capability
827         if ( plsc->dev_dash )
828         {
829             plsc->dev_npts = npts;
830             plsc->dev_x    = x;
831             plsc->dev_y    = y;
832             plP_esc( PLESC_DASH, NULL );
833             return;
834         }
835 
836         for ( i = 0; i < npts - 1; i++ )
837         {
838             grdashline( x + i, y + i );
839         }
840     }
841 }
842 
843 //--------------------------------------------------------------------------
844 // void grdashline()
845 //
846 // Draws a dashed line to the specified point from the previous one.
847 //--------------------------------------------------------------------------
848 
849 static void
grdashline(short * x,short * y)850 grdashline( short *x, short *y )
851 {
852     PLINT  nx, ny, nxp, nyp, incr, temp;
853     PLINT  modulo, dx, dy, i, xtmp, ytmp;
854     PLINT  tstep, pix_distance, j;
855     int    loop_x;
856     short  xl[2], yl[2];
857     double nxstep, nystep;
858 
859 // Check if pattern needs to be restarted
860 
861     if ( x[0] != lastx || y[0] != lasty )
862     {
863         plsc->curel   = 0;
864         plsc->pendn   = 1;
865         plsc->timecnt = 0;
866         plsc->alarm   = plsc->mark[0];
867     }
868 
869     lastx = xtmp = x[0];
870     lasty = ytmp = y[0];
871 
872     if ( x[0] == x[1] && y[0] == y[1] )
873         return;
874 
875     nx  = x[1] - x[0];
876     dx  = ( nx > 0 ) ? 1 : -1;
877     nxp = ABS( nx );
878 
879     ny  = y[1] - y[0];
880     dy  = ( ny > 0 ) ? 1 : -1;
881     nyp = ABS( ny );
882 
883     if ( nyp > nxp )
884     {
885         modulo = nyp;
886         incr   = nxp;
887         loop_x = 0;
888     }
889     else
890     {
891         modulo = nxp;
892         incr   = nyp;
893         loop_x = 1;
894     }
895 
896     temp = modulo / 2;
897 
898 // Compute the timer step
899 
900     nxstep = nxp * plsc->umx;
901     nystep = nyp * plsc->umy;
902     tstep  = (PLINT) ( sqrt( nxstep * nxstep + nystep * nystep ) / modulo );
903     if ( tstep < 1 )
904         tstep = 1;
905 
906     // tstep is distance per pixel moved
907 
908     i = 0;
909     while ( i < modulo )
910     {
911         pix_distance = ( plsc->alarm - plsc->timecnt + tstep - 1 ) / tstep;
912         i           += pix_distance;
913         if ( i > modulo )
914             pix_distance -= ( i - modulo );
915         plsc->timecnt += pix_distance * tstep;
916 
917         temp += pix_distance * incr;
918         j     = temp / modulo;
919         temp  = temp % modulo;
920 
921         if ( loop_x )
922         {
923             xtmp += pix_distance * dx;
924             ytmp += j * dy;
925         }
926         else
927         {
928             xtmp += j * dx;
929             ytmp += pix_distance * dy;
930         }
931         if ( plsc->pendn != 0 )
932         {
933             xl[0] = (short) lastx;
934             yl[0] = (short) lasty;
935             xl[1] = (short) xtmp;
936             yl[1] = (short) ytmp;
937             plP_line( xl, yl );
938         }
939 
940 // Update line style variables when alarm goes off
941 
942         while ( plsc->timecnt >= plsc->alarm )
943         {
944             if ( plsc->pendn != 0 )
945             {
946                 plsc->pendn    = 0;
947                 plsc->timecnt -= plsc->alarm;
948                 plsc->alarm    = plsc->space[plsc->curel];
949             }
950             else
951             {
952                 plsc->pendn    = 1;
953                 plsc->timecnt -= plsc->alarm;
954                 plsc->curel++;
955                 if ( plsc->curel >= plsc->nms )
956                     plsc->curel = 0;
957                 plsc->alarm = plsc->mark[plsc->curel];
958             }
959         }
960         lastx = xtmp;
961         lasty = ytmp;
962     }
963 }
964 
965 //--------------------------------------------------------------------------
966 // interpolate_between()
967 //
968 // Returns a pointer to an array of PLFLT values which interpolate in n steps
969 // from a to b.
970 // Note:
971 // The returned array is allocated by the function and needs to be freed by
972 // the function's caller.
973 // If the return value is NULL, the allocation failed and it is up to the
974 // caller to handle the error.
975 //--------------------------------------------------------------------------
976 
interpolate_between(PLINT n,PLFLT a,PLFLT b)977 PLFLT *interpolate_between( PLINT n, PLFLT a, PLFLT b )
978 {
979     PLFLT *values;
980     PLFLT step_size;
981     int   i;
982 
983     if ( ( values = (PLFLT *) malloc( (size_t) n * sizeof ( PLFLT ) ) ) == NULL )
984     {
985         return NULL;
986     }
987 
988     step_size = ( b - a ) / (PLFLT) ( n - 1 );
989     for ( i = 0; i < n; i++ )
990     {
991         values[i] = a + step_size * (PLFLT) i;
992     }
993 
994     return values;
995 }
996