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