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