r162: *** empty log message ***
[ctsim.git] / libctgraphics / sgp.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **      Name:       sgp.cpp             Simple Graphics Package
5 **      Programmer: Kevin Rosenberg
6 **
7 **  This is part of the CTSim program
8 **  Copyright (C) 1983-2000 Kevin Rosenberg
9 **
10 **  $Id: sgp.cpp,v 1.5 2000/07/28 08:28:08 kevin Exp $
11 **
12 **  This program is free software; you can redistribute it and/or modify
13 **  it under the terms of the GNU General Public License (version 2) as
14 **  published by the Free Software Foundation.
15 **
16 **  This program 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 General Public License for more details.
20 **
21 **  You should have received a copy of the GNU General Public License
22 **  along with this program; if not, write to the Free Software
23 **  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24 ******************************************************************************/
25
26 #include <stdio.h>
27 #include <math.h>
28 #include "ctsupport.h"
29 #include "sgp.h"
30
31
32
33 #ifdef HAVE_WXWINDOWS
34 SGPDriver::SGPDriver (wxDC* pDC, const char* szWinTitle = "", int xsize = 640, int ysize = 480)
35   : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_sWindowTitle(szWinTitle), m_idDriver(0)
36 {
37   m_pDC = pDC;
38   m_idDriver |= SGPDRIVER_WXWINDOWS;
39 }
40 #endif
41
42 SGPDriver::SGPDriver (const char* szWinTitle = "", int xsize = 640, int ysize = 480)
43   : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_sWindowTitle(szWinTitle), m_idDriver(0)
44 {
45 #ifdef HAVE_G2_H
46   m_idG2 = g2_open_X11X (m_iPhysicalXSize, m_iPhysicalYSize, 10, 10, const_cast<char*>(szWinTitle), const_cast<char*>(szWinTitle), NULL, -1, -1);
47   m_idDriver |= SGPDRIVER_G2;
48 #endif
49 }
50
51 SGPDriver::~SGPDriver ()
52 {
53   if (isG2()) 
54     g2_close (m_idG2);
55 }
56
57
58 // NAME
59 //   SGP::SGP        Constructor for Simple Graphics Package
60
61 SGP::SGP (const SGPDriver& driver)
62     : m_driver (driver)
63 {
64   m_iPhysicalXSize = m_driver.getPhysicalXSize();
65   m_iPhysicalYSize = m_driver.getPhysicalYSize();
66
67   wc_to_ndc.setIdentity ();
68   mc_to_ndc.setIdentity();
69   ndc_to_mc.setIdentity();
70   m_ctm.setIdentity();
71
72   setWindow (0., 0., 1., 1.);
73   setViewport (0., 0., 1., 1.);
74   moveAbs (0., 0.);
75   m_iCurrentPhysicalX = 0;
76   m_iCurrentPhysicalY = 0;
77  
78   setTextAngle (0.);
79   setTextSize (1. / 25.);
80 }
81
82
83 void
84 SGP::stylusNDC (double x, double y, bool beam)
85 {
86   int xp = static_cast<int>(x * m_iPhysicalXSize + 0.5);
87   int yp = static_cast<int>(y * m_iPhysicalYSize + 0.5);
88   if (beam) {
89       if (m_driver.isWX())
90           m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
91       if (m_driver.isG2())
92           g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
93   }
94   m_iCurrentPhysicalX = xp;
95   m_iCurrentPhysicalY = yp;
96 }
97
98 void
99 SGP::markerNDC (double x, double y)
100 {
101 }
102
103 void
104 SGP::pointNDC (double x, double y)
105 {
106 }
107
108
109 // NAME
110 //    clear     Clear Window
111
112 void 
113 SGP::eraseWindow ()
114 {
115   if (m_driver.isG2())
116     g2_clear (m_driver.idG2());
117   if (m_driver.isWX())
118     m_driver.idWX()->Clear();
119 }
120
121 // NAME
122 //      sgp2_window             Set window in world coordinates
123  
124
125 void
126 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
127 {
128   if (xmin >= xmax || ymin >= ymax) {
129     sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
130     return;
131   }
132   
133   xw_min = xmin;
134   yw_min = ymin;
135   xw_max = xmax;
136   yw_max = ymax;
137   m_bRecalcTransform = true;
138 }
139
140
141 // NAME
142 //      sgp2_viewport                   Set viewport in NDC
143
144 void
145 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
146 {
147   if (xmin >= xmax || ymin >= ymax) {
148     sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
149     return;
150   }
151     
152   xv_min = xmin;
153   yv_min = ymin;
154   xv_max = xmax;
155   yv_max = ymax;
156   m_bRecalcTransform = true;
157   
158   viewNDC[0] = xmin;                    // Array for clip_rect() 
159   viewNDC[1] = ymin;
160   viewNDC[2] = xmax;
161   viewNDC[3] = ymax;
162 }
163
164
165 // NAME
166 //      frameViewport           draw box around viewport
167
168 void
169 SGP::frameViewport (void)
170 {
171   stylusNDC (xv_min, yv_min, 0);
172   stylusNDC (xv_max, yv_min, 1);
173   stylusNDC (xv_max, yv_max, 1);
174   stylusNDC (xv_min, yv_max, 1);
175   stylusNDC (xv_min, yv_min, 1);
176 }
177
178 void
179 SGP::setTextColor (int iFGcolor, int iBGcolor)
180 {
181 }
182
183 void 
184 SGP::setColor (int icol)
185 {
186 }
187
188 void
189 SGP::setMarker (int idMarke, int iColor)
190 {
191 }
192
193 //==============================================================
194 // set line style.  Pass 16 bit repeating pattern               
195 //==============================================================
196 void 
197 SGP::setLineStyle (int style)
198 {
199 }
200
201 //==============================================================
202 // absolute draw to                                             
203 //*==============================================================
204
205 void 
206 SGP::lineAbs (double x, double y)
207 {
208   if (m_bRecalcTransform)
209     calc_transform();
210
211   double x1 = m_dCurrentWorldX;
212   double y1 = m_dCurrentWorldY;
213   mc_to_ndc.transformPoint (&x1, &y1);
214
215   double x2 = x;
216   double y2 = y;
217   mc_to_ndc.transformPoint (&x1, &y2);
218   
219   if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport 
220     stylusNDC (x1, y1, 0);  // move to first point
221     stylusNDC (x2, y2, 1);  // draw to second point 
222   }
223
224   m_dCurrentWorldX = x;
225   m_dCurrentWorldY = y;
226 }
227
228 void 
229 SGP::moveAbs (double x, double y)
230 {
231     m_dCurrentWorldX = x;
232     m_dCurrentWorldY = y;                       /* moves are not clipped */
233 }
234
235 void 
236 SGP::lineRel (double x, double y)
237 {
238   lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
239 }
240
241 void 
242 SGP::moveRel (double x, double y)
243 {
244   moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
245 }
246
247 void
248 SGP::setTextSize (double height)
249 {
250   if (m_driver.isG2())
251     g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
252 }
253
254 void
255 SGP::setTextAngle (double angle)
256 {
257   m_dTextAngle = angle;
258 }
259
260 void 
261 SGP::polylineAbs (double x[], double y[], int n)
262 {
263   if (m_bRecalcTransform)
264     calc_transform();
265
266   double x1 = x[0], y1 = y[0];
267   mc_to_ndc.transformPoint (&x1, &y1);
268   double x2 = x[1], y2 = y[1];
269   mc_to_ndc.transformPoint (&x2, &y2);
270
271   double xt = x2;       // don't pass (x2,y2) to clip, we need them 
272   double yt = y2;       // as the beginning point of the next line 
273
274   if (clip_rect (x1, y1, xt, yt, viewNDC)) {
275     stylusNDC (x1, y1, 0);
276     stylusNDC (xt, yt, 1);
277   }
278   
279   for (int i = 2; i < n; i++) {
280     x1 = x2; y1 = y2;                   // NDC endpoint of last line 
281     x2 = x[i];  y2 = y[i];
282     mc_to_ndc.transformPoint (&x2, &y2);
283     xt = x2;
284     yt = y2;
285     if (clip_rect (x1, y1, xt, yt, viewNDC)) {
286       stylusNDC (x1, y1, 0);
287       stylusNDC (xt, yt, 1);
288     }
289   }
290 }
291
292
293 void 
294 SGP::markerAbs (double x, double y)
295 {
296   if (m_bRecalcTransform)
297     calc_transform();
298
299   double xndc = x;
300   double yndc = y;
301   mc_to_ndc.transformPoint (&xndc, &yndc);
302   markerNDC (xndc, yndc); 
303   stylusNDC (xndc, yndc, false);            // move to location 
304   m_dCurrentWorldX = x;
305   m_dCurrentWorldY = y;
306 }
307
308
309 void 
310 SGP::markerRel (double x, double y)
311 {
312   markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
313 }
314
315
316 void 
317 SGP::pointAbs (double x, double y)
318 {
319   if (m_bRecalcTransform)
320     calc_transform();
321   double xndc = x, yndc = y;
322   mc_to_ndc.transformPoint (&xndc, &yndc);
323   pointNDC (xndc, yndc);
324   stylusNDC (xndc, yndc, false);            // move to location 
325   m_dCurrentWorldX = x;
326   m_dCurrentWorldY = y;
327 }
328
329
330 void 
331 SGP::pointRel (double x, double y)
332 {
333   pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
334 }
335
336
337 void
338 SGP::drawText (const string& rsMessage)
339 {
340   drawText (rsMessage.c_str());
341 }
342
343 void 
344 SGP::drawText (const char *pszMessage)
345 {
346   if (m_bRecalcTransform)
347     calc_transform();
348
349   double xndc = m_dCurrentWorldX;
350   double yndc = m_dCurrentWorldY;
351   mc_to_ndc.transformPoint (&xndc, &yndc);
352
353   stylusNDC (xndc, yndc, false);            // move to location 
354
355   if (m_driver.isG2()) {
356     if (m_dTextAngle == 0.)
357       g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
358   }
359   if (m_driver.isWX()) {
360   }
361 }
362
363
364 // NAME
365 //   drawRect                           Draw box in graphics mode
366 //
367 // SYNOPSIS
368 //   drawbox (xmin, ymin, xmax, ymax)
369 //   double xmin, ymin                  Lower left corner of box
370 //   double xmax, ymax                  Upper left corner of box
371 //
372 // NOTES
373 //   This routine leaves the current position of graphic cursor at lower
374 //   left corner of box.
375
376 void
377 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
378 {
379         moveAbs (xmin, ymin);
380         lineAbs (xmax, ymin);
381         lineAbs (xmax, ymax);
382         lineAbs (xmin, ymax);
383         lineAbs (xmin, ymin);
384 }
385
386 // FUNCTION
387 // sgp2_circle - draw circle of radius r at current center              
388  
389 void 
390 SGP::drawCircle (const double r)
391 {
392         drawArc (0.0, 7.0, r);
393 }
394
395 // =============================================================
396 // draw arc around current center.  pass angles and radius      
397 //==============================================================
398
399 void 
400 SGP::drawArc (double start, double stop, const double r)
401 {
402   if ((stop-start) > 2 * PI)
403     stop = start + 2 * PI;
404   if ((start-stop) > 2 * PI)
405     stop = start + 2 * PI;
406   while (start >= stop)
407     stop += 2*PI;
408
409   double x = r * cos ((double) start);
410   double y = r * sin ((double) start);
411   moveRel (x, y);          // move from center to start of arc 
412
413   double theta = 5 * PI / 180;
414   double c = cos(theta);
415   double s = sin(theta);
416
417   double angle, xp, yp;
418   for (angle = start; angle < stop - theta; angle += theta) {
419     xp = c * x - s * y;
420     yp = s * x + c * y;
421     lineRel (xp - x, yp - y);
422     x = xp; y = yp;
423   }
424
425   c = cos (stop - angle);
426   s = sin (stop - angle);
427   xp = c * x - s * y;
428   yp = s * x + c * y;
429   lineRel (xp - x, yp - y);
430
431   x = r * cos ((double) stop);
432   y = r * sin ((double) stop);
433   moveRel (-x, -y);             // move back to center of circle 
434 }
435
436
437
438 ///////////////////////////////////////////////////////////////////////
439 // Coordinate Transformations
440 ///////////////////////////////////////////////////////////////////////
441
442
443 void
444 SGP::transformNDCtoMC (double* x, double* y)
445 {
446   if (m_bRecalcTransform)
447     calc_transform();
448   ndc_to_mc.transformPoint (x, y);
449 }
450
451
452 void
453 SGP::transformMCtoNDC (double* x, double* y)
454 {
455   if (m_bRecalcTransform)
456     calc_transform();
457   mc_to_ndc.transformPoint (x, y);
458 }
459
460
461 void
462 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
463 {
464   if (m_bRecalcTransform)
465     calc_transform();
466   *x = xIn;
467   *y = yIn;
468   mc_to_ndc.transformPoint (x, y);
469 }
470
471
472 // NAME
473 //      calc_transform                  Calculate transform matrices
474
475 void
476 SGP::calc_transform ()
477 {
478   double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
479   double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
480     
481   wc_to_ndc.setIdentity();
482   wc_to_ndc.mtx[0][0] = scaleX;
483   wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
484   wc_to_ndc.mtx[1][1] = scaleY;
485   wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
486
487   mc_to_ndc = m_ctm * wc_to_ndc;
488   ndc_to_mc = mc_to_ndc.invert();
489
490   m_bRecalcTransform = false;
491 }
492
493 void
494 SGP::ctmClear ()
495 {
496   m_ctm.setIdentity();
497   calc_transform();
498 }
499
500 void 
501 SGP::ctmSet (const TransformationMatrix2D& m)
502 {
503   m_ctm = m;
504   calc_transform();
505 }
506
507
508 void
509 SGP::preTranslate  (double x, double y)
510 {
511     TransformationMatrix2D m;
512
513     m.setTranslate (x, y);
514     ctmSet (m * m_ctm);
515 }
516
517
518 void 
519 SGP::postTranslate (double x, double y)
520 {
521     TransformationMatrix2D m;
522
523     m.setTranslate (x, y);
524     ctmSet (m_ctm * m);
525 }
526
527
528 void 
529 SGP::preScale (double sx, double sy)
530 {
531     TransformationMatrix2D m;
532
533     m.setScale (sx, sy);
534     ctmSet (m * m_ctm);
535 }
536
537
538 void 
539 SGP::postScale (double sx, double sy)
540 {
541     TransformationMatrix2D m;
542
543     m.setScale (sx, sy);
544     m_ctm = m_ctm * m;
545     ctmSet (m_ctm * m);
546 }
547
548
549 void 
550 SGP::preRotate (double theta)
551 {
552     TransformationMatrix2D m;
553
554     m.setRotate (theta);
555     m_ctm = m * m_ctm;
556     ctmSet (m * m_ctm);
557 }
558
559
560 void 
561 SGP::postRotate (double theta)
562 {
563     TransformationMatrix2D m;
564
565     m.setRotate (theta);
566     ctmSet (m_ctm * m);
567 }
568
569
570 void 
571 SGP::preShear (double shrx, double shry)
572 {
573     TransformationMatrix2D m;
574
575     m.setShear (shrx, shry);
576     ctmSet (m * m_ctm);
577 }
578
579
580 void 
581 SGP::postShear (double shrx, double shry)
582 {
583     TransformationMatrix2D m;
584
585     m.setShear (shrx, shry);
586     ctmSet (m_ctm * m);
587 }
588
589
590 ////////////////////////////////////////////////////////////////////////
591 //  Bitmap Markers
592 ////////////////////////////////////////////////////////////////////////
593
594 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
595 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] = 
596 {
597     {'\000', '\000', '\010', '\000', '\000'},    // small dot 
598     {'\000', '\034', '\024', '\034', '\000'},    // empty square 
599     {'\000', '\034', '\034', '\034', '\000'},    // filled square 
600     {'\000', '\010', '\024', '\010', '\000'},    // empty diamond 
601     {'\000', '\010', '\034', '\010', '\000'},    // filled diamond 
602     {'\010', '\010', '\076', '\010', '\010'},    // cross 
603     {'\000', '\024', '\010', '\024', '\000'},    // X 
604     {'\034', '\042', '\042', '\042', '\034'},    // open circle 
605     {'\034', '\076', '\076', '\076', '\034'},    // filled circle 
606     {'\076', '\042', '\042', '\042', '\076'},    // big open square 
607     {'\010', '\024', '\042', '\024', '\010'},    // big open diamond 
608 };