r184: *** 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.10 2000/08/25 15:59:13 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 RGBColor SGP::s_aRGBColor[] =
33 {
34   RGBColor (0, 0, 0),
35   RGBColor (0, 0, 128),
36   RGBColor (0, 128, 0),
37   RGBColor (0, 128, 128),
38   RGBColor (128, 0, 0),
39   RGBColor (128, 0, 128),
40   RGBColor (128, 128, 0),
41   RGBColor (80, 80, 80),
42   RGBColor (160, 160, 160),
43   RGBColor (0, 0, 255),
44   RGBColor (0, 255, 0),
45   RGBColor (0, 255, 255),
46   RGBColor (255, 0, 0),
47   RGBColor (255, 0, 255),
48   RGBColor (255, 255, 0),
49   RGBColor (255, 255, 255),
50 };
51
52 int SGP::s_iRGBColorCount = sizeof(s_aRGBColor) / sizeof(class RGBColor);
53
54 #ifdef HAVE_WXWINDOWS
55 SGPDriver::SGPDriver (wxDC* pDC, int xsize = 640, int ysize = 480)
56   : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_idDriver(0)
57 {
58   m_pDC = pDC;
59   m_idDriver |= SGPDRIVER_WXWINDOWS;
60 }
61 #endif
62
63 SGPDriver::SGPDriver (const char* szWinTitle = "", int xsize = 640, int ysize = 480)
64   : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_sWindowTitle(szWinTitle), m_idDriver(0)
65 {
66 #ifdef HAVE_G2_H
67   m_idG2 = g2_open_X11X (m_iPhysicalXSize, m_iPhysicalYSize, 10, 10, const_cast<char*>(szWinTitle), const_cast<char*>(szWinTitle), NULL, -1, -1);
68   m_idDriver |= SGPDRIVER_G2;
69 #endif
70 }
71
72 SGPDriver::~SGPDriver ()
73 {
74   if (isG2()) 
75     g2_close (m_idG2);
76 }
77
78
79 // NAME
80 //   SGP::SGP        Constructor for Simple Graphics Package
81
82 SGP::SGP (const SGPDriver& driver)
83     : m_driver (driver)
84 #if HAVE_WXWINDOWS
85     , m_pPen(NULL)
86 #endif
87 {
88   m_iPhysicalXSize = m_driver.getPhysicalXSize();
89   m_iPhysicalYSize = m_driver.getPhysicalYSize();
90
91   wc_to_ndc.setIdentity ();
92   mc_to_ndc.setIdentity();
93   ndc_to_mc.setIdentity();
94   m_ctm.setIdentity();
95
96   setWindow (0., 0., 1., 1.);
97   setViewport (0., 0., 1., 1.);
98   moveAbs (0., 0.);
99   stylusNDC (0., 0., false);
100  
101   setTextAngle (0.);
102   setTextSize (1. / 25.);
103   setColor (C_BLACK);
104 }
105
106
107 void
108 SGP::stylusNDC (double x, double y, bool beam)
109 {
110   int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
111   int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
112   if (m_driver.isWX())
113     yp = m_iPhysicalYSize - yp;
114
115   if (beam) {
116 #if HAVE_WXWINDOWS
117       if (m_driver.isWX())
118           m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
119 #endif
120 #if HAVE_G2_H
121       if (m_driver.isG2())
122           g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
123 #endif
124   }
125   m_iCurrentPhysicalX = xp;
126   m_iCurrentPhysicalY = yp;
127 }
128
129 void
130 SGP::markerNDC (double x, double y)
131 {
132 }
133
134 void
135 SGP::pointNDC (double x, double y)
136 {
137 }
138
139
140 // NAME
141 //    clear     Clear Window
142
143 void 
144 SGP::eraseWindow ()
145 {
146 #if HAVE_G2_H
147   if (m_driver.isG2())
148     g2_clear (m_driver.idG2());
149 #endif
150 #if HAVE_WXWINDOWS
151   if (m_driver.isWX())
152     m_driver.idWX()->Clear();
153 #endif
154 }
155
156 // NAME
157 //      sgp2_window             Set window in world coordinates
158  
159
160 void
161 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
162 {
163   if (xmin >= xmax || ymin >= ymax) {
164     sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
165     return;
166   }
167   
168   xw_min = xmin;
169   yw_min = ymin;
170   xw_max = xmax;
171   yw_max = ymax;
172   m_bRecalcTransform = true;
173 }
174
175
176 // NAME
177 //      sgp2_viewport                   Set viewport in NDC
178
179 void
180 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
181 {
182   if (xmin >= xmax || ymin >= ymax) {
183     sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
184     return;
185   }
186     
187   xv_min = xmin;
188   yv_min = ymin;
189   xv_max = xmax;
190   yv_max = ymax;
191   m_bRecalcTransform = true;
192   
193   viewNDC[0] = xmin;                    // Array for clip_rect() 
194   viewNDC[1] = ymin;
195   viewNDC[2] = xmax;
196   viewNDC[3] = ymax;
197 }
198
199
200 // NAME
201 //      frameViewport           draw box around viewport
202
203 void
204 SGP::frameViewport (void)
205 {
206   stylusNDC (xv_min, yv_min, 0);
207   stylusNDC (xv_max, yv_min, 1);
208   stylusNDC (xv_max, yv_max, 1);
209   stylusNDC (xv_min, yv_max, 1);
210   stylusNDC (xv_min, yv_min, 1);
211 }
212
213 void
214 SGP::setTextColor (int iFGcolor, int iBGcolor)
215 {
216 }
217
218 void 
219 SGP::setColor (int icol)
220 {
221   if (icol >= 0 && icol < s_iRGBColorCount) {
222 #if HAVE_G2_H
223     if (m_driver.isG2()) {
224       int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
225       g2_pen (m_driver.idG2(), iInk);
226     }
227 #endif
228 #if HAVE_WXWINDOWS
229     if (m_driver.isWX()) {
230       wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
231       delete m_pPen;
232       m_pPen = new wxPen (colour, 1, wxSOLID);
233       m_driver.idWX()->SetPen (*m_pPen);
234     }
235 #endif
236   }
237 }
238
239 void
240 SGP::setRasterOp (int ro)
241 {
242 #if HAVE_WXWINDOWS
243   if (m_driver.isWX()) {
244     int wxFxn = -1;
245     switch (ro) {
246     case RO_AND:
247       wxFxn = wxAND;
248       break;
249     case RO_AND_INVERT:
250       wxFxn = wxAND_INVERT;
251       break;
252     case RO_AND_REVERSE:
253       wxFxn = wxAND_REVERSE;
254       break;
255     case RO_CLEAR:
256       wxFxn = wxCLEAR;
257       break;
258     case RO_COPY:
259       wxFxn = wxCOPY;
260       break;
261     case RO_EQUIV:
262       wxFxn = wxEQUIV;
263       break;
264     case RO_INVERT:
265       wxFxn = wxINVERT;
266       break;
267     case RO_NAND:
268       wxFxn = wxNAND;
269       break;
270     case RO_NOR:
271       wxFxn = wxNOR;
272       break;
273     case RO_NO_OP:
274       wxFxn = wxNO_OP;
275       break;
276     case RO_OR:
277       wxFxn = wxOR;
278       break;
279     case RO_OR_INVERT:
280       wxFxn = wxOR_INVERT;
281       break;
282     case RO_OR_REVERSE:
283       wxFxn = wxOR_REVERSE;
284       break;
285     case RO_SET:
286       wxFxn = wxSET;
287       break;
288     case RO_SRC_INVERT:
289       wxFxn = wxSRC_INVERT;
290       break;
291     case RO_XOR:
292       wxFxn = wxXOR;
293       break;
294     }
295     if (wxFxn >= 0)
296       m_driver.idWX()->SetLogicalFunction (wxFxn);
297   }
298 #endif
299 }
300
301
302 void
303 SGP::setMarker (int idMarke, int iColor)
304 {
305 }
306
307 //==============================================================
308 // set line style.  Pass 16 bit repeating pattern               
309 //==============================================================
310 void 
311 SGP::setLineStyle (int style)
312 {
313 }
314
315 //==============================================================
316 // absolute draw to                                             
317 //*==============================================================
318
319 void 
320 SGP::lineAbs (double x, double y)
321 {
322   if (m_bRecalcTransform)
323     calc_transform();
324
325   double x1 = m_dCurrentWorldX;
326   double y1 = m_dCurrentWorldY;
327   mc_to_ndc.transformPoint (&x1, &y1);
328
329   double x2 = x;
330   double y2 = y;
331   mc_to_ndc.transformPoint (&x2, &y2);
332   
333   if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport 
334     stylusNDC (x1, y1, 0);  // move to first point
335     stylusNDC (x2, y2, 1);  // draw to second point 
336   }
337
338   m_dCurrentWorldX = x;
339   m_dCurrentWorldY = y;
340 }
341
342 void 
343 SGP::moveAbs (double x, double y)
344 {
345     m_dCurrentWorldX = x;
346     m_dCurrentWorldY = y;                       /* moves are not clipped */
347 }
348
349 void 
350 SGP::lineRel (double x, double y)
351 {
352   lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
353 }
354
355 void 
356 SGP::moveRel (double x, double y)
357 {
358   moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
359 }
360
361 void
362 SGP::setTextSize (double height)
363 {
364   if (m_driver.isG2())
365     g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
366 }
367
368 void
369 SGP::setTextAngle (double angle)
370 {
371   m_dTextAngle = angle;
372 }
373
374 void 
375 SGP::polylineAbs (double x[], double y[], int n)
376 {
377   if (m_bRecalcTransform)
378     calc_transform();
379
380   double x1 = x[0], y1 = y[0];
381   mc_to_ndc.transformPoint (&x1, &y1);
382   double x2 = x[1], y2 = y[1];
383   mc_to_ndc.transformPoint (&x2, &y2);
384
385   double xt = x2;       // don't pass (x2,y2) to clip, we need them 
386   double yt = y2;       // as the beginning point of the next line 
387
388   if (clip_rect (x1, y1, xt, yt, viewNDC)) {
389     stylusNDC (x1, y1, 0);
390     stylusNDC (xt, yt, 1);
391   }
392   
393   for (int i = 2; i < n; i++) {
394     x1 = x2; y1 = y2;                   // NDC endpoint of last line 
395     x2 = x[i];  y2 = y[i];
396     mc_to_ndc.transformPoint (&x2, &y2);
397     xt = x2;
398     yt = y2;
399     if (clip_rect (x1, y1, xt, yt, viewNDC)) {
400       stylusNDC (x1, y1, 0);
401       stylusNDC (xt, yt, 1);
402     }
403   }
404 }
405
406
407 void 
408 SGP::markerAbs (double x, double y)
409 {
410   if (m_bRecalcTransform)
411     calc_transform();
412
413   double xndc = x;
414   double yndc = y;
415   mc_to_ndc.transformPoint (&xndc, &yndc);
416   markerNDC (xndc, yndc); 
417   stylusNDC (xndc, yndc, false);            // move to location 
418   m_dCurrentWorldX = x;
419   m_dCurrentWorldY = y;
420 }
421
422
423 void 
424 SGP::markerRel (double x, double y)
425 {
426   markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
427 }
428
429
430 void 
431 SGP::pointAbs (double x, double y)
432 {
433   if (m_bRecalcTransform)
434     calc_transform();
435   double xndc = x, yndc = y;
436   mc_to_ndc.transformPoint (&xndc, &yndc);
437   pointNDC (xndc, yndc);
438   stylusNDC (xndc, yndc, false);            // move to location 
439   m_dCurrentWorldX = x;
440   m_dCurrentWorldY = y;
441 }
442
443
444 void 
445 SGP::pointRel (double x, double y)
446 {
447   pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
448 }
449
450
451 void
452 SGP::drawText (const string& rsMessage)
453 {
454   drawText (rsMessage.c_str());
455 }
456
457 void 
458 SGP::drawText (const char *pszMessage)
459 {
460   if (m_bRecalcTransform)
461     calc_transform();
462
463   double xndc = m_dCurrentWorldX;
464   double yndc = m_dCurrentWorldY;
465   mc_to_ndc.transformPoint (&xndc, &yndc);
466
467   stylusNDC (xndc, yndc, false);            // move to location 
468
469 #if HAVE_G2_H
470   if (m_driver.isG2()) {
471     g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
472   }
473 #endif
474 #if HAVE_WXWINDOWS
475   if (m_driver.isWX()) {
476     wxString str (pszMessage);
477     m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
478   }
479 #endif
480 }
481
482
483 // NAME
484 //   drawRect                           Draw box in graphics mode
485 //
486 // SYNOPSIS
487 //   drawbox (xmin, ymin, xmax, ymax)
488 //   double xmin, ymin                  Lower left corner of box
489 //   double xmax, ymax                  Upper left corner of box
490 //
491 // NOTES
492 //   This routine leaves the current position of graphic cursor at lower
493 //   left corner of box.
494
495 void
496 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
497 {
498         moveAbs (xmin, ymin);
499         lineAbs (xmax, ymin);
500         lineAbs (xmax, ymax);
501         lineAbs (xmin, ymax);
502         lineAbs (xmin, ymin);
503 }
504
505 // FUNCTION
506 // sgp2_circle - draw circle of radius r at current center              
507  
508 void 
509 SGP::drawCircle (const double r)
510 {
511         drawArc (0.0, 7.0, r);
512 }
513
514 // =============================================================
515 // draw arc around current center.  pass angles and radius      
516 //==============================================================
517
518 void 
519 SGP::drawArc (double start, double stop, const double r)
520 {
521   if ((stop-start) > 2 * PI)
522     stop = start + 2 * PI;
523   if ((start-stop) > 2 * PI)
524     stop = start + 2 * PI;
525   while (start >= stop)
526     stop += 2*PI;
527
528   double x = r * cos ((double) start);
529   double y = r * sin ((double) start);
530   moveRel (x, y);          // move from center to start of arc 
531
532   double theta = 5 * PI / 180;
533   double c = cos(theta);
534   double s = sin(theta);
535
536   double angle, xp, yp;
537   for (angle = start; angle < stop - theta; angle += theta) {
538     xp = c * x - s * y;
539     yp = s * x + c * y;
540     lineRel (xp - x, yp - y);
541     x = xp; y = yp;
542   }
543
544   c = cos (stop - angle);
545   s = sin (stop - angle);
546   xp = c * x - s * y;
547   yp = s * x + c * y;
548   lineRel (xp - x, yp - y);
549
550   x = r * cos ((double) stop);
551   y = r * sin ((double) stop);
552   moveRel (-x, -y);             // move back to center of circle 
553 }
554
555
556
557 ///////////////////////////////////////////////////////////////////////
558 // Coordinate Transformations
559 ///////////////////////////////////////////////////////////////////////
560
561
562 void
563 SGP::transformNDCtoMC (double* x, double* y)
564 {
565   if (m_bRecalcTransform)
566     calc_transform();
567   ndc_to_mc.transformPoint (x, y);
568 }
569
570
571 void
572 SGP::transformMCtoNDC (double* x, double* y)
573 {
574   if (m_bRecalcTransform)
575     calc_transform();
576   mc_to_ndc.transformPoint (x, y);
577 }
578
579
580 void
581 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
582 {
583   if (m_bRecalcTransform)
584     calc_transform();
585   *x = xIn;
586   *y = yIn;
587   mc_to_ndc.transformPoint (x, y);
588 }
589
590
591 // NAME
592 //      calc_transform                  Calculate transform matrices
593
594 void
595 SGP::calc_transform ()
596 {
597   double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
598   double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
599     
600   wc_to_ndc.setIdentity();
601   wc_to_ndc.mtx[0][0] = scaleX;
602   wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
603   wc_to_ndc.mtx[1][1] = scaleY;
604   wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
605
606   mc_to_ndc = m_ctm * wc_to_ndc;
607   ndc_to_mc = mc_to_ndc.invert();
608
609   m_bRecalcTransform = false;
610 }
611
612 void
613 SGP::ctmClear ()
614 {
615   m_ctm.setIdentity();
616   calc_transform();
617 }
618
619 void 
620 SGP::ctmSet (const TransformationMatrix2D& m)
621 {
622   m_ctm = m;
623   calc_transform();
624 }
625
626
627 void
628 SGP::preTranslate  (double x, double y)
629 {
630     TransformationMatrix2D m;
631
632     m.setTranslate (x, y);
633     ctmSet (m * m_ctm);
634 }
635
636
637 void 
638 SGP::postTranslate (double x, double y)
639 {
640     TransformationMatrix2D m;
641
642     m.setTranslate (x, y);
643     ctmSet (m_ctm * m);
644 }
645
646
647 void 
648 SGP::preScale (double sx, double sy)
649 {
650     TransformationMatrix2D m;
651
652     m.setScale (sx, sy);
653     ctmSet (m * m_ctm);
654 }
655
656
657 void 
658 SGP::postScale (double sx, double sy)
659 {
660     TransformationMatrix2D m;
661
662     m.setScale (sx, sy);
663     m_ctm = m_ctm * m;
664     ctmSet (m_ctm * m);
665 }
666
667
668 void 
669 SGP::preRotate (double theta)
670 {
671     TransformationMatrix2D m;
672
673     m.setRotate (theta);
674     m_ctm = m * m_ctm;
675     ctmSet (m * m_ctm);
676 }
677
678
679 void 
680 SGP::postRotate (double theta)
681 {
682     TransformationMatrix2D m;
683
684     m.setRotate (theta);
685     ctmSet (m_ctm * m);
686 }
687
688
689 void 
690 SGP::preShear (double shrx, double shry)
691 {
692     TransformationMatrix2D m;
693
694     m.setShear (shrx, shry);
695     ctmSet (m * m_ctm);
696 }
697
698
699 void 
700 SGP::postShear (double shrx, double shry)
701 {
702     TransformationMatrix2D m;
703
704     m.setShear (shrx, shry);
705     ctmSet (m_ctm * m);
706 }
707
708
709 ////////////////////////////////////////////////////////////////////////
710 //  Bitmap Markers
711 ////////////////////////////////////////////////////////////////////////
712
713 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
714 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] = 
715 {
716     {'\000', '\000', '\010', '\000', '\000'},    // small dot 
717     {'\000', '\034', '\024', '\034', '\000'},    // empty square 
718     {'\000', '\034', '\034', '\034', '\000'},    // filled square 
719     {'\000', '\010', '\024', '\010', '\000'},    // empty diamond 
720     {'\000', '\010', '\034', '\010', '\000'},    // filled diamond 
721     {'\010', '\010', '\076', '\010', '\010'},    // cross 
722     {'\000', '\024', '\010', '\024', '\000'},    // X 
723     {'\034', '\042', '\042', '\042', '\034'},    // open circle 
724     {'\034', '\076', '\076', '\076', '\034'},    // filled circle 
725     {'\076', '\042', '\042', '\042', '\076'},    // big open square 
726     {'\010', '\024', '\042', '\024', '\010'},    // big open diamond 
727 };