1 //! @file
2 //!
3 //! 3d plot routines.
4 //!
5 // Copyright (C) 2004-2014 Alan W. Irwin
6 // Copyright (C) 2004 Joao Cardoso
7 // Copyright (C) 2004 Andrew Ross
8 //
9 // This file is part of PLplot.
10 //
11 // PLplot is free software; you can redistribute it and/or modify
12 // it under the terms of the GNU Library General Public License as published
13 // by the Free Software Foundation; either version 2 of the License, or
14 // (at your option) any later version.
15 //
16 // PLplot is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU Library General Public License for more details.
20 //
21 // You should have received a copy of the GNU Library General Public License
22 // along with PLplot; if not, write to the Free Software
23 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 //
25
26 #include "plplotP.h"
27
28 // Internal constants
29
30 #define BINC 50 // Block size for memory allocation
31
32 static PLINT pl3mode = 0; // 0 3d solid; 1 mesh plot
33 static PLINT pl3upv = 1; // 1 update view; 0 no update
34
35 static PLINT zbflg = 0, zbcol;
36 static PLFLT zbtck, zbwidth;
37
38 static PLINT *oldhiview = NULL;
39 static PLINT *oldloview = NULL;
40 static PLINT *newhiview = NULL;
41 static PLINT *newloview = NULL;
42 static PLINT *utmp = NULL;
43 static PLINT *vtmp = NULL;
44 static PLFLT *ctmp = NULL;
45
46 static PLINT mhi, xxhi, newhisize;
47 static PLINT mlo, xxlo, newlosize;
48
49 // Light source for shading
50 static PLFLT xlight, ylight, zlight;
51 static PLINT falsecolor = 0;
52 static PLFLT fc_minz, fc_maxz;
53
54 // Prototypes for static functions
55
56 static void plgrid3( PLFLT );
57 static void plnxtv( PLINT *, PLINT *, PLFLT*, PLINT, PLINT );
58 static void
59 plside3( PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp, PLINT nx, PLINT ny, PLINT opt );
60 static void
61 plt3zz( PLINT x0, PLINT y0, PLINT dx, PLINT dy, PLINT flag, PLINT *init,
62 PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp, PLINT nx, PLINT ny,
63 PLINT *u, PLINT *v, PLFLT* c );
64 static void plnxtvhi( PLINT *, PLINT *, PLFLT*, PLINT, PLINT );
65 static void plnxtvlo( PLINT *, PLINT *, PLFLT*, PLINT, PLINT );
66 static void plnxtvhi_draw( PLINT *u, PLINT *v, PLFLT* c, PLINT n );
67
68 static void savehipoint( PLINT, PLINT );
69 static void savelopoint( PLINT, PLINT );
70 static void swaphiview( void );
71 static void swaploview( void );
72 static void myexit( PLCHAR_VECTOR );
73 static void myabort( PLCHAR_VECTOR );
74 static void freework( void );
75 static int plabv( PLINT, PLINT, PLINT, PLINT, PLINT, PLINT );
76 static void pl3cut( PLINT, PLINT, PLINT, PLINT, PLINT,
77 PLINT, PLINT, PLINT, PLINT *, PLINT * );
78 static PLFLT plGetAngleToLight( PLFLT* x, PLFLT* y, PLFLT* z );
79 static void plP_draw3d( PLINT x, PLINT y, PLFLT *c, PLINT j, PLINT move );
80 //static void plxyindexlimits( PLINT instart, PLINT inn,
81 // PLINT *inarray_min, PLINT *inarray_max,
82 // PLINT *outstart, PLINT *outn, PLINT outnmax,
83 // PLINT *outarray_min, PLINT *outarray_max );
84
85
86 // #define MJL_HACK 1
87 #if MJL_HACK
88 static void plP_fill3( PLINT x0, PLINT y0, PLINT x1, PLINT y1,
89 PLINT x2, PLINT y2, PLINT j );
90 static void plP_fill4( PLINT x0, PLINT y0, PLINT x1, PLINT y1,
91 PLINT x2, PLINT y2, PLINT x3, PLINT y3, PLINT j );
92 #endif
93
94 //--------------------------------------------------------------------------
95 // void plsetlightsource(x, y, z)
96 //
97 // Sets the position of the light source.
98 //--------------------------------------------------------------------------
99
100 void
c_pllightsource(PLFLT x,PLFLT y,PLFLT z)101 c_pllightsource( PLFLT x, PLFLT y, PLFLT z )
102 {
103 xlight = x;
104 ylight = y;
105 zlight = z;
106 }
107
108 //--------------------------------------------------------------------------
109 // void plmesh(x, y, z, nx, ny, opt)
110 //
111 // Plots a mesh representation of the function z[x][y]. The x values
112 // are stored as x[0..nx-1], the y values as y[0..ny-1], and the
113 // z values are in the 2-d array z[][]. The integer "opt" specifies:
114 // see plmeshc() below.
115 //--------------------------------------------------------------------------
116
117 void
c_plmesh(PLFLT_VECTOR x,PLFLT_VECTOR y,PLFLT_MATRIX z,PLINT nx,PLINT ny,PLINT opt)118 c_plmesh( PLFLT_VECTOR x, PLFLT_VECTOR y, PLFLT_MATRIX z, PLINT nx, PLINT ny, PLINT opt )
119 {
120 plfplot3dc( x, y, plf2ops_c(), (PLPointer) z, nx, ny, opt | MESH, NULL, 0 );
121 }
122
123 void
plfmesh(PLFLT_VECTOR x,PLFLT_VECTOR y,PLF2OPS zops,PLPointer zp,PLINT nx,PLINT ny,PLINT opt)124 plfmesh( PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp,
125 PLINT nx, PLINT ny, PLINT opt )
126 {
127 plfplot3dc( x, y, zops, zp, nx, ny, opt | MESH, NULL, 0 );
128 }
129
130 //--------------------------------------------------------------------------
131 // void plmeshc(x, y, z, nx, ny, opt, clevel, nlevel)
132 //
133 // Plots a mesh representation of the function z[x][y]. The x values
134 // are stored as x[0..nx-1], the y values as y[0..ny-1], and the
135 // z values are in the 2-d array z[][]. The integer "opt" specifies:
136 //
137 // DRAW_LINEX draw lines parallel to the X axis
138 // DRAW_LINEY draw lines parallel to the Y axis
139 // DRAW_LINEXY draw lines parallel to both the X and Y axis
140 // MAG_COLOR draw the mesh with a color dependent of the magnitude
141 // BASE_CONT draw contour plot at bottom xy plane
142 // TOP_CONT draw contour plot at top xy plane (not yet)
143 // DRAW_SIDES draw sides
144 //
145 // or any bitwise combination, e.g. "MAG_COLOR | DRAW_LINEX"
146 //
147 //--------------------------------------------------------------------------
148
149 void
c_plmeshc(PLFLT_VECTOR x,PLFLT_VECTOR y,PLFLT_MATRIX z,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel)150 c_plmeshc( PLFLT_VECTOR x, PLFLT_VECTOR y, PLFLT_MATRIX z, PLINT nx, PLINT ny, PLINT opt,
151 PLFLT_VECTOR clevel, PLINT nlevel )
152 {
153 plfplot3dc( x, y, plf2ops_c(), (PLPointer) z, nx, ny, opt | MESH, clevel, nlevel );
154 }
155
156 void
plfmeshc(PLFLT_VECTOR x,PLFLT_VECTOR y,PLF2OPS zops,PLPointer zp,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel)157 plfmeshc( PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp,
158 PLINT nx, PLINT ny, PLINT opt, PLFLT_VECTOR clevel, PLINT nlevel )
159 {
160 plfplot3dc( x, y, zops, zp, nx, ny, opt | MESH, clevel, nlevel );
161 }
162
163 // clipping helper for 3d polygons
164
165 int
plP_clip_poly(int Ni,PLFLT * Vi[3],int axis,PLFLT dir,PLFLT offset)166 plP_clip_poly( int Ni, PLFLT *Vi[3], int axis, PLFLT dir, PLFLT offset )
167 {
168 int anyout = 0;
169 PLFLT _in[PL_MAXPOLY], _T[3][PL_MAXPOLY];
170 PLFLT *in, *T[3], *TT = NULL;
171 int No = 0;
172 int i, j, k;
173
174 if ( Ni > PL_MAXPOLY )
175 {
176 in = (PLFLT *) malloc( sizeof ( PLFLT ) * (size_t) Ni );
177 TT = (PLFLT *) malloc( 3 * sizeof ( PLFLT ) * (size_t) Ni );
178
179 if ( in == NULL || TT == NULL )
180 {
181 plexit( "plP_clip_poly: insufficient memory for large polygon" );
182 }
183
184 T[0] = &TT[0];
185 T[1] = &TT[Ni];
186 T[2] = &TT[2 * Ni];
187 }
188 else
189 {
190 in = _in;
191 T[0] = &_T[0][0];
192 T[1] = &_T[1][0];
193 T[2] = &_T[2][0];
194 }
195
196 for ( i = 0; i < Ni; i++ )
197 {
198 in[i] = Vi[axis][i] * dir + offset;
199 anyout += in[i] < 0;
200 }
201
202 // none out
203 if ( anyout == 0 )
204 return Ni;
205
206 // all out
207 if ( anyout == Ni )
208 {
209 return 0;
210 }
211
212 // some out
213 // copy over to a temp vector
214 for ( i = 0; i < 3; i++ )
215 {
216 for ( j = 0; j < Ni; j++ )
217 {
218 T[i][j] = Vi[i][j];
219 }
220 }
221 // copy back selectively
222 for ( i = 0; i < Ni; i++ )
223 {
224 j = ( i + 1 ) % Ni;
225
226 if ( in[i] >= 0 && in[j] >= 0 )
227 {
228 for ( k = 0; k < 3; k++ )
229 Vi[k][No] = T[k][j];
230 No++;
231 }
232 else if ( in[i] >= 0 && in[j] < 0 )
233 {
234 PLFLT u = in[i] / ( in[i] - in[j] );
235 for ( k = 0; k < 3; k++ )
236 Vi[k][No] = T[k][i] * ( 1 - u ) + T[k][j] * u;
237 No++;
238 }
239 else if ( in[i] < 0 && in[j] >= 0 )
240 {
241 PLFLT u = in[i] / ( in[i] - in[j] );
242 for ( k = 0; k < 3; k++ )
243 Vi[k][No] = T[k][i] * ( 1 - u ) + T[k][j] * u;
244 No++;
245 for ( k = 0; k < 3; k++ )
246 Vi[k][No] = T[k][j];
247 No++;
248 }
249 }
250
251 if ( Ni > PL_MAXPOLY )
252 {
253 free( in );
254 free( TT );
255 }
256
257 return No;
258 }
259
260 // helper for plsurf3d, similar to c_plfill3()
261 static void
shade_triangle(PLFLT x0,PLFLT y0,PLFLT z0,PLFLT x1,PLFLT y1,PLFLT z1,PLFLT x2,PLFLT y2,PLFLT z2)262 shade_triangle( PLFLT x0, PLFLT y0, PLFLT z0,
263 PLFLT x1, PLFLT y1, PLFLT z1,
264 PLFLT x2, PLFLT y2, PLFLT z2 )
265 {
266 int i;
267 // arrays for interface to core functions
268 short u[6], v[6];
269 PLFLT x[6], y[6], z[6];
270 int n;
271 PLFLT xmin, xmax, ymin, ymax, zmin, zmax, zscale;
272 PLFLT *V[3];
273
274 plP_gdom( &xmin, &xmax, &ymin, &ymax );
275 plP_grange( &zscale, &zmin, &zmax );
276
277 x[0] = x0; x[1] = x1; x[2] = x2;
278 y[0] = y0; y[1] = y1; y[2] = y2;
279 z[0] = z0; z[1] = z1; z[2] = z2;
280 n = 3;
281
282 V[0] = x; V[1] = y; V[2] = z;
283
284 n = plP_clip_poly( n, V, 0, 1, -xmin );
285 n = plP_clip_poly( n, V, 0, -1, xmax );
286 n = plP_clip_poly( n, V, 1, 1, -ymin );
287 n = plP_clip_poly( n, V, 1, -1, ymax );
288 n = plP_clip_poly( n, V, 2, 1, -zmin );
289 n = plP_clip_poly( n, V, 2, -1, zmax );
290
291 if ( n > 0 )
292 {
293 if ( falsecolor )
294 plcol1( ( ( z[0] + z[1] + z[2] ) / 3. - fc_minz ) / ( fc_maxz - fc_minz ) );
295 else
296 plcol1( plGetAngleToLight( x, y, z ) );
297
298 for ( i = 0; i < n; i++ )
299 {
300 u[i] = (short) plP_wcpcx( plP_w3wcx( x[i], y[i], z[i] ) );
301 v[i] = (short) plP_wcpcy( plP_w3wcy( x[i], y[i], z[i] ) );
302 }
303 u[n] = u[0];
304 v[n] = v[0];
305
306 #ifdef SHADE_DEBUG // show triangles
307 plcol0( 1 );
308 x[3] = x[0]; y[3] = y[0]; z[3] = z[0];
309 plline3( 4, x, y, z );
310 #else // fill triangles
311 plP_fill( u, v, n + 1 );
312 #endif
313 }
314 }
315
316 //--------------------------------------------------------------------------
317 // void plsurf3d(x, y, z, nx, ny, opt, clevel, nlevel)
318 //
319 // Plots the 3-d surface representation of the function z[x][y].
320 // The x values are stored as x[0..nx-1], the y values as y[0..ny-1],
321 // and the z values are in the 2-d array z[][]. The integer "opt" specifies:
322 // see plsurf3dl() below.
323 //--------------------------------------------------------------------------
324
325 void
c_plsurf3d(PLFLT_VECTOR x,PLFLT_VECTOR y,PLFLT_MATRIX z,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel)326 c_plsurf3d( PLFLT_VECTOR x, PLFLT_VECTOR y, PLFLT_MATRIX z, PLINT nx, PLINT ny,
327 PLINT opt, PLFLT_VECTOR clevel, PLINT nlevel )
328 {
329 plfsurf3d( x, y, plf2ops_c(), (PLPointer) z, nx, ny,
330 opt, clevel, nlevel );
331 }
332
333 void
plfsurf3d(PLFLT_VECTOR x,PLFLT_VECTOR y,PLF2OPS zops,PLPointer zp,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel)334 plfsurf3d( PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp,
335 PLINT nx, PLINT ny, PLINT opt, PLFLT_VECTOR clevel, PLINT nlevel )
336 {
337 PLINT i;
338 PLINT *indexymin = (PLINT *) malloc( (size_t) nx * sizeof ( PLINT ) );
339 PLINT *indexymax = (PLINT *) malloc( (size_t) nx * sizeof ( PLINT ) );
340
341 if ( !indexymin || !indexymax )
342 plexit( "plsurf3d: Out of memory." );
343 for ( i = 0; i < nx; i++ )
344 {
345 indexymin[i] = 0;
346 indexymax[i] = ny;
347 }
348 plfsurf3dl( x, y, zops, zp, nx, ny, opt, clevel, nlevel,
349 0, nx, indexymin, indexymax );
350 free_mem( indexymin );
351 free_mem( indexymax );
352 }
353
354 //--------------------------------------------------------------------------
355 // void plsurf3dl(x, y, z, nx, ny, opt, clevel, nlevel,
356 // indexxmin, indexxmax, indexymin, indexymax)
357 //
358 // Plots the 3-d surface representation of the function z[x][y].
359 // The x values are stored as x[0..nx-1], the y values as y[0..ny-1],
360 // and the z values are in the 2-d array z[][].
361 //
362 //
363 // BASE_CONT draw contour plot at bottom xy plane
364 // TOP_CONT draw contour plot at top xy plane (not implemented)
365 // SURF_CONT draw contour at surface
366 // FACETED each square that makes up the surface is faceted
367 // DRAW_SIDES draw sides
368 // MAG_COLOR the surface is colored according to the value of z;
369 // if not defined, the surface is colored according to the
370 // intensity of the reflected light in the surface from a light
371 // source whose position is set using c_pllightsource()
372 //
373 // or any bitwise combination, e.g. "MAG_COLOR | SURF_CONT | BASE_CONT"
374 //
375 // indexymin and indexymax are arrays which specify the y index range
376 // (following the convention that the upper range limit is one more than
377 // actual index limit) for an x index range of indexxmin, indexxmax.
378 // This code is a complete departure from the approach taken in the old version
379 // of this routine. Formerly to code attempted to use the logic for the hidden
380 // line algorithm to draw the hidden surface. This was really hard. This code
381 // below uses a simple back to front (painters) algorithm. All the
382 // triangles are drawn.
383 //
384 // There are multitude of ways this code could be optimized. Given the
385 // problems with the old code, I tried to focus on clarity here.
386 //--------------------------------------------------------------------------
387
388 void
c_plsurf3dl(PLFLT_VECTOR x,PLFLT_VECTOR y,PLFLT_MATRIX z,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel,PLINT indexxmin,PLINT indexxmax,PLINT_VECTOR indexymin,PLINT_VECTOR indexymax)389 c_plsurf3dl( PLFLT_VECTOR x, PLFLT_VECTOR y, PLFLT_MATRIX z, PLINT nx, PLINT ny,
390 PLINT opt, PLFLT_VECTOR clevel, PLINT nlevel,
391 PLINT indexxmin, PLINT indexxmax, PLINT_VECTOR indexymin, PLINT_VECTOR indexymax )
392 {
393 plfsurf3dl( x, y, plf2ops_c(), (PLPointer) z, nx, ny,
394 opt, clevel, nlevel, indexxmin, indexxmax, indexymin, indexymax );
395 }
396
397 void
plfsurf3dl(PLFLT_VECTOR x,PLFLT_VECTOR y,PLF2OPS zops,PLPointer zp,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel,PLINT indexxmin,PLINT indexxmax,PLINT_VECTOR indexymin,PLINT_VECTOR indexymax)398 plfsurf3dl( PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp, PLINT nx, PLINT ny,
399 PLINT opt, PLFLT_VECTOR clevel, PLINT nlevel,
400 PLINT indexxmin, PLINT indexxmax, PLINT_VECTOR indexymin, PLINT_VECTOR indexymax )
401 {
402 PLFLT cxx, cxy, cyx, cyy, cyz;
403 PLINT i, j, k;
404 PLINT ixDir, ixOrigin, iyDir, iyOrigin, nFast, nSlow;
405 PLINT ixFast, ixSlow, iyFast, iySlow;
406 PLINT iFast, iSlow;
407 PLFLT xmin, xmax, ymin, ymax, zmin, zmax, zscale;
408 PLFLT xm, ym, zm;
409 PLINT ixmin = 0, ixmax = nx, iymin = 0, iymax = ny;
410 PLFLT xx[3], yy[3], zz[3];
411 PLFLT px[4], py[4], pz[4];
412 CONT_LEVEL *cont, *clev;
413 CONT_LINE *cline;
414 int ct, ix, iy, iftriangle;
415 PLINT color = plsc->icol0;
416 PLFLT width = plsc->width;
417 PLFLT ( *getz )( PLPointer, PLINT, PLINT ) = zops->get;
418
419 if ( plsc->level < 3 )
420 {
421 myabort( "plsurf3dl: Please set up window first" );
422 return;
423 }
424
425 if ( nx <= 0 || ny <= 0 )
426 {
427 myabort( "plsurf3dl: Bad array dimensions." );
428 return;
429 }
430
431 //
432 // Don't use the data z value to scale the color, use the z axis
433 // values set by plw3d()
434 //
435 // plMinMax2dGrid(z, nx, ny, &fc_maxz, &fc_minz);
436 //
437
438 fc_minz = plsc->ranmi;
439 fc_maxz = plsc->ranma;
440 if ( fc_maxz == fc_minz )
441 {
442 plwarn( "plsurf3dl: Maximum and minimum Z values are equal! \"fixing\"..." );
443 fc_maxz = fc_minz + 1e-6;
444 }
445
446 if ( opt & MAG_COLOR )
447 falsecolor = 1;
448 else
449 falsecolor = 0;
450
451 plP_gdom( &xmin, &xmax, &ymin, &ymax );
452 plP_grange( &zscale, &zmin, &zmax );
453 if ( zmin > zmax )
454 {
455 PLFLT t = zmin;
456 zmin = zmax;
457 zmax = t;
458 }
459
460 // Check that points in x and in y are strictly increasing and in range
461
462 for ( i = 0; i < nx - 1; i++ )
463 {
464 if ( x[i] >= x[i + 1] )
465 {
466 myabort( "plsurf3dl: X array must be strictly increasing" );
467 return;
468 }
469 if ( x[i] < xmin && x[i + 1] >= xmin )
470 ixmin = i;
471 if ( x[i + 1] > xmax && x[i] <= xmax )
472 ixmax = i + 2;
473 }
474 for ( i = 0; i < ny - 1; i++ )
475 {
476 if ( y[i] >= y[i + 1] )
477 {
478 myabort( "plsurf3dl: Y array must be strictly increasing" );
479 return;
480 }
481 if ( y[i] < ymin && y[i + 1] >= ymin )
482 iymin = i;
483 if ( y[i + 1] > ymax && y[i] <= ymax )
484 iymax = i + 2;
485 }
486
487 // get the viewing parameters
488 plP_gw3wc( &cxx, &cxy, &cyx, &cyy, &cyz );
489
490 // we're going to draw from back to front
491
492 // iFast will index the dominant (fastest changing) dimension
493 // iSlow will index the slower changing dimension
494 //
495 // iX indexes the X dimension
496 // iY indexes the Y dimension
497
498 // get direction for X
499 if ( cxy >= 0 )
500 {
501 ixDir = 1; // direction in X
502 ixOrigin = ixmin; // starting point
503 }
504 else
505 {
506 ixDir = -1;
507 ixOrigin = ixmax - 1;
508 }
509 // get direction for Y
510 if ( cxx >= 0 )
511 {
512 iyDir = -1;
513 iyOrigin = iymax - 1;
514 }
515 else
516 {
517 iyDir = 1;
518 iyOrigin = iymin;
519 }
520 // figure out which dimension is dominant
521 if ( fabs( cxx ) > fabs( cxy ) )
522 {
523 // X is dominant
524 nFast = ixmax - ixmin; // samples in the Fast direction
525 nSlow = iymax - iymin; // samples in the Slow direction
526
527 ixFast = ixDir; ixSlow = 0;
528 iyFast = 0; iySlow = iyDir;
529 }
530 else
531 {
532 nFast = iymax - iymin;
533 nSlow = ixmax - ixmin;
534
535 ixFast = 0; ixSlow = ixDir;
536 iyFast = iyDir; iySlow = 0;
537 }
538
539 // we've got to draw the background grid first, hidden line code has to draw it last
540 if ( zbflg )
541 {
542 PLFLT bx[3], by[3], bz[3];
543 PLFLT tick = zbtck, tp;
544 PLINT nsub = 0;
545
546 // get the tick spacing
547 pldtik( zmin, zmax, &tick, &nsub, FALSE );
548
549 // determine the vertices for the background grid line
550 bx[0] = ( ixOrigin != ixmin && ixFast == 0 ) || ixFast > 0 ? xmax : xmin;
551 by[0] = ( iyOrigin != iymin && iyFast == 0 ) || iyFast > 0 ? ymax : ymin;
552 bx[1] = ixOrigin != ixmin ? xmax : xmin;
553 by[1] = iyOrigin != iymin ? ymax : ymin;
554 bx[2] = ( ixOrigin != ixmin && ixSlow == 0 ) || ixSlow > 0 ? xmax : xmin;
555 by[2] = ( iyOrigin != iymin && iySlow == 0 ) || iySlow > 0 ? ymax : ymin;
556
557 plwidth( zbwidth );
558 plcol0( zbcol );
559 for ( tp = tick * floor( zmin / tick ) + tick; tp <= zmax; tp += tick )
560 {
561 bz[0] = bz[1] = bz[2] = tp;
562 plline3( 3, bx, by, bz );
563 }
564 // draw the vertical line at the back corner
565 bx[0] = bx[1];
566 by[0] = by[1];
567 bz[0] = zmin;
568 plline3( 2, bx, by, bz );
569 plwidth( width );
570 plcol0( color );
571 }
572
573 // If enabled, draw the contour at the base
574
575 // The contour plotted at the base will be identical to the one obtained
576 // with c_plcont(). The contour plotted at the surface is simple minded, but
577 // can be improved by using the contour data available.
578 //
579
580 if ( clevel != NULL && opt & BASE_CONT )
581 {
582 #define NPTS 100
583 int np = NPTS;
584 PLFLT **zstore;
585 PLcGrid2 cgrid2;
586 PLFLT *zzloc = (PLFLT *) malloc( (size_t) NPTS * sizeof ( PLFLT ) );
587 if ( zzloc == NULL )
588 plexit( "plsurf3dl: Insufficient memory" );
589
590 // get the contour lines
591
592 // prepare cont_store input
593 cgrid2.nx = nx;
594 cgrid2.ny = ny;
595 plAlloc2dGrid( &cgrid2.xg, nx, ny );
596 plAlloc2dGrid( &cgrid2.yg, nx, ny );
597 plAlloc2dGrid( &zstore, nx, ny );
598
599 for ( i = indexxmin; i < indexxmax; i++ )
600 {
601 for ( j = 0; j < indexymin[i]; j++ )
602 {
603 cgrid2.xg[i][j] = x[i];
604 cgrid2.yg[i][j] = y[indexymin[i]];
605 zstore[i][j] = getz( zp, i, indexymin[i] );
606 }
607 for ( j = indexymin[i]; j < indexymax[i]; j++ )
608 {
609 cgrid2.xg[i][j] = x[i];
610 cgrid2.yg[i][j] = y[j];
611 zstore[i][j] = getz( zp, i, j );
612 }
613 for ( j = indexymax[i]; j < ny; j++ )
614 {
615 cgrid2.xg[i][j] = x[i];
616 cgrid2.yg[i][j] = y[indexymax[i] - 1];
617 zstore[i][j] = getz( zp, i, indexymax[i] - 1 );
618 }
619 }
620 // Fill cont structure with contours.
621 cont_store( (PLFLT_MATRIX) zstore, nx, ny, indexxmin + 1, indexxmax, 1, ny,
622 clevel, nlevel, pltr2, (void *) &cgrid2, &cont );
623
624 // Free the 2D input arrays to cont_store since not needed any more.
625 plFree2dGrid( zstore, nx, ny );
626 plFree2dGrid( cgrid2.xg, nx, ny );
627 plFree2dGrid( cgrid2.yg, nx, ny );
628
629 // follow the contour levels and lines
630 clev = cont;
631 do // for each contour level
632 {
633 cline = clev->line;
634 do // there are several lines that make up the contour
635 {
636 if ( cline->npts > np )
637 {
638 np = cline->npts;
639 if ( ( zzloc = (PLFLT *) realloc( zzloc, (size_t) np * sizeof ( PLFLT ) ) ) == NULL )
640 {
641 plexit( "plsurf3dl: Insufficient memory" );
642 }
643 }
644 for ( j = 0; j < cline->npts; j++ )
645 zzloc[j] = plsc->ranmi;
646 if ( cline->npts > 0 )
647 {
648 plcol1( ( clev->level - fc_minz ) / ( fc_maxz - fc_minz ) );
649 plline3( cline->npts, cline->x, cline->y, zzloc );
650 }
651 cline = cline->next;
652 }
653 while ( cline != NULL );
654 clev = clev->next;
655 }
656 while ( clev != NULL );
657
658 cont_clean_store( cont ); // now release the memory
659 free( zzloc );
660 }
661
662 // Now we can iterate over the grid drawing the quads
663 for ( iSlow = 0; iSlow < nSlow - 1; iSlow++ )
664 {
665 for ( iFast = 0; iFast < nFast - 1; iFast++ )
666 {
667 // get the 4 corners of the Quad, which are
668 //
669 // 0--2
670 // | |
671 // 1--3
672 //
673
674 xm = ym = zm = 0.;
675
676 iftriangle = 1;
677 for ( i = 0; i < 2; i++ )
678 {
679 for ( j = 0; j < 2; j++ )
680 {
681 // we're transforming from Fast/Slow coordinates to x/y coordinates
682 // note, these are the indices, not the values
683 ix = ixFast * ( iFast + i ) + ixSlow * ( iSlow + j ) + ixOrigin;
684 iy = iyFast * ( iFast + i ) + iySlow * ( iSlow + j ) + iyOrigin;
685
686 if ( indexxmin <= ix && ix < indexxmax &&
687 indexymin[ix] <= iy && iy < indexymax[ix] )
688 {
689 xm += px[2 * i + j] = x[ix];
690 ym += py[2 * i + j] = y[iy];
691 zm += pz[2 * i + j] = getz( zp, ix, iy );
692 }
693 else
694 {
695 iftriangle = 0;
696 break;
697 }
698 }
699 if ( iftriangle == 0 )
700 break;
701 }
702
703 if ( iftriangle == 0 )
704 continue;
705 // the "mean point" of the quad, common to all four triangles
706 // -- perhaps not a good thing to do for the light shading
707
708 xm /= 4.; ym /= 4.; zm /= 4.;
709
710 // now draw the quad as four triangles
711
712 for ( i = 1; i < 3; i++ )
713 {
714 for ( j = 0; j < 4; j += 3 )
715 {
716 shade_triangle( px[j], py[j], pz[j], xm, ym, zm, px[i], py[i], pz[i] );
717 }
718 }
719
720 // After shading completed for a quad, render surface contours.
721 if ( clevel != NULL && ( opt & SURF_CONT ) )
722 {
723 for ( i = 1; i < 3; i++ )
724 {
725 for ( j = 0; j < 4; j += 3 )
726 #define min3( a, b, c ) ( MIN( ( MIN( a, b ) ), c ) )
727 #define max3( a, b, c ) ( MAX( ( MAX( a, b ) ), c ) )
728
729 {
730 for ( k = 0; k < nlevel; k++ )
731 {
732 if ( clevel[k] >= min3( pz[i], zm, pz[j] ) && clevel[k] < max3( pz[i], zm, pz[j] ) )
733 {
734 ct = 0;
735 if ( clevel[k] >= MIN( pz[i], zm ) && clevel[k] < MAX( pz[i], zm ) ) // p0-pm
736 {
737 xx[ct] = ( ( clevel[k] - pz[i] ) * ( xm - px[i] ) ) / ( zm - pz[i] ) + px[i];
738 yy[ct] = ( ( clevel[k] - pz[i] ) * ( ym - py[i] ) ) / ( zm - pz[i] ) + py[i];
739 ct++;
740 }
741
742 if ( clevel[k] >= MIN( pz[i], pz[j] ) && clevel[k] < MAX( pz[i], pz[j] ) ) // p0-p1
743 {
744 xx[ct] = ( ( clevel[k] - pz[i] ) * ( px[j] - px[i] ) ) / ( pz[j] - pz[i] ) + px[i];
745 yy[ct] = ( ( clevel[k] - pz[i] ) * ( py[j] - py[i] ) ) / ( pz[j] - pz[i] ) + py[i];
746 ct++;
747 }
748
749 if ( clevel[k] >= MIN( pz[j], zm ) && clevel[k] < MAX( pz[j], zm ) ) // p1-pm
750 {
751 xx[ct] = ( ( clevel[k] - pz[j] ) * ( xm - px[j] ) ) / ( zm - pz[j] ) + px[j];
752 yy[ct] = ( ( clevel[k] - pz[j] ) * ( ym - py[j] ) ) / ( zm - pz[j] ) + py[j];
753 ct++;
754 }
755
756 if ( ct == 2 )
757 {
758 // yes, xx and yy are the intersection points of the triangle with
759 // the contour line -- draw a straight line betweeen the points
760 // -- at the end this will make up the contour line
761
762 // surface contour with color set by user
763 plcol0( color );
764 zz[0] = zz[1] = clevel[k];
765 plline3( 2, xx, yy, zz );
766
767 // don't break; one triangle can span various contour levels
768 }
769 else
770 plwarn( "plsurf3dl: ***ERROR***\n" );
771 }
772 }
773 }
774 }
775 }
776 }
777 }
778
779 if ( opt & FACETED )
780 {
781 plcol0( 0 );
782 plfplot3dcl( x, y, zops, zp, nx, ny, MESH | DRAW_LINEXY, NULL, 0,
783 indexxmin, indexxmax, indexymin, indexymax );
784 }
785
786 if ( opt & DRAW_SIDES ) // the sides look ugly !!!
787 { // draw one more row with all the Z's set to zmin
788 plP_grange( &zscale, &zmin, &zmax );
789
790 iSlow = nSlow - 1;
791 iftriangle = 1;
792 for ( iFast = 0; iFast < nFast - 1; iFast++ )
793 {
794 for ( i = 0; i < 2; i++ )
795 {
796 ix = ixFast * ( iFast + i ) + ixSlow * iSlow + ixOrigin;
797 iy = iyFast * ( iFast + i ) + iySlow * iSlow + iyOrigin;
798 if ( indexxmin <= ix && ix < indexxmax &&
799 indexymin[ix] <= iy && iy < indexymax[ix] )
800 {
801 px[2 * i] = x[ix];
802 py[2 * i] = y[iy];
803 pz[2 * i] = getz( zp, ix, iy );
804 }
805 else
806 {
807 iftriangle = 0;
808 break;
809 }
810 }
811 if ( iftriangle == 0 )
812 break;
813 // now draw the quad as two triangles (4 might be better)
814
815 shade_triangle( px[0], py[0], pz[0], px[2], py[2], pz[2], px[0], py[0], zmin );
816 shade_triangle( px[2], py[2], pz[2], px[2], py[2], zmin, px[0], py[0], zmin );
817 }
818
819 iFast = nFast - 1;
820 iftriangle = 1;
821 for ( iSlow = 0; iSlow < nSlow - 1; iSlow++ )
822 {
823 for ( i = 0; i < 2; i++ )
824 {
825 ix = ixFast * iFast + ixSlow * ( iSlow + i ) + ixOrigin;
826 iy = iyFast * iFast + iySlow * ( iSlow + i ) + iyOrigin;
827 if ( indexxmin <= ix && ix < indexxmax &&
828 indexymin[ix] <= iy && iy < indexymax[ix] )
829 {
830 px[2 * i] = x[ix];
831 py[2 * i] = y[iy];
832 pz[2 * i] = getz( zp, ix, iy );
833 }
834 else
835 {
836 iftriangle = 0;
837 break;
838 }
839 }
840 if ( iftriangle == 0 )
841 break;
842
843 // now draw the quad as two triangles (4 might be better)
844 shade_triangle( px[0], py[0], pz[0], px[2], py[2], pz[2], px[0], py[0], zmin );
845 shade_triangle( px[2], py[2], pz[2], px[2], py[2], zmin, px[0], py[0], zmin );
846 }
847 }
848 }
849
850 //--------------------------------------------------------------------------
851 // void plot3d(x, y, z, nx, ny, opt, side)
852 //
853 // Plots a 3-d representation of the function z[x][y]. The x values
854 // are stored as x[0..nx-1], the y values as y[0..ny-1], and the z
855 // values are in the 2-d array z[][]. The integer "opt" specifies:
856 // see plot3dcl() below
857 //--------------------------------------------------------------------------
858
859 void
c_plot3d(PLFLT_VECTOR x,PLFLT_VECTOR y,PLFLT_MATRIX z,PLINT nx,PLINT ny,PLINT opt,PLBOOL side)860 c_plot3d( PLFLT_VECTOR x, PLFLT_VECTOR y, PLFLT_MATRIX z,
861 PLINT nx, PLINT ny, PLINT opt, PLBOOL side )
862 {
863 plfplot3dc( x, y, plf2ops_c(), (PLPointer) z, nx, ny, opt | ( side != 0 ? DRAW_SIDES : 0 ), NULL, 0 );
864 }
865
866 void
plfplot3d(PLFLT_VECTOR x,PLFLT_VECTOR y,PLF2OPS zops,PLPointer zp,PLINT nx,PLINT ny,PLINT opt,PLBOOL side)867 plfplot3d( PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp,
868 PLINT nx, PLINT ny, PLINT opt, PLBOOL side )
869 {
870 plfplot3dc( x, y, zops, zp, nx, ny, opt | ( side != 0 ? DRAW_SIDES : 0 ), NULL, 0 );
871 }
872
873 //--------------------------------------------------------------------------
874 // void plot3dc(x, y, z, nx, ny, opt, clevel, nlevel)
875 //
876 // Plots a 3-d representation of the function z[x][y]. The x values
877 // are stored as x[0..nx-1], the y values as y[0..ny-1], and the z
878 // values are in the 2-d array z[][]. The integer "opt" specifies:
879 // see plot3dcl() below
880 //--------------------------------------------------------------------------
881
882 void
c_plot3dc(PLFLT_VECTOR x,PLFLT_VECTOR y,PLFLT_MATRIX z,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel)883 c_plot3dc( PLFLT_VECTOR x, PLFLT_VECTOR y, PLFLT_MATRIX z,
884 PLINT nx, PLINT ny, PLINT opt,
885 PLFLT_VECTOR clevel, PLINT nlevel )
886 {
887 plfplot3dcl( x, y, plf2ops_c(), (PLPointer) z, nx, ny, opt, clevel, nlevel, 0, 0, NULL, NULL );
888 }
889
890 void
plfplot3dc(PLFLT_VECTOR x,PLFLT_VECTOR y,PLF2OPS zops,PLPointer zp,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel)891 plfplot3dc( PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp,
892 PLINT nx, PLINT ny, PLINT opt, PLFLT_VECTOR clevel, PLINT nlevel )
893 {
894 plfplot3dcl( x, y, zops, zp, nx, ny, opt, clevel, nlevel, 0, 0, NULL, NULL );
895 }
896
897 //--------------------------------------------------------------------------
898 // void plot3dcl(x, y, z, nx, ny, opt, clevel, nlevel,
899 // indexxmin, indexxmax, indexymin, indexymax)
900 //
901 // Plots a 3-d representation of the function z[x][y]. The x values
902 // are stored as x[0..nx-1], the y values as y[0..ny-1], and the z
903 // values are in the 2-d array z[][]. The integer "opt" specifies:
904 //
905 // DRAW_LINEX : Draw lines parallel to x-axis
906 // DRAW_LINEY : Draw lines parallel to y-axis
907 // DRAW_LINEXY: Draw lines parallel to both axes
908 // MAG_COLOR: Magnitude coloring of wire frame
909 // BASE_CONT: Draw contour at bottom xy plane
910 // TOP_CONT: Draw contour at top xy plane (not yet)
911 // DRAW_SIDES: Draw sides around the plot
912 // MESH: Draw the "under" side of the plot
913 //
914 // or any bitwise combination, e.g. "MAG_COLOR | DRAW_LINEX"
915 // indexymin and indexymax are arrays which specify the y index limits
916 // (following the convention that the upper range limit is one more than
917 // actual index limit) for an x index range of indexxmin, indexxmax.
918 //--------------------------------------------------------------------------
919
920 void
c_plot3dcl(PLFLT_VECTOR x,PLFLT_VECTOR y,PLFLT_MATRIX z,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel,PLINT indexxmin,PLINT indexxmax,PLINT_VECTOR indexymin,PLINT_VECTOR indexymax)921 c_plot3dcl( PLFLT_VECTOR x, PLFLT_VECTOR y, PLFLT_MATRIX z,
922 PLINT nx, PLINT ny, PLINT opt,
923 PLFLT_VECTOR clevel, PLINT nlevel,
924 PLINT indexxmin, PLINT indexxmax, PLINT_VECTOR indexymin, PLINT_VECTOR indexymax )
925 {
926 plfplot3dcl( x, y, plf2ops_c(), (PLPointer) z, nx, ny,
927 opt, clevel, nlevel, indexxmin, indexxmax, indexymin, indexymax );
928 }
929
930 //--------------------------------------------------------------------------
931 //! Plots a 3-d representation of the virtual function z, which is represented
932 //! by zops and zp.
933 //!
934 //! @param x The x values are stored as x[0..nx-1]
935 //! @param y The y values are stored as y[0..ny-1]
936 //! @param zops Pointer to a plf2ops_t structure of function pointers (see
937 //! plf2opts_t in plplot.h) which define how to perform various manipulations
938 //! (including retrieval) on the elements of the the 2D data field pointed to
939 //! by zp. Pointers suitable for passing as zops can be obtained for some
940 //! predefined types of 2-d data storage by calling one of the plf2ops_*()
941 //! functions (see plf2ops.c) or you can create your own set for arbitrary 2-d
942 //! storage formats.
943 //! @param zp Pointer to 2D data field. This pointer is passed to the
944 //! functions of zops whenever the 2D field needs to be manipulated. The
945 //! combination of zops and zp provides total flexibility in how the underlying
946 //! data values are managed.
947 //! @param nx The number of values in x.
948 //! @param ny The number of values in y.
949 //! @param opt Specifies options for the plot. It can be a bitwise OR-ing of
950 //! these:
951 //! - DRAW_LINEX : Draw lines parallel to x-axis
952 //! - DRAW_LINEY : Draw lines parallel to y-axis
953 //! - DRAW_LINEXY: Draw lines parallel to both axes
954 //! - MAG_COLOR: Magnitude coloring of wire frame
955 //! - BASE_CONT: Draw contour at bottom xy plane
956 //! - TOP_CONT: Draw contour at top xy plane (not yet)
957 //! - DRAW_SIDES: Draw sides around the plot
958 //! - MESH: Draw the "under" side of the plot
959 //! or any bitwise OR'd combination, e.g. "MAG_COLOR | DRAW_LINEX"
960 //! @param clevel z values at which to draw contours
961 //! @param nlevel Number of values in clevels
962 //! @param PL_UNUSED( indexxmin ) Not used.
963 //! @param PL_UNUSED( indexxmax ) Not used.
964 //! @param PL_UNUSED( indexymin ) Not used.
965 //! @param PL_UNUSED( indexymax ) Not used.
966 //!
967 //--------------------------------------------------------------------------
968
969 void
plfplot3dcl(PLFLT_VECTOR x,PLFLT_VECTOR y,PLF2OPS zops,PLPointer zp,PLINT nx,PLINT ny,PLINT opt,PLFLT_VECTOR clevel,PLINT nlevel,PLINT PL_UNUSED (indexxmin),PLINT PL_UNUSED (indexxmax),PLINT_VECTOR PL_UNUSED (indexymin),PLINT_VECTOR PL_UNUSED (indexymax))970 plfplot3dcl( PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp,
971 PLINT nx, PLINT ny, PLINT opt,
972 PLFLT_VECTOR clevel, PLINT nlevel,
973 PLINT PL_UNUSED( indexxmin ), PLINT PL_UNUSED( indexxmax ), PLINT_VECTOR PL_UNUSED( indexymin ), PLINT_VECTOR PL_UNUSED( indexymax ) )
974 {
975 PLFLT cxx, cxy, cyx, cyy, cyz;
976 PLINT init, ix, iy, color;
977 PLFLT width;
978 PLFLT xmin, xmax, ymin, ymax, zmin, zmax, zscale;
979 PLINT ixmin = 0, ixmax = nx - 1, iymin = 0, iymax = ny - 1;
980 PLINT clipped = 0, base_cont = 0, side = 0;
981 PLFLT ( *getz )( PLPointer, PLINT, PLINT ) = zops->get;
982 PLFLT *_x = NULL, *_y = NULL, **_z = NULL;
983 PLFLT_VECTOR x_modified, y_modified;
984 int i;
985
986 pl3mode = 0;
987
988 if ( plsc->level < 3 )
989 {
990 myabort( "plot3dcl: Please set up window first" );
991 return;
992 }
993
994 if ( ( opt & 3 ) == 0 )
995 {
996 myabort( "plot3dcl: Bad option" );
997 return;
998 }
999
1000 if ( nx <= 0 || ny <= 0 )
1001 {
1002 myabort( "plot3dcl: Bad array dimensions." );
1003 return;
1004 }
1005
1006 plP_gdom( &xmin, &xmax, &ymin, &ymax );
1007 plP_grange( &zscale, &zmin, &zmax );
1008 if ( zmin > zmax )
1009 {
1010 PLFLT t = zmin;
1011 zmin = zmax;
1012 zmax = t;
1013 }
1014
1015 // Check that points in x and in y are strictly increasing
1016
1017 for ( i = 0; i < nx - 1; i++ )
1018 {
1019 if ( x[i] >= x[i + 1] )
1020 {
1021 myabort( "plot3dcl: X array must be strictly increasing" );
1022 return;
1023 }
1024 }
1025 for ( i = 0; i < ny - 1; i++ )
1026 {
1027 if ( y[i] >= y[i + 1] )
1028 {
1029 myabort( "plot3dcl: Y array must be strictly increasing" );
1030 return;
1031 }
1032 }
1033
1034 if ( opt & MESH )
1035 pl3mode = 1;
1036
1037 if ( opt & DRAW_SIDES )
1038 side = 1;
1039
1040 // figure out the part of the data to use
1041 if ( xmin < x[0] )
1042 xmin = x[0];
1043 if ( xmax > x[nx - 1] )
1044 xmax = x[nx - 1];
1045 if ( ymin < y[0] )
1046 ymin = y[0];
1047 if ( ymax > y[ny - 1] )
1048 ymax = y[ny - 1];
1049 for ( ixmin = 0; ixmin < nx - 1 && x[ixmin + 1] < xmin; ixmin++ )
1050 {
1051 }
1052 for ( ixmax = nx - 1; ixmax > 0 && x[ixmax - 1] > xmax; ixmax-- )
1053 {
1054 }
1055 for ( iymin = 0; iymin < ny - 1 && y[iymin + 1] < ymin; iymin++ )
1056 {
1057 }
1058 for ( iymax = ny - 1; iymax > 0 && y[iymax - 1] > ymax; iymax-- )
1059 {
1060 }
1061 //fprintf(stderr, "(%d,%d) %d %d %d %d\n", nx, ny, ixmin, ixmax, iymin, iymax);
1062 // do we need to clip?
1063 if ( ixmin > 0 || ixmax < nx - 1 || iymin > 0 || iymax < ny - 1 )
1064 {
1065 // adjust the input so it stays within bounds
1066 int _nx = ixmax - ixmin + 1;
1067 int _ny = iymax - iymin + 1;
1068 PLFLT ty0, ty1, tx0, tx1;
1069 int j;
1070
1071 if ( _nx <= 1 || _ny <= 1 )
1072 {
1073 myabort( "plot3dcl: selected x or y range has no data" );
1074 return;
1075 }
1076
1077 // allocate storage for new versions of the input vectors
1078 if ( ( ( _x = (PLFLT *) malloc( (size_t) _nx * sizeof ( PLFLT ) ) ) == NULL ) ||
1079 ( ( _y = (PLFLT *) malloc( (size_t) _ny * sizeof ( PLFLT ) ) ) == NULL ) ||
1080 ( ( _z = (PLFLT **) malloc( (size_t) _nx * sizeof ( PLFLT* ) ) ) == NULL ) )
1081 {
1082 plexit( "c_plot3dcl: Insufficient memory" );
1083 }
1084
1085 clipped = 1;
1086
1087 // copy over the independent variables
1088 _x[0] = xmin;
1089 _x[_nx - 1] = xmax;
1090 for ( i = 1; i < _nx - 1; i++ )
1091 _x[i] = x[ixmin + i];
1092 _y[0] = ymin;
1093 _y[_ny - 1] = ymax;
1094 for ( i = 1; i < _ny - 1; i++ )
1095 _y[i] = y[iymin + i];
1096
1097 // copy the data array so we can interpolate around the edges
1098 for ( i = 0; i < _nx; i++ )
1099 {
1100 if ( ( _z[i] = (PLFLT *) malloc( (size_t) _ny * sizeof ( PLFLT ) ) ) == NULL )
1101 {
1102 plexit( "c_plot3dcl: Insufficient memory" );
1103 }
1104 }
1105
1106 // interpolation factors for the 4 edges
1107 ty0 = ( _y[0] - y[iymin] ) / ( y[iymin + 1] - y[iymin] );
1108 ty1 = ( _y[_ny - 1] - y[iymax - 1] ) / ( y[iymax] - y[iymax - 1] );
1109 tx0 = ( _x[0] - x[ixmin] ) / ( x[ixmin + 1] - x[ixmin] );
1110 tx1 = ( _x[_nx - 1] - x[ixmax - 1] ) / ( x[ixmax] - x[ixmax - 1] );
1111 for ( i = 0; i < _nx; i++ )
1112 {
1113 if ( i == 0 )
1114 {
1115 _z[i][0] = getz( zp, ixmin, iymin ) * ( 1 - ty0 ) * ( 1 - tx0 ) + getz( zp, ixmin, iymin + 1 ) * ( 1 - tx0 ) * ty0
1116 + getz( zp, ixmin + 1, iymin ) * tx0 * ( 1 - ty0 ) + getz( zp, ixmin + 1, iymin + 1 ) * tx0 * ty0;
1117 for ( j = 1; j < _ny - 1; j++ )
1118 _z[i][j] = getz( zp, ixmin, iymin + j ) * ( 1 - tx0 ) + getz( zp, ixmin + 1, iymin + j ) * tx0;
1119 _z[i][_ny - 1] = getz( zp, ixmin, iymax - 1 ) * ( 1 - tx0 ) * ( 1 - ty1 ) + getz( zp, ixmin, iymax ) * ( 1 - tx0 ) * ty1
1120 + getz( zp, ixmin + 1, iymax - 1 ) * tx0 * ( 1 - ty1 ) + getz( zp, ixmin + 1, iymax ) * tx0 * ty1;
1121 }
1122 else if ( i == _nx - 1 )
1123 {
1124 _z[i][0] = getz( zp, ixmax - 1, iymin ) * ( 1 - tx1 ) * ( 1 - ty0 ) + getz( zp, ixmax - 1, iymin + 1 ) * ( 1 - tx1 ) * ty0
1125 + getz( zp, ixmax, iymin ) * tx1 * ( 1 - ty0 ) + getz( zp, ixmax, iymin + 1 ) * tx1 * ty0;
1126 for ( j = 1; j < _ny - 1; j++ )
1127 _z[i][j] = getz( zp, ixmax - 1, iymin + j ) * ( 1 - tx1 ) + getz( zp, ixmax, iymin + j ) * tx1;
1128 _z[i][_ny - 1] = getz( zp, ixmax - 1, iymax - 1 ) * ( 1 - tx1 ) * ( 1 - ty1 ) + getz( zp, ixmax, iymax ) * ( 1 - tx1 ) * ty1
1129 + getz( zp, ixmax, iymax - 1 ) * tx1 * ( 1 - ty1 ) + getz( zp, ixmax, iymax ) * tx1 * ty1;
1130 }
1131 else
1132 {
1133 _z[i][0] = getz( zp, ixmin + i, iymin ) * ( 1 - ty0 ) + getz( zp, ixmin + i, iymin + 1 ) * ty0;
1134 for ( j = 1; j < _ny - 1; j++ )
1135 _z[i][j] = getz( zp, ixmin + i, iymin + j );
1136 _z[i][_ny - 1] = getz( zp, ixmin + i, iymax - 1 ) * ( 1 - ty1 ) + getz( zp, ixmin + i, iymax ) * ty1;
1137 }
1138 for ( j = 0; j < _ny; j++ )
1139 {
1140 if ( _z[i][j] < zmin )
1141 _z[i][j] = zmin;
1142 else if ( _z[i][j] > zmax )
1143 _z[i][j] = zmax;
1144 }
1145 }
1146 // replace the input with our clipped versions
1147 zp = (PLPointer) _z;
1148 getz = plf2ops_c()->get;
1149 nx = _nx;
1150 ny = _ny;
1151 // Do not want to modify input x and y (const modifier)
1152 x_modified = (PLFLT_VECTOR) _x;
1153 y_modified = (PLFLT_VECTOR) _y;
1154 }
1155 else
1156 {
1157 x_modified = x;
1158 y_modified = y;
1159 }
1160
1161 // From here on must use x_modified and y_modified rather than
1162 // x and y.
1163 if ( ( opt & BASE_CONT ) || ( opt & TOP_CONT ) || ( opt & MAG_COLOR ) )
1164 {
1165 //
1166 // Don't use the data z value to scale the color, use the z axis
1167 // values set by plw3d()
1168 //
1169 // plMinMax2dGrid(z, nx, ny, &fc_maxz, &fc_minz);
1170 //
1171
1172 fc_minz = plsc->ranmi;
1173 fc_maxz = plsc->ranma;
1174
1175 if ( fc_maxz == fc_minz )
1176 {
1177 plwarn( "plot3dcl: Maximum and minimum Z values are equal! \"fixing\"..." );
1178 fc_maxz = fc_minz + 1e-6;
1179 }
1180 }
1181
1182 if ( opt & BASE_CONT ) // If enabled, draw the contour at the base.
1183 {
1184 if ( clevel != NULL && nlevel != 0 )
1185 {
1186 base_cont = 1;
1187 // even if MESH is not set, "set it",
1188 // as the base contour can only be done in this case
1189 pl3mode = 1;
1190 }
1191 }
1192
1193 if ( opt & MAG_COLOR ) // If enabled, use magnitude colored wireframe
1194 {
1195 if ( ( ctmp = (PLFLT *) malloc( (size_t) ( 2 * MAX( nx, ny ) ) * sizeof ( PLFLT ) ) ) == NULL )
1196 {
1197 plexit( "c_plot3dcl: Insufficient memory" );
1198 }
1199 }
1200 else
1201 ctmp = NULL;
1202
1203 // next logic only knows opt = 1 | 2 | 3, make sure that it only gets that
1204 opt &= DRAW_LINEXY;
1205
1206 // Allocate work arrays
1207
1208 utmp = (PLINT *) malloc( (size_t) ( 2 * MAX( nx, ny ) ) * sizeof ( PLINT ) );
1209 vtmp = (PLINT *) malloc( (size_t) ( 2 * MAX( nx, ny ) ) * sizeof ( PLINT ) );
1210
1211 if ( !utmp || !vtmp )
1212 myexit( "plot3dcl: Out of memory." );
1213
1214 plP_gw3wc( &cxx, &cxy, &cyx, &cyy, &cyz );
1215 init = 1;
1216 // Call 3d line plotter. Each viewing quadrant
1217 // (perpendicular to x-y plane) must be handled separately.
1218 if ( cxx >= 0.0 && cxy <= 0.0 )
1219 {
1220 if ( opt == DRAW_LINEY )
1221 plt3zz( 1, ny, 1, -1, -opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1222 else
1223 {
1224 for ( iy = 2; iy <= ny; iy++ )
1225 plt3zz( 1, iy, 1, -1, -opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1226 }
1227 if ( opt == DRAW_LINEX )
1228 plt3zz( 1, ny, 1, -1, opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1229 else
1230 {
1231 for ( ix = 1; ix <= nx - 1; ix++ )
1232 plt3zz( ix, ny, 1, -1, opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1233 }
1234 }
1235
1236 else if ( cxx <= 0.0 && cxy <= 0.0 )
1237 {
1238 if ( opt == DRAW_LINEX )
1239 plt3zz( nx, ny, -1, -1, opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1240 else
1241 {
1242 for ( ix = 2; ix <= nx; ix++ )
1243 plt3zz( ix, ny, -1, -1, opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1244 }
1245 if ( opt == DRAW_LINEY )
1246 plt3zz( nx, ny, -1, -1, -opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1247 else
1248 {
1249 for ( iy = ny; iy >= 2; iy-- )
1250 plt3zz( nx, iy, -1, -1, -opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1251 }
1252 }
1253
1254 else if ( cxx <= 0.0 && cxy >= 0.0 )
1255 {
1256 if ( opt == DRAW_LINEY )
1257 plt3zz( nx, 1, -1, 1, -opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1258 else
1259 {
1260 for ( iy = ny - 1; iy >= 1; iy-- )
1261 plt3zz( nx, iy, -1, 1, -opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1262 }
1263 if ( opt == DRAW_LINEX )
1264 plt3zz( nx, 1, -1, 1, opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1265 else
1266 {
1267 for ( ix = nx; ix >= 2; ix-- )
1268 plt3zz( ix, 1, -1, 1, opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1269 }
1270 }
1271
1272 else if ( cxx >= 0.0 && cxy >= 0.0 )
1273 {
1274 if ( opt == DRAW_LINEX )
1275 plt3zz( 1, 1, 1, 1, opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1276 else
1277 {
1278 for ( ix = nx - 1; ix >= 1; ix-- )
1279 plt3zz( ix, 1, 1, 1, opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1280 }
1281 if ( opt == DRAW_LINEY )
1282 plt3zz( 1, 1, 1, 1, -opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1283 else
1284 {
1285 for ( iy = 1; iy <= ny - 1; iy++ )
1286 plt3zz( 1, iy, 1, 1, -opt, &init, x_modified, y_modified, zops, zp, nx, ny, utmp, vtmp, ctmp );
1287 }
1288 }
1289
1290 // draw contour at the base. Not 100%! Why?
1291
1292 if ( base_cont )
1293 {
1294 int np = NPTS, j;
1295 CONT_LEVEL *cont, *clev;
1296 CONT_LINE *cline;
1297
1298 PLINT *uu = (PLINT *) malloc( (size_t) NPTS * sizeof ( PLINT ) );
1299 PLINT *vv = (PLINT *) malloc( (size_t) NPTS * sizeof ( PLINT ) );
1300 // prepare cont_store input
1301 PLFLT **zstore;
1302 PLcGrid2 cgrid2;
1303
1304 if ( ( uu == NULL ) || ( vv == NULL ) )
1305 {
1306 plexit( "c_plot3dcl: Insufficient memory" );
1307 }
1308
1309 cgrid2.nx = nx;
1310 cgrid2.ny = ny;
1311 plAlloc2dGrid( &cgrid2.xg, nx, ny );
1312 plAlloc2dGrid( &cgrid2.yg, nx, ny );
1313 plAlloc2dGrid( &zstore, nx, ny );
1314
1315 for ( i = 0; i < nx; i++ )
1316 {
1317 for ( j = 0; j < ny; j++ )
1318 {
1319 cgrid2.xg[i][j] = x_modified[i];
1320 cgrid2.yg[i][j] = y_modified[j];
1321 zstore[i][j] = getz( zp, i, j );
1322 }
1323 }
1324
1325 pl3upv = 0;
1326
1327 // Fill cont structure with contours.
1328 cont_store( (PLFLT_MATRIX) zstore, nx, ny, 1, nx, 1, ny,
1329 clevel, nlevel, pltr2, (void *) &cgrid2, &cont );
1330
1331 // Free the 2D input arrays to cont_store since not needed any more.
1332 plFree2dGrid( zstore, nx, ny );
1333 plFree2dGrid( cgrid2.xg, nx, ny );
1334 plFree2dGrid( cgrid2.yg, nx, ny );
1335
1336 // follow the contour levels and lines
1337 clev = cont;
1338 do // for each contour level
1339 {
1340 cline = clev->line;
1341 do // there are several lines that make up each contour
1342 {
1343 int cx, k, l, m, start, end;
1344 PLFLT tx, ty;
1345 if ( cline->npts > np )
1346 {
1347 np = cline->npts;
1348 if ( ( ( uu = (PLINT *) realloc( uu, (size_t) np * sizeof ( PLINT ) ) ) == NULL ) ||
1349 ( ( vv = (PLINT *) realloc( vv, (size_t) np * sizeof ( PLINT ) ) ) == NULL ) )
1350 {
1351 plexit( "c_plot3dcl: Insufficient memory" );
1352 }
1353 }
1354
1355 // the hidden line plotter plnxtv() only works OK if the x points are in increasing order.
1356 // As this does not always happens, the situation must be detected and the line segment
1357 // must be reversed before being plotted
1358 i = 0;
1359 if ( cline->npts > 0 )
1360 {
1361 do
1362 {
1363 plcol1( ( clev->level - fc_minz ) / ( fc_maxz - fc_minz ) );
1364 cx = plP_wcpcx( plP_w3wcx( cline->x[i], cline->y[i], plsc->ranmi ) );
1365 for ( j = i; j < cline->npts; j++ ) // convert to 2D coordinates
1366 {
1367 uu[j] = plP_wcpcx( plP_w3wcx( cline->x[j], cline->y[j], plsc->ranmi ) );
1368 vv[j] = plP_wcpcy( plP_w3wcy( cline->x[j], cline->y[j], plsc->ranmi ) );
1369 if ( uu[j] < cx ) // find turn back point
1370 break;
1371 else
1372 cx = uu[j];
1373 }
1374 plnxtv( &uu[i], &vv[i], NULL, j - i, 0 ); // plot line with increasing x
1375
1376 if ( j < cline->npts ) // line not yet finished,
1377 {
1378 start = j - 1;
1379 for ( i = start; i < cline->npts; i++ ) // search turn forward point
1380 {
1381 uu[i] = plP_wcpcx( plP_w3wcx( cline->x[i], cline->y[i], plsc->ranmi ) );
1382 if ( uu[i] > cx )
1383 break;
1384 else
1385 cx = uu[i];
1386 }
1387 end = i - 1;
1388
1389 for ( k = 0; k < ( end - start + 1 ) / 2; k++ ) // reverse line segment
1390 {
1391 l = start + k;
1392 m = end - k;
1393 tx = cline->x[l];
1394 ty = cline->y[l];
1395 cline->x[l] = cline->x[m];
1396 cline->y[l] = cline->y[m];
1397 cline->x[m] = tx;
1398 cline->y[m] = ty;
1399 }
1400
1401 // convert to 2D coordinates
1402 for ( j = start; j <= end; j++ )
1403 {
1404 uu[j] = plP_wcpcx( plP_w3wcx( cline->x[j], cline->y[j], plsc->ranmi ) );
1405 vv[j] = plP_wcpcy( plP_w3wcy( cline->x[j], cline->y[j], plsc->ranmi ) );
1406 }
1407 plnxtv( &uu[start], &vv[start], NULL, end - start + 1, 0 ); // and plot it
1408
1409 cline->x[end] = cline->x[start];
1410 cline->y[end] = cline->y[start];
1411 i = end; // restart where it was left
1412 }
1413 } while ( j < cline->npts && i < cline->npts );
1414 }
1415 cline = cline->next;
1416 }
1417 while ( cline != NULL );
1418 clev = clev->next;
1419 }
1420 while ( clev != NULL );
1421
1422 cont_clean_store( cont ); // now release the contour memory
1423 pl3upv = 1;
1424 free( uu );
1425 free( vv );
1426 }
1427
1428 // Finish up by drawing sides, background grid (both are optional)
1429
1430 if ( side )
1431 plside3( x_modified, y_modified, zops, zp, nx, ny, opt );
1432
1433 if ( zbflg )
1434 {
1435 color = plsc->icol0;
1436 width = plsc->width;
1437 plwidth( zbwidth );
1438 plcol0( zbcol );
1439 plgrid3( zbtck );
1440 plwidth( width );
1441 plcol0( color );
1442 }
1443
1444 freework();
1445
1446 if ( clipped )
1447 {
1448 free( _x );
1449 free( _y );
1450 for ( i = 0; i < nx; i++ )
1451 free( _z[i] );
1452 free( _z );
1453 }
1454 }
1455
1456 //--------------------------------------------------------------------------
1457 // void plxyindexlimits()
1458 //
1459 // Transform from y array limits to corresponding x array limits (or vice
1460 // versa).
1461 //
1462 // N.B. we follow the convention here that all upper range limits are one
1463 // more than the actual last index.
1464 // instart (>= 0) through inn is the index range where
1465 // the input inarray_min and inarray_max arrays are defined.
1466 //
1467 // outstart (>= 0), through outn (with outn <= outnmax) is the index range
1468 // where the output outarray_min and outarray_max arrays are defined.
1469 //
1470 // In order to assure the transformation from y array limits to x array limits
1471 // (or vice versa) is single-valued, this programme plaborts if the
1472 // inarray_min array has a maximum or inarray_max array has a minimum.
1473 //--------------------------------------------------------------------------
1474
1475 //static void
1476 //plxyindexlimits( PLINT instart, PLINT inn,
1477 // PLINT *inarray_min, PLINT *inarray_max,
1478 // PLINT *outstart, PLINT *outn, PLINT outnmax,
1479 // PLINT *outarray_min, PLINT *outarray_max )
1480 //{
1481 // PLINT i, j;
1482 // if ( inn < 0 )
1483 // {
1484 // myabort( "plxyindexlimits: Must have instart >= 0" );
1485 // return;
1486 // }
1487 // if ( inn - instart <= 0 )
1488 // {
1489 // myabort( "plxyindexlimits: Must have at least 1 defined point" );
1490 // return;
1491 // }
1492 // *outstart = inarray_min[instart];
1493 // *outn = inarray_max[instart];
1494 // for ( i = instart; i < inn; i++ )
1495 // {
1496 // *outstart = MIN( *outstart, inarray_min[i] );
1497 // *outn = MAX( *outn, inarray_max[i] );
1498 // if ( i + 2 < inn )
1499 // {
1500 // if ( inarray_min[i] < inarray_min[i + 1] &&
1501 // inarray_min[i + 1] > inarray_min[i + 2] )
1502 // {
1503 // myabort( "plxyindexlimits: inarray_min must not have a maximum" );
1504 // return;
1505 // }
1506 // if ( inarray_max[i] > inarray_max[i + 1] &&
1507 // inarray_max[i + 1] < inarray_max[i + 2] )
1508 // {
1509 // myabort( "plxyindexlimits: inarray_max must not have a minimum" );
1510 // return;
1511 // }
1512 // }
1513 // }
1514 // if ( *outstart < 0 )
1515 // {
1516 // myabort( "plxyindexlimits: Must have all elements of inarray_min >= 0" );
1517 // return;
1518 // }
1519 // if ( *outn > outnmax )
1520 // {
1521 // myabort( "plxyindexlimits: Must have all elements of inarray_max <= outnmax" );
1522 // return;
1523 // }
1524 // for ( j = *outstart; j < *outn; j++ )
1525 // {
1526 // i = instart;
1527 // // Find first valid i for this j.
1528 // while ( i < inn && !( inarray_min[i] <= j && j < inarray_max[i] ) )
1529 // i++;
1530 // if ( i < inn )
1531 // outarray_min[j] = i;
1532 // else
1533 // {
1534 // myabort( "plxyindexlimits: bad logic; invalid i should never happen" );
1535 // return;
1536 // }
1537 // // Find next invalid i for this j.
1538 // while ( i < inn && ( inarray_min[i] <= j && j < inarray_max[i] ) )
1539 // i++;
1540 // outarray_max[j] = i;
1541 // }
1542 //}
1543
1544 //--------------------------------------------------------------------------
1545 // void plP_gzback()
1546 //
1547 // Get background parameters for 3d plot.
1548 //--------------------------------------------------------------------------
1549
1550 void
plP_gzback(PLINT ** zbf,PLINT ** zbc,PLFLT ** zbt,PLFLT ** zbw)1551 plP_gzback( PLINT **zbf, PLINT **zbc, PLFLT **zbt, PLFLT **zbw )
1552 {
1553 *zbf = &zbflg;
1554 *zbc = &zbcol;
1555 *zbt = &zbtck;
1556 *zbw = &zbwidth;
1557 }
1558
1559 //--------------------------------------------------------------------------
1560 // PLFLT plGetAngleToLight()
1561 //
1562 // Gets cos of angle between normal to a polygon and a light source.
1563 // Requires at least 3 elements, forming non-parallel lines
1564 // in the arrays.
1565 //--------------------------------------------------------------------------
1566
1567 static PLFLT
plGetAngleToLight(PLFLT * x,PLFLT * y,PLFLT * z)1568 plGetAngleToLight( PLFLT* x, PLFLT* y, PLFLT* z )
1569 {
1570 PLFLT vx1, vx2, vy1, vy2, vz1, vz2;
1571 PLFLT px, py, pz;
1572 PLFLT vlx, vly, vlz;
1573 PLFLT mag1, mag2;
1574 PLFLT cosangle;
1575
1576 vx1 = x[1] - x[0];
1577 vx2 = x[2] - x[1];
1578 vy1 = y[1] - y[0];
1579 vy2 = y[2] - y[1];
1580 vz1 = z[1] - z[0];
1581 vz2 = z[2] - z[1];
1582
1583 // Find vector perpendicular to the face
1584 px = vy1 * vz2 - vz1 * vy2;
1585 py = vz1 * vx2 - vx1 * vz2;
1586 pz = vx1 * vy2 - vy1 * vx2;
1587 mag1 = px * px + py * py + pz * pz;
1588
1589 // Vectors were parallel!
1590 if ( mag1 == 0 )
1591 return 1;
1592
1593 vlx = xlight - x[0];
1594 vly = ylight - y[0];
1595 vlz = zlight - z[0];
1596 mag2 = vlx * vlx + vly * vly + vlz * vlz;
1597 if ( mag2 == 0 )
1598 return 1;
1599
1600 // Now have 3 vectors going through the first point on the given surface
1601 cosangle = fabs( ( vlx * px + vly * py + vlz * pz ) / sqrt( mag1 * mag2 ) );
1602
1603 // In case of numerical rounding
1604 if ( cosangle > 1 )
1605 cosangle = 1;
1606 return cosangle;
1607 }
1608
1609 //--------------------------------------------------------------------------
1610 // void plt3zz()
1611 //
1612 // Draws the next zig-zag line for a 3-d plot. The data is stored in array
1613 // z[][] as a function of x[] and y[], and is plotted out starting at index
1614 // (x0,y0).
1615 //
1616 // Depending on the state of "flag", the sequence of data points sent to
1617 // plnxtv is altered so as to allow cross-hatch plotting, or plotting
1618 // parallel to either the x-axis or the y-axis.
1619 //--------------------------------------------------------------------------
1620
1621 static void
plt3zz(PLINT x0,PLINT y0,PLINT dx,PLINT dy,PLINT flag,PLINT * init,PLFLT_VECTOR x,PLFLT_VECTOR y,PLF2OPS zops,PLPointer zp,PLINT nx,PLINT ny,PLINT * u,PLINT * v,PLFLT * c)1622 plt3zz( PLINT x0, PLINT y0, PLINT dx, PLINT dy, PLINT flag, PLINT *init,
1623 PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp, PLINT nx, PLINT ny,
1624 PLINT *u, PLINT *v, PLFLT* c )
1625 {
1626 PLINT n = 0;
1627 PLFLT x2d, y2d;
1628 PLFLT ( *getz )( PLPointer, PLINT, PLINT ) = zops->get;
1629
1630 while ( 1 <= x0 && x0 <= nx && 1 <= y0 && y0 <= ny )
1631 {
1632 x2d = plP_w3wcx( x[x0 - 1], y[y0 - 1], getz( zp, x0 - 1, y0 - 1 ) );
1633 y2d = plP_w3wcy( x[x0 - 1], y[y0 - 1], getz( zp, x0 - 1, y0 - 1 ) );
1634 u[n] = plP_wcpcx( x2d );
1635 v[n] = plP_wcpcy( y2d );
1636 if ( c != NULL )
1637 c[n] = ( getz( zp, x0 - 1, y0 - 1 ) - fc_minz ) / ( fc_maxz - fc_minz );
1638
1639 switch ( flag )
1640 {
1641 case -3:
1642 y0 += dy;
1643 flag = -flag;
1644 break;
1645 case -2:
1646 y0 += dy;
1647 break;
1648 case -1:
1649 y0 += dy;
1650 flag = -flag;
1651 break;
1652 case 1:
1653 x0 += dx;
1654 break;
1655 case 2:
1656 x0 += dx;
1657 flag = -flag;
1658 break;
1659 case 3:
1660 x0 += dx;
1661 flag = -flag;
1662 break;
1663 }
1664 n++;
1665 }
1666
1667 if ( flag == 1 || flag == -2 )
1668 {
1669 if ( flag == 1 )
1670 {
1671 x0 -= dx;
1672 y0 += dy;
1673 }
1674 else if ( flag == -2 )
1675 {
1676 y0 -= dy;
1677 x0 += dx;
1678 }
1679 if ( 1 <= x0 && x0 <= nx && 1 <= y0 && y0 <= ny )
1680 {
1681 x2d = plP_w3wcx( x[x0 - 1], y[y0 - 1], getz( zp, x0 - 1, y0 - 1 ) );
1682 y2d = plP_w3wcy( x[x0 - 1], y[y0 - 1], getz( zp, x0 - 1, y0 - 1 ) );
1683 u[n] = plP_wcpcx( x2d );
1684 v[n] = plP_wcpcy( y2d );
1685 if ( c != NULL )
1686 c[n] = ( getz( zp, x0 - 1, y0 - 1 ) - fc_minz ) / ( fc_maxz - fc_minz );
1687 n++;
1688 }
1689 }
1690
1691 // All the setup is done. Time to do the work.
1692
1693 plnxtv( u, v, c, n, *init );
1694 *init = 0;
1695 }
1696
1697 //--------------------------------------------------------------------------
1698 // void plside3()
1699 //
1700 // This routine draws sides around the front of the 3d plot so that
1701 // it does not appear to float.
1702 //--------------------------------------------------------------------------
1703
1704 static void
plside3(PLFLT_VECTOR x,PLFLT_VECTOR y,PLF2OPS zops,PLPointer zp,PLINT nx,PLINT ny,PLINT opt)1705 plside3( PLFLT_VECTOR x, PLFLT_VECTOR y, PLF2OPS zops, PLPointer zp, PLINT nx, PLINT ny, PLINT opt )
1706 {
1707 PLINT i;
1708 PLFLT cxx, cxy, cyx, cyy, cyz;
1709 PLFLT xmin, ymin, zmin, xmax, ymax, zmax, zscale;
1710 PLFLT tx, ty, ux, uy;
1711 PLFLT ( *getz )( PLPointer, PLINT, PLINT ) = zops->get;
1712
1713 plP_gw3wc( &cxx, &cxy, &cyx, &cyy, &cyz );
1714 plP_gdom( &xmin, &xmax, &ymin, &ymax );
1715 plP_grange( &zscale, &zmin, &zmax );
1716
1717 // Get x, y coordinates of legs and plot
1718
1719 if ( cxx >= 0.0 && cxy <= 0.0 )
1720 {
1721 if ( opt != 1 )
1722 {
1723 for ( i = 0; i < nx; i++ )
1724 {
1725 tx = plP_w3wcx( x[i], y[0], zmin );
1726 ty = plP_w3wcy( x[i], y[0], zmin );
1727 ux = plP_w3wcx( x[i], y[0], getz( zp, i, 0 ) );
1728 uy = plP_w3wcy( x[i], y[0], getz( zp, i, 0 ) );
1729 pljoin( tx, ty, ux, uy );
1730 }
1731 }
1732 if ( opt != 2 )
1733 {
1734 for ( i = 0; i < ny; i++ )
1735 {
1736 tx = plP_w3wcx( x[0], y[i], zmin );
1737 ty = plP_w3wcy( x[0], y[i], zmin );
1738 ux = plP_w3wcx( x[0], y[i], getz( zp, 0, i ) );
1739 uy = plP_w3wcy( x[0], y[i], getz( zp, 0, i ) );
1740 pljoin( tx, ty, ux, uy );
1741 }
1742 }
1743 }
1744 else if ( cxx <= 0.0 && cxy <= 0.0 )
1745 {
1746 if ( opt != 1 )
1747 {
1748 for ( i = 0; i < nx; i++ )
1749 {
1750 tx = plP_w3wcx( x[i], y[ny - 1], zmin );
1751 ty = plP_w3wcy( x[i], y[ny - 1], zmin );
1752 ux = plP_w3wcx( x[i], y[ny - 1], getz( zp, i, ny - 1 ) );
1753 uy = plP_w3wcy( x[i], y[ny - 1], getz( zp, i, ny - 1 ) );
1754 pljoin( tx, ty, ux, uy );
1755 }
1756 }
1757 if ( opt != 2 )
1758 {
1759 for ( i = 0; i < ny; i++ )
1760 {
1761 tx = plP_w3wcx( x[0], y[i], zmin );
1762 ty = plP_w3wcy( x[0], y[i], zmin );
1763 ux = plP_w3wcx( x[0], y[i], getz( zp, 0, i ) );
1764 uy = plP_w3wcy( x[0], y[i], getz( zp, 0, i ) );
1765 pljoin( tx, ty, ux, uy );
1766 }
1767 }
1768 }
1769 else if ( cxx <= 0.0 && cxy >= 0.0 )
1770 {
1771 if ( opt != 1 )
1772 {
1773 for ( i = 0; i < nx; i++ )
1774 {
1775 tx = plP_w3wcx( x[i], y[ny - 1], zmin );
1776 ty = plP_w3wcy( x[i], y[ny - 1], zmin );
1777 ux = plP_w3wcx( x[i], y[ny - 1], getz( zp, i, ny - 1 ) );
1778 uy = plP_w3wcy( x[i], y[ny - 1], getz( zp, i, ny - 1 ) );
1779 pljoin( tx, ty, ux, uy );
1780 }
1781 }
1782 if ( opt != 2 )
1783 {
1784 for ( i = 0; i < ny; i++ )
1785 {
1786 tx = plP_w3wcx( x[nx - 1], y[i], zmin );
1787 ty = plP_w3wcy( x[nx - 1], y[i], zmin );
1788 ux = plP_w3wcx( x[nx - 1], y[i], getz( zp, nx - 1, i ) );
1789 uy = plP_w3wcy( x[nx - 1], y[i], getz( zp, nx - 1, i ) );
1790 pljoin( tx, ty, ux, uy );
1791 }
1792 }
1793 }
1794 else if ( cxx >= 0.0 && cxy >= 0.0 )
1795 {
1796 if ( opt != 1 )
1797 {
1798 for ( i = 0; i < nx; i++ )
1799 {
1800 tx = plP_w3wcx( x[i], y[0], zmin );
1801 ty = plP_w3wcy( x[i], y[0], zmin );
1802 ux = plP_w3wcx( x[i], y[0], getz( zp, i, 0 ) );
1803 uy = plP_w3wcy( x[i], y[0], getz( zp, i, 0 ) );
1804 pljoin( tx, ty, ux, uy );
1805 }
1806 }
1807 if ( opt != 2 )
1808 {
1809 for ( i = 0; i < ny; i++ )
1810 {
1811 tx = plP_w3wcx( x[nx - 1], y[i], zmin );
1812 ty = plP_w3wcy( x[nx - 1], y[i], zmin );
1813 ux = plP_w3wcx( x[nx - 1], y[i], getz( zp, nx - 1, i ) );
1814 uy = plP_w3wcy( x[nx - 1], y[i], getz( zp, nx - 1, i ) );
1815 pljoin( tx, ty, ux, uy );
1816 }
1817 }
1818 }
1819 }
1820
1821 //--------------------------------------------------------------------------
1822 // void plgrid3()
1823 //
1824 // This routine draws a grid around the back side of the 3d plot with
1825 // hidden line removal.
1826 //--------------------------------------------------------------------------
1827
1828 static void
plgrid3(PLFLT tick)1829 plgrid3( PLFLT tick )
1830 {
1831 PLFLT xmin, ymin, zmin, xmax, ymax, zmax, zscale;
1832 PLFLT cxx, cxy, cyx, cyy, cyz, zmin_in, zmax_in;
1833 PLINT u[3], v[3];
1834 PLINT nsub = 0;
1835 PLFLT tp;
1836
1837 plP_gw3wc( &cxx, &cxy, &cyx, &cyy, &cyz );
1838 plP_gdom( &xmin, &xmax, &ymin, &ymax );
1839 plP_grange( &zscale, &zmin_in, &zmax_in );
1840 zmin = ( zmax_in > zmin_in ) ? zmin_in : zmax_in;
1841 zmax = ( zmax_in > zmin_in ) ? zmax_in : zmin_in;
1842
1843 pldtik( zmin, zmax, &tick, &nsub, FALSE );
1844 tp = tick * floor( zmin / tick ) + tick;
1845 pl3upv = 0;
1846
1847 if ( cxx >= 0.0 && cxy <= 0.0 )
1848 {
1849 while ( tp <= zmax )
1850 {
1851 u[0] = plP_wcpcx( plP_w3wcx( xmin, ymax, tp ) );
1852 v[0] = plP_wcpcy( plP_w3wcy( xmin, ymax, tp ) );
1853 u[1] = plP_wcpcx( plP_w3wcx( xmax, ymax, tp ) );
1854 v[1] = plP_wcpcy( plP_w3wcy( xmax, ymax, tp ) );
1855 u[2] = plP_wcpcx( plP_w3wcx( xmax, ymin, tp ) );
1856 v[2] = plP_wcpcy( plP_w3wcy( xmax, ymin, tp ) );
1857 plnxtv( u, v, 0, 3, 0 );
1858
1859 tp += tick;
1860 }
1861 u[0] = plP_wcpcx( plP_w3wcx( xmax, ymax, zmin ) );
1862 v[0] = plP_wcpcy( plP_w3wcy( xmax, ymax, zmin ) );
1863 u[1] = plP_wcpcx( plP_w3wcx( xmax, ymax, zmax ) );
1864 v[1] = plP_wcpcy( plP_w3wcy( xmax, ymax, zmax ) );
1865 plnxtv( u, v, 0, 2, 0 );
1866 }
1867 else if ( cxx <= 0.0 && cxy <= 0.0 )
1868 {
1869 while ( tp <= zmax )
1870 {
1871 u[0] = plP_wcpcx( plP_w3wcx( xmax, ymax, tp ) );
1872 v[0] = plP_wcpcy( plP_w3wcy( xmax, ymax, tp ) );
1873 u[1] = plP_wcpcx( plP_w3wcx( xmax, ymin, tp ) );
1874 v[1] = plP_wcpcy( plP_w3wcy( xmax, ymin, tp ) );
1875 u[2] = plP_wcpcx( plP_w3wcx( xmin, ymin, tp ) );
1876 v[2] = plP_wcpcy( plP_w3wcy( xmin, ymin, tp ) );
1877 plnxtv( u, v, 0, 3, 0 );
1878
1879 tp += tick;
1880 }
1881 u[0] = plP_wcpcx( plP_w3wcx( xmax, ymin, zmin ) );
1882 v[0] = plP_wcpcy( plP_w3wcy( xmax, ymin, zmin ) );
1883 u[1] = plP_wcpcx( plP_w3wcx( xmax, ymin, zmax ) );
1884 v[1] = plP_wcpcy( plP_w3wcy( xmax, ymin, zmax ) );
1885 plnxtv( u, v, 0, 2, 0 );
1886 }
1887 else if ( cxx <= 0.0 && cxy >= 0.0 )
1888 {
1889 while ( tp <= zmax )
1890 {
1891 u[0] = plP_wcpcx( plP_w3wcx( xmax, ymin, tp ) );
1892 v[0] = plP_wcpcy( plP_w3wcy( xmax, ymin, tp ) );
1893 u[1] = plP_wcpcx( plP_w3wcx( xmin, ymin, tp ) );
1894 v[1] = plP_wcpcy( plP_w3wcy( xmin, ymin, tp ) );
1895 u[2] = plP_wcpcx( plP_w3wcx( xmin, ymax, tp ) );
1896 v[2] = plP_wcpcy( plP_w3wcy( xmin, ymax, tp ) );
1897 plnxtv( u, v, 0, 3, 0 );
1898
1899 tp += tick;
1900 }
1901 u[0] = plP_wcpcx( plP_w3wcx( xmin, ymin, zmin ) );
1902 v[0] = plP_wcpcy( plP_w3wcy( xmin, ymin, zmin ) );
1903 u[1] = plP_wcpcx( plP_w3wcx( xmin, ymin, zmax ) );
1904 v[1] = plP_wcpcy( plP_w3wcy( xmin, ymin, zmax ) );
1905 plnxtv( u, v, 0, 2, 0 );
1906 }
1907 else if ( cxx >= 0.0 && cxy >= 0.0 )
1908 {
1909 while ( tp <= zmax )
1910 {
1911 u[0] = plP_wcpcx( plP_w3wcx( xmin, ymin, tp ) );
1912 v[0] = plP_wcpcy( plP_w3wcy( xmin, ymin, tp ) );
1913 u[1] = plP_wcpcx( plP_w3wcx( xmin, ymax, tp ) );
1914 v[1] = plP_wcpcy( plP_w3wcy( xmin, ymax, tp ) );
1915 u[2] = plP_wcpcx( plP_w3wcx( xmax, ymax, tp ) );
1916 v[2] = plP_wcpcy( plP_w3wcy( xmax, ymax, tp ) );
1917 plnxtv( u, v, 0, 3, 0 );
1918
1919 tp += tick;
1920 }
1921 u[0] = plP_wcpcx( plP_w3wcx( xmin, ymax, zmin ) );
1922 v[0] = plP_wcpcy( plP_w3wcy( xmin, ymax, zmin ) );
1923 u[1] = plP_wcpcx( plP_w3wcx( xmin, ymax, zmax ) );
1924 v[1] = plP_wcpcy( plP_w3wcy( xmin, ymax, zmax ) );
1925 plnxtv( u, v, 0, 2, 0 );
1926 }
1927 pl3upv = 1;
1928 }
1929
1930 //--------------------------------------------------------------------------
1931 // void plnxtv()
1932 //
1933 // Draw the next view of a 3-d plot. The physical coordinates of the
1934 // points for the next view are placed in the n points of arrays u and
1935 // v. The silhouette found so far is stored in the heap as a set of m peak
1936 // points.
1937 //
1938 // These routines dynamically allocate memory for hidden line removal.
1939 // Memory is allocated in blocks of 2*BINC*sizeof(PLINT) bytes. Large
1940 // values of BINC give better performance but also allocate more memory
1941 // than is needed. If your 3D plots are very "spiky" or you are working
1942 // with very large matrices then you will probably want to increase BINC.
1943 //--------------------------------------------------------------------------
1944
1945 static void
plnxtv(PLINT * u,PLINT * v,PLFLT * c,PLINT n,PLINT init)1946 plnxtv( PLINT *u, PLINT *v, PLFLT* c, PLINT n, PLINT init )
1947 {
1948 plnxtvhi( u, v, c, n, init );
1949
1950 if ( pl3mode )
1951 plnxtvlo( u, v, c, n, init );
1952 }
1953
1954 //--------------------------------------------------------------------------
1955 // void plnxtvhi()
1956 //
1957 // Draw the top side of the 3-d plot.
1958 //--------------------------------------------------------------------------
1959
1960 static void
plnxtvhi(PLINT * u,PLINT * v,PLFLT * c,PLINT n,PLINT init)1961 plnxtvhi( PLINT *u, PLINT *v, PLFLT* c, PLINT n, PLINT init )
1962 {
1963 //
1964 // For the initial set of points, just display them and store them as the
1965 // peak points.
1966 //
1967 if ( init == 1 )
1968 {
1969 int i;
1970 oldhiview = (PLINT *) malloc( (size_t) ( 2 * n ) * sizeof ( PLINT ) );
1971 if ( !oldhiview )
1972 myexit( "plnxtvhi: Out of memory." );
1973
1974 oldhiview[0] = u[0];
1975 oldhiview[1] = v[0];
1976 plP_draw3d( u[0], v[0], c, 0, 1 );
1977 for ( i = 1; i < n; i++ )
1978 {
1979 oldhiview[2 * i] = u[i];
1980 oldhiview[2 * i + 1] = v[i];
1981 plP_draw3d( u[i], v[i], c, i, 0 );
1982 }
1983 mhi = n;
1984 return;
1985 }
1986
1987 //
1988 // Otherwise, we need to consider hidden-line removal problem. We scan
1989 // over the points in both the old (i.e. oldhiview[]) and new (i.e. u[]
1990 // and v[]) arrays in order of increasing x coordinate. At each stage, we
1991 // find the line segment in the other array (if one exists) that straddles
1992 // the x coordinate of the point. We have to determine if the point lies
1993 // above or below the line segment, and to check if the below/above status
1994 // has changed since the last point.
1995 //
1996 // If pl3upv = 0 we do not update the view, this is useful for drawing
1997 // lines on the graph after we are done plotting points. Hidden line
1998 // removal is still done, but the view is not updated.
1999 //
2000 xxhi = 0;
2001 if ( pl3upv != 0 )
2002 {
2003 newhisize = 2 * ( mhi + BINC );
2004 if ( newhiview != NULL )
2005 {
2006 newhiview =
2007 (PLINT *) realloc( (void *) newhiview,
2008 (size_t) newhisize * sizeof ( PLINT ) );
2009 }
2010 else
2011 {
2012 newhiview =
2013 (PLINT *) malloc( (size_t) newhisize * sizeof ( PLINT ) );
2014 }
2015 if ( !newhiview )
2016 myexit( "plnxtvhi: Out of memory." );
2017 }
2018
2019 // Do the draw or shading with hidden line removal
2020
2021 plnxtvhi_draw( u, v, c, n );
2022
2023 // Set oldhiview
2024
2025 swaphiview();
2026 }
2027
2028 //--------------------------------------------------------------------------
2029 // void plnxtvhi_draw()
2030 //
2031 // Draw the top side of the 3-d plot.
2032 //--------------------------------------------------------------------------
2033
2034 static void
plnxtvhi_draw(PLINT * u,PLINT * v,PLFLT * c,PLINT n)2035 plnxtvhi_draw( PLINT *u, PLINT *v, PLFLT* c, PLINT n )
2036 {
2037 PLINT i = 0, j = 0, first = 1;
2038 PLINT sx1 = 0, sx2 = 0, sy1 = 0, sy2 = 0;
2039 PLINT su1, su2, sv1, sv2;
2040 PLINT cx, cy, px, py;
2041 PLINT seg, ptold, lstold = 0, pthi, pnewhi = 0, newhi, change, ochange = 0;
2042
2043 //
2044 // (oldhiview[2*i], oldhiview[2*i]) is the i'th point in the old array
2045 // (u[j], v[j]) is the j'th point in the new array
2046 //
2047
2048 //
2049 // First attempt at 3d shading. It works ok for simple plots, but
2050 // will just not draw faces, or draw them overlapping for very
2051 // jagged plots
2052 //
2053
2054 while ( i < mhi || j < n )
2055 {
2056 //
2057 // The coordinates of the point under consideration are (px,py). The
2058 // line segment joins (sx1,sy1) to (sx2,sy2). "ptold" is true if the
2059 // point lies in the old array. We set it by comparing the x coordinates
2060 // of the i'th old point and the j'th new point, being careful if we
2061 // have fallen past the edges. Having found the point, load up the point
2062 // and segment coordinates appropriately.
2063 //
2064
2065 ptold = ( j >= n || ( i < mhi && oldhiview[2 * i] < u[j] ) );
2066 if ( ptold )
2067 {
2068 px = oldhiview[2 * i];
2069 py = oldhiview[2 * i + 1];
2070 seg = j > 0 && j < n;
2071 if ( seg )
2072 {
2073 sx1 = u[j - 1];
2074 sy1 = v[j - 1];
2075 sx2 = u[j];
2076 sy2 = v[j];
2077 }
2078 }
2079 else
2080 {
2081 px = u[j];
2082 py = v[j];
2083 seg = i > 0 && i < mhi;
2084 if ( seg )
2085 {
2086 sx1 = oldhiview[2 * ( i - 1 )];
2087 sy1 = oldhiview[2 * ( i - 1 ) + 1];
2088 sx2 = oldhiview[2 * i];
2089 sy2 = oldhiview[2 * i + 1];
2090 }
2091 }
2092
2093 //
2094 // Now determine if the point is higher than the segment, using the
2095 // logical function "above". We also need to know if it is the old view
2096 // or the new view that is higher. "newhi" is set true if the new view
2097 // is higher than the old.
2098 //
2099 if ( seg )
2100 pthi = plabv( px, py, sx1, sy1, sx2, sy2 );
2101 else
2102 pthi = 1;
2103
2104 newhi = ( ptold && !pthi ) || ( !ptold && pthi );
2105 //
2106 // The last point and this point lie on different sides of
2107 // the current silouette
2108 //
2109 change = ( newhi && !pnewhi ) || ( !newhi && pnewhi );
2110
2111 //
2112 // There is a new intersection point to put in the peak array if the
2113 // state of "newhi" changes.
2114 //
2115 if ( first )
2116 {
2117 plP_draw3d( px, py, c, j, 1 );
2118 first = 0;
2119 lstold = ptold;
2120 savehipoint( px, py );
2121 pthi = 0;
2122 ochange = 0;
2123 }
2124 else if ( change )
2125 {
2126 //
2127 // Take care of special cases at end of arrays. If pl3upv is 0 the
2128 // endpoints are not connected to the old view.
2129 //
2130 if ( pl3upv == 0 && ( ( !ptold && j == 0 ) || ( ptold && i == 0 ) ) )
2131 {
2132 plP_draw3d( px, py, c, j, 1 );
2133 lstold = ptold;
2134 pthi = 0;
2135 ochange = 0;
2136 }
2137 else if ( pl3upv == 0 &&
2138 ( ( !ptold && i >= mhi ) || ( ptold && j >= n ) ) )
2139 {
2140 plP_draw3d( px, py, c, j, 1 );
2141 lstold = ptold;
2142 pthi = 0;
2143 ochange = 0;
2144 }
2145 else
2146 {
2147 //
2148 // If pl3upv is not 0 then we do want to connect the current line
2149 // with the previous view at the endpoints. Also find intersection
2150 // point with old view.
2151 //
2152 if ( i == 0 )
2153 {
2154 sx1 = oldhiview[0];
2155 sy1 = -1;
2156 sx2 = oldhiview[0];
2157 sy2 = oldhiview[1];
2158 }
2159 else if ( i >= mhi )
2160 {
2161 sx1 = oldhiview[2 * ( mhi - 1 )];
2162 sy1 = oldhiview[2 * ( mhi - 1 ) + 1];
2163 sx2 = oldhiview[2 * ( mhi - 1 )];
2164 sy2 = -1;
2165 }
2166 else
2167 {
2168 sx1 = oldhiview[2 * ( i - 1 )];
2169 sy1 = oldhiview[2 * ( i - 1 ) + 1];
2170 sx2 = oldhiview[2 * i];
2171 sy2 = oldhiview[2 * i + 1];
2172 }
2173
2174 if ( j == 0 )
2175 {
2176 su1 = u[0];
2177 sv1 = -1;
2178 su2 = u[0];
2179 sv2 = v[0];
2180 }
2181 else if ( j >= n )
2182 {
2183 su1 = u[n - 1];
2184 sv1 = v[n - 1];
2185 su2 = u[n - 1];
2186 sv2 = -1;
2187 }
2188 else
2189 {
2190 su1 = u[j - 1];
2191 sv1 = v[j - 1];
2192 su2 = u[j];
2193 sv2 = v[j];
2194 }
2195
2196 // Determine the intersection
2197
2198 pl3cut( sx1, sy1, sx2, sy2, su1, sv1, su2, sv2, &cx, &cy );
2199 if ( cx == px && cy == py )
2200 {
2201 if ( lstold && !ochange )
2202 plP_draw3d( px, py, c, j, 1 );
2203 else
2204 plP_draw3d( px, py, c, j, 0 );
2205
2206 savehipoint( px, py );
2207 lstold = 1;
2208 pthi = 0;
2209 }
2210 else
2211 {
2212 if ( lstold && !ochange )
2213 plP_draw3d( cx, cy, c, j, 1 );
2214 else
2215 plP_draw3d( cx, cy, c, j, 0 );
2216
2217 lstold = 1;
2218 savehipoint( cx, cy );
2219 }
2220 ochange = 1;
2221 }
2222 }
2223
2224 // If point is high then draw plot to point and update view.
2225
2226 if ( pthi )
2227 {
2228 if ( lstold && ptold )
2229 plP_draw3d( px, py, c, j, 1 );
2230 else
2231 plP_draw3d( px, py, c, j, 0 );
2232
2233 savehipoint( px, py );
2234 lstold = ptold;
2235 ochange = 0;
2236 }
2237 pnewhi = newhi;
2238
2239 if ( ptold )
2240 i++;
2241 else
2242 j++;
2243 }
2244 }
2245
2246 //--------------------------------------------------------------------------
2247 // void plP_draw3d()
2248 //
2249 // Does a simple move or line draw.
2250 //--------------------------------------------------------------------------
2251
2252 static void
plP_draw3d(PLINT x,PLINT y,PLFLT * c,PLINT j,PLINT move)2253 plP_draw3d( PLINT x, PLINT y, PLFLT *c, PLINT j, PLINT move )
2254 {
2255 if ( move )
2256 plP_movphy( x, y );
2257 else
2258 {
2259 if ( c != NULL )
2260 plcol1( c[j - 1] );
2261 plP_draphy( x, y );
2262 }
2263 }
2264
2265 //--------------------------------------------------------------------------
2266 // void plnxtvlo()
2267 //
2268 // Draw the bottom side of the 3-d plot.
2269 //--------------------------------------------------------------------------
2270
2271 static void
plnxtvlo(PLINT * u,PLINT * v,PLFLT * c,PLINT n,PLINT init)2272 plnxtvlo( PLINT *u, PLINT *v, PLFLT*c, PLINT n, PLINT init )
2273 {
2274 PLINT i, j, first;
2275 PLINT sx1 = 0, sx2 = 0, sy1 = 0, sy2 = 0;
2276 PLINT su1, su2, sv1, sv2;
2277 PLINT cx, cy, px, py;
2278 PLINT seg, ptold, lstold = 0, ptlo, pnewlo, newlo, change, ochange = 0;
2279
2280 first = 1;
2281 pnewlo = 0;
2282
2283 //
2284 // For the initial set of points, just display them and store them as the
2285 // peak points.
2286 //
2287 if ( init == 1 )
2288 {
2289 oldloview = (PLINT *) malloc( (size_t) ( 2 * n ) * sizeof ( PLINT ) );
2290 if ( !oldloview )
2291 myexit( "\nplnxtvlo: Out of memory." );
2292
2293 plP_draw3d( u[0], v[0], c, 0, 1 );
2294 oldloview[0] = u[0];
2295 oldloview[1] = v[0];
2296 for ( i = 1; i < n; i++ )
2297 {
2298 plP_draw3d( u[i], v[i], c, i, 0 );
2299 oldloview[2 * i] = u[i];
2300 oldloview[2 * i + 1] = v[i];
2301 }
2302 mlo = n;
2303 return;
2304 }
2305
2306 //
2307 // Otherwise, we need to consider hidden-line removal problem. We scan
2308 // over the points in both the old (i.e. oldloview[]) and new (i.e. u[]
2309 // and v[]) arrays in order of increasing x coordinate. At each stage, we
2310 // find the line segment in the other array (if one exists) that straddles
2311 // the x coordinate of the point. We have to determine if the point lies
2312 // above or below the line segment, and to check if the below/above status
2313 // has changed since the last point.
2314 //
2315 // If pl3upv = 0 we do not update the view, this is useful for drawing
2316 // lines on the graph after we are done plotting points. Hidden line
2317 // removal is still done, but the view is not updated.
2318 //
2319 xxlo = 0;
2320 i = 0;
2321 j = 0;
2322 if ( pl3upv != 0 )
2323 {
2324 newlosize = 2 * ( mlo + BINC );
2325 if ( newloview != NULL )
2326 {
2327 newloview =
2328 (PLINT *) realloc( (void *) newloview,
2329 (size_t) newlosize * sizeof ( PLINT ) );
2330 }
2331 else
2332 {
2333 newloview =
2334 (PLINT *) malloc( (size_t) newlosize * sizeof ( PLINT ) );
2335 }
2336 if ( !newloview )
2337 myexit( "plnxtvlo: Out of memory." );
2338 }
2339
2340 //
2341 // (oldloview[2*i], oldloview[2*i]) is the i'th point in the old array
2342 // (u[j], v[j]) is the j'th point in the new array.
2343 //
2344 while ( i < mlo || j < n )
2345 {
2346 //
2347 // The coordinates of the point under consideration are (px,py). The
2348 // line segment joins (sx1,sy1) to (sx2,sy2). "ptold" is true if the
2349 // point lies in the old array. We set it by comparing the x coordinates
2350 // of the i'th old point and the j'th new point, being careful if we
2351 // have fallen past the edges. Having found the point, load up the point
2352 // and segment coordinates appropriately.
2353 //
2354 ptold = ( j >= n || ( i < mlo && oldloview[2 * i] < u[j] ) );
2355 if ( ptold )
2356 {
2357 px = oldloview[2 * i];
2358 py = oldloview[2 * i + 1];
2359 seg = j > 0 && j < n;
2360 if ( seg )
2361 {
2362 sx1 = u[j - 1];
2363 sy1 = v[j - 1];
2364 sx2 = u[j];
2365 sy2 = v[j];
2366 }
2367 }
2368 else
2369 {
2370 px = u[j];
2371 py = v[j];
2372 seg = i > 0 && i < mlo;
2373 if ( seg )
2374 {
2375 sx1 = oldloview[2 * ( i - 1 )];
2376 sy1 = oldloview[2 * ( i - 1 ) + 1];
2377 sx2 = oldloview[2 * i];
2378 sy2 = oldloview[2 * i + 1];
2379 }
2380 }
2381
2382 //
2383 // Now determine if the point is lower than the segment, using the
2384 // logical function "above". We also need to know if it is the old view
2385 // or the new view that is lower. "newlo" is set true if the new view is
2386 // lower than the old.
2387 //
2388 if ( seg )
2389 ptlo = !plabv( px, py, sx1, sy1, sx2, sy2 );
2390 else
2391 ptlo = 1;
2392
2393 newlo = ( ptold && !ptlo ) || ( !ptold && ptlo );
2394 change = ( newlo && !pnewlo ) || ( !newlo && pnewlo );
2395
2396 //
2397 // There is a new intersection point to put in the peak array if the
2398 // state of "newlo" changes.
2399 //
2400 if ( first )
2401 {
2402 plP_draw3d( px, py, c, j, 1 );
2403 first = 0;
2404 lstold = ptold;
2405 savelopoint( px, py );
2406 ptlo = 0;
2407 ochange = 0;
2408 }
2409 else if ( change )
2410 {
2411 //
2412 // Take care of special cases at end of arrays. If pl3upv is 0 the
2413 // endpoints are not connected to the old view.
2414 //
2415 if ( pl3upv == 0 && ( ( !ptold && j == 0 ) || ( ptold && i == 0 ) ) )
2416 {
2417 plP_draw3d( px, py, c, j, 1 );
2418 lstold = ptold;
2419 ptlo = 0;
2420 ochange = 0;
2421 }
2422 else if ( pl3upv == 0 &&
2423 ( ( !ptold && i >= mlo ) || ( ptold && j >= n ) ) )
2424 {
2425 plP_draw3d( px, py, c, j, 1 );
2426 lstold = ptold;
2427 ptlo = 0;
2428 ochange = 0;
2429 }
2430
2431 //
2432 // If pl3upv is not 0 then we do want to connect the current line
2433 // with the previous view at the endpoints. Also find intersection
2434 // point with old view.
2435 //
2436 else
2437 {
2438 if ( i == 0 )
2439 {
2440 sx1 = oldloview[0];
2441 sy1 = 100000;
2442 sx2 = oldloview[0];
2443 sy2 = oldloview[1];
2444 }
2445 else if ( i >= mlo )
2446 {
2447 sx1 = oldloview[2 * ( mlo - 1 )];
2448 sy1 = oldloview[2 * ( mlo - 1 ) + 1];
2449 sx2 = oldloview[2 * ( mlo - 1 )];
2450 sy2 = 100000;
2451 }
2452 else
2453 {
2454 sx1 = oldloview[2 * ( i - 1 )];
2455 sy1 = oldloview[2 * ( i - 1 ) + 1];
2456 sx2 = oldloview[2 * i];
2457 sy2 = oldloview[2 * i + 1];
2458 }
2459
2460 if ( j == 0 )
2461 {
2462 su1 = u[0];
2463 sv1 = 100000;
2464 su2 = u[0];
2465 sv2 = v[0];
2466 }
2467 else if ( j >= n )
2468 {
2469 su1 = u[n - 1];
2470 sv1 = v[n - 1];
2471 su2 = u[n];
2472 sv2 = 100000;
2473 }
2474 else
2475 {
2476 su1 = u[j - 1];
2477 sv1 = v[j - 1];
2478 su2 = u[j];
2479 sv2 = v[j];
2480 }
2481
2482 // Determine the intersection
2483
2484 pl3cut( sx1, sy1, sx2, sy2, su1, sv1, su2, sv2, &cx, &cy );
2485 if ( cx == px && cy == py )
2486 {
2487 if ( lstold && !ochange )
2488 plP_draw3d( px, py, c, j, 1 );
2489 else
2490 plP_draw3d( px, py, c, j, 0 );
2491
2492 savelopoint( px, py );
2493 lstold = 1;
2494 ptlo = 0;
2495 }
2496 else
2497 {
2498 if ( lstold && !ochange )
2499 plP_draw3d( cx, cy, c, j, 1 );
2500 else
2501 plP_draw3d( cx, cy, c, j, 0 );
2502
2503 lstold = 1;
2504 savelopoint( cx, cy );
2505 }
2506 ochange = 1;
2507 }
2508 }
2509
2510 // If point is low then draw plot to point and update view.
2511
2512 if ( ptlo )
2513 {
2514 if ( lstold && ptold )
2515 plP_draw3d( px, py, c, j, 1 );
2516 else
2517 plP_draw3d( px, py, c, j, 0 );
2518
2519 savelopoint( px, py );
2520 lstold = ptold;
2521 ochange = 0;
2522 }
2523
2524 pnewlo = newlo;
2525
2526 if ( ptold )
2527 i = i + 1;
2528 else
2529 j = j + 1;
2530 }
2531
2532 // Set oldloview
2533
2534 swaploview();
2535 }
2536
2537 //--------------------------------------------------------------------------
2538 // savehipoint
2539 // savelopoint
2540 //
2541 // Add a point to the list of currently visible peaks/valleys, when
2542 // drawing the top/bottom surface, respectively.
2543 //--------------------------------------------------------------------------
2544
2545 static void
savehipoint(PLINT px,PLINT py)2546 savehipoint( PLINT px, PLINT py )
2547 {
2548 if ( pl3upv == 0 )
2549 return;
2550
2551 if ( xxhi >= newhisize ) // allocate additional space
2552 {
2553 newhisize += 2 * BINC;
2554 newhiview = (PLINT *) realloc( (void *) newhiview,
2555 (size_t) newhisize * sizeof ( PLINT ) );
2556 if ( !newhiview )
2557 myexit( "savehipoint: Out of memory." );
2558 }
2559
2560 newhiview[xxhi] = px;
2561 xxhi++;
2562 newhiview[xxhi] = py;
2563 xxhi++;
2564 }
2565
2566 static void
savelopoint(PLINT px,PLINT py)2567 savelopoint( PLINT px, PLINT py )
2568 {
2569 if ( pl3upv == 0 )
2570 return;
2571
2572 if ( xxlo >= newlosize ) // allocate additional space
2573 {
2574 newlosize += 2 * BINC;
2575 newloview = (PLINT *) realloc( (void *) newloview,
2576 (size_t) newlosize * sizeof ( PLINT ) );
2577 if ( !newloview )
2578 myexit( "savelopoint: Out of memory." );
2579 }
2580
2581 newloview[xxlo] = px;
2582 xxlo++;
2583 newloview[xxlo] = py;
2584 xxlo++;
2585 }
2586
2587 //--------------------------------------------------------------------------
2588 // swaphiview
2589 // swaploview
2590 //
2591 // Swaps the top/bottom views. Need to do a real swap so that the
2592 // memory cleanup routine really frees everything (and only once).
2593 //--------------------------------------------------------------------------
2594
2595 static void
swaphiview(void)2596 swaphiview( void )
2597 {
2598 PLINT *tmp;
2599
2600 if ( pl3upv != 0 )
2601 {
2602 mhi = xxhi / 2;
2603 tmp = oldhiview;
2604 oldhiview = newhiview;
2605 newhiview = tmp;
2606 }
2607 }
2608
2609 static void
swaploview(void)2610 swaploview( void )
2611 {
2612 PLINT *tmp;
2613
2614 if ( pl3upv != 0 )
2615 {
2616 mlo = xxlo / 2;
2617 tmp = oldloview;
2618 oldloview = newloview;
2619 newloview = tmp;
2620 }
2621 }
2622
2623 //--------------------------------------------------------------------------
2624 // freework
2625 //
2626 // Frees memory associated with work arrays
2627 //--------------------------------------------------------------------------
2628
2629 static void
freework(void)2630 freework( void )
2631 {
2632 free_mem( oldhiview );
2633 free_mem( oldloview );
2634 free_mem( newhiview );
2635 free_mem( newloview );
2636 free_mem( vtmp );
2637 free_mem( utmp );
2638 free_mem( ctmp );
2639 }
2640
2641 //--------------------------------------------------------------------------
2642 // myexit
2643 //
2644 // Calls plexit, cleaning up first.
2645 //--------------------------------------------------------------------------
2646
2647 static void
myexit(PLCHAR_VECTOR msg)2648 myexit( PLCHAR_VECTOR msg )
2649 {
2650 freework();
2651 plexit( msg );
2652 }
2653
2654 //--------------------------------------------------------------------------
2655 // myabort
2656 //
2657 // Calls plabort, cleaning up first.
2658 // Caller should return to the user program.
2659 //--------------------------------------------------------------------------
2660
2661 static void
myabort(PLCHAR_VECTOR msg)2662 myabort( PLCHAR_VECTOR msg )
2663 {
2664 freework();
2665 plabort( msg );
2666 }
2667
2668 //--------------------------------------------------------------------------
2669 // int plabv()
2670 //
2671 // Determines if point (px,py) lies above the line joining (sx1,sy1) to
2672 // (sx2,sy2). It only works correctly if sx1 <= px <= sx2.
2673 //--------------------------------------------------------------------------
2674
2675 static int
plabv(PLINT px,PLINT py,PLINT sx1,PLINT sy1,PLINT sx2,PLINT sy2)2676 plabv( PLINT px, PLINT py, PLINT sx1, PLINT sy1, PLINT sx2, PLINT sy2 )
2677 {
2678 int above;
2679
2680 if ( py >= sy1 && py >= sy2 )
2681 above = 1;
2682 else if ( py < sy1 && py < sy2 )
2683 above = 0;
2684 else if ( (double) ( sx2 - sx1 ) * ( py - sy1 ) >=
2685 (double) ( px - sx1 ) * ( sy2 - sy1 ) )
2686 above = 1;
2687 else
2688 above = 0;
2689
2690 return above;
2691 }
2692
2693 //--------------------------------------------------------------------------
2694 // void pl3cut()
2695 //
2696 // Determines the point of intersection (cx,cy) between the line joining
2697 // (sx1,sy1) to (sx2,sy2) and the line joining (su1,sv1) to (su2,sv2).
2698 //--------------------------------------------------------------------------
2699
2700 static void
pl3cut(PLINT sx1,PLINT sy1,PLINT sx2,PLINT sy2,PLINT su1,PLINT sv1,PLINT su2,PLINT sv2,PLINT * cx,PLINT * cy)2701 pl3cut( PLINT sx1, PLINT sy1, PLINT sx2, PLINT sy2,
2702 PLINT su1, PLINT sv1, PLINT su2, PLINT sv2, PLINT *cx, PLINT *cy )
2703 {
2704 PLINT x21, y21, u21, v21, yv1, xu1, a, b;
2705 double fa, fb;
2706
2707 x21 = sx2 - sx1;
2708 y21 = sy2 - sy1;
2709 u21 = su2 - su1;
2710 v21 = sv2 - sv1;
2711 yv1 = sy1 - sv1;
2712 xu1 = sx1 - su1;
2713
2714 a = x21 * v21 - y21 * u21;
2715 fa = (double) a;
2716 if ( a == 0 )
2717 {
2718 if ( sx2 < su2 )
2719 {
2720 *cx = sx2;
2721 *cy = sy2;
2722 }
2723 else
2724 {
2725 *cx = su2;
2726 *cy = sv2;
2727 }
2728 return;
2729 }
2730 else
2731 {
2732 b = yv1 * u21 - xu1 * v21;
2733 fb = (double) b;
2734 *cx = (PLINT) ( sx1 + ( fb * x21 ) / fa + .5 );
2735 *cy = (PLINT) ( sy1 + ( fb * y21 ) / fa + .5 );
2736 }
2737 }
2738
2739 //--------------------------------------------------------------------------
2740 // void plRotationShear
2741 //
2742 // Calculates the rotation and shear angles from a plplot transformation matrix
2743 //
2744 // N.B. the plot transformation matrix
2745 //
2746 // [xFormMatrix[0] xFormMatrix[2]]
2747 // [xFormMatrix[1] xFormMatrix[3]]
2748 //
2749 // is calculated as
2750 //
2751 // [stride cos(t) stride sin(t)]
2752 // [sin(p-t) cos(p-t)]
2753 //
2754 // where t is the rotation angle and p is the shear angle.
2755 // The purpose of this routine is to determine stride, rotation angle,
2756 // and shear angle from xFormMatrix.
2757 //
2758 // For information only, xFormMatrix is the product of the following
2759 // rotation, skew(shear), and scale matrices:
2760 //
2761 // [stride 0] [1 0] [cos(t) sin(t)]
2762 // [0 cos(p)] [tan(p) 1] [-sin(t) cos(t)]
2763 //
2764 //--------------------------------------------------------------------------
2765
2766 void
plRotationShear(PLFLT * xFormMatrix,PLFLT * rotation,PLFLT * shear,PLFLT * stride)2767 plRotationShear( PLFLT *xFormMatrix, PLFLT *rotation, PLFLT *shear, PLFLT *stride )
2768 {
2769 PLFLT smr;
2770 *stride = sqrt( xFormMatrix[0] * xFormMatrix[0] + xFormMatrix[2] * xFormMatrix[2] );
2771
2772 // Calculate rotation in range from -pi to pi.
2773 *rotation = atan2( xFormMatrix[2], xFormMatrix[0] );
2774
2775 // Calculate shear - rotation in range from -pi to pi.
2776 smr = atan2( xFormMatrix[1], xFormMatrix[3] );
2777
2778 // Calculate shear in range from -2 pi to 2 pi.
2779 *shear = smr + *rotation;
2780
2781 // Calculate shear in range from -pi to pi.
2782 if ( *shear < -PI )
2783 *shear += 2. * PI;
2784 else if ( *shear > PI )
2785 *shear -= 2. * PI;
2786
2787 // Actually must honour some convention to calculate the negative
2788 // of the shear angle instead of the shear angle. Why??
2789 *shear = -*shear;
2790 // Comment out the modified old logic which determines the negative
2791 // of the shear angle in a more complicated way. Note, the modification
2792 // to divide the asin argument by *stride which solved a long-standing
2793 // bug (as does the above logic in a simpler way).
2794 //
2795 //shear = -asin( (xFormMatrix[0] * xFormMatrix[1] +
2796 // xFormMatrix[2] * xFormMatrix[3] )/ *stride);
2797 //
2798
2799 // Compute the cross product of the vectors [1,0] and [0,1] to
2800 // determine if we need to make a "quadrant 3,4" adjustment
2801 // to the shear angle.
2802
2803 //
2804 // if ( xFormMatrix[0] * xFormMatrix[3] - xFormMatrix[1] * xFormMatrix[2] < 0.0 )
2805 // {
2806 //shear = -( M_PI + *shear );
2807 // }
2808 //
2809 }
2810
2811