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