PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
plline.c
Go to the documentation of this file.
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 
28 
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
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
75 c_plline( PLINT n, const PLFLT *x, const PLFLT *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
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
131 c_plline3( PLINT n, const PLFLT *x, const PLFLT *y, const PLFLT *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
266 c_plpoly3( PLINT n, const PLFLT *x, const PLFLT *y, const PLFLT *z, const PLBOOL *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
404 c_plstyl( PLINT nms, const PLINT *mark, const PLINT *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
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
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
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
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
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
554 plP_drawor_poly( const PLFLT *x, const PLFLT *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
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
599 plP_pllclp( PLINT *x, PLINT *y, PLINT npts,
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
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
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
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 
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 }