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