1062901737201670c32db960935990d70bc26d5e
[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.8 2000/07/31 14:48:35 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, const char* szWinTitle = "", int xsize = 640, int ysize = 480)
56   : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_sWindowTitle(szWinTitle), 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::setMarker (int idMarke, int iColor)
237 {
238 }
239
240 //==============================================================
241 // set line style.  Pass 16 bit repeating pattern               
242 //==============================================================
243 void 
244 SGP::setLineStyle (int style)
245 {
246 }
247
248 //==============================================================
249 // absolute draw to                                             
250 //*==============================================================
251
252 void 
253 SGP::lineAbs (double x, double y)
254 {
255   if (m_bRecalcTransform)
256     calc_transform();
257
258   double x1 = m_dCurrentWorldX;
259   double y1 = m_dCurrentWorldY;
260   mc_to_ndc.transformPoint (&x1, &y1);
261
262   double x2 = x;
263   double y2 = y;
264   mc_to_ndc.transformPoint (&x2, &y2);
265   
266   if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport 
267     stylusNDC (x1, y1, 0);  // move to first point
268     stylusNDC (x2, y2, 1);  // draw to second point 
269   }
270
271   m_dCurrentWorldX = x;
272   m_dCurrentWorldY = y;
273 }
274
275 void 
276 SGP::moveAbs (double x, double y)
277 {
278     m_dCurrentWorldX = x;
279     m_dCurrentWorldY = y;                       /* moves are not clipped */
280 }
281
282 void 
283 SGP::lineRel (double x, double y)
284 {
285   lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
286 }
287
288 void 
289 SGP::moveRel (double x, double y)
290 {
291   moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
292 }
293
294 void
295 SGP::setTextSize (double height)
296 {
297   if (m_driver.isG2())
298     g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
299 }
300
301 void
302 SGP::setTextAngle (double angle)
303 {
304   m_dTextAngle = angle;
305 }
306
307 void 
308 SGP::polylineAbs (double x[], double y[], int n)
309 {
310   if (m_bRecalcTransform)
311     calc_transform();
312
313   double x1 = x[0], y1 = y[0];
314   mc_to_ndc.transformPoint (&x1, &y1);
315   double x2 = x[1], y2 = y[1];
316   mc_to_ndc.transformPoint (&x2, &y2);
317
318   double xt = x2;       // don't pass (x2,y2) to clip, we need them 
319   double yt = y2;       // as the beginning point of the next line 
320
321   if (clip_rect (x1, y1, xt, yt, viewNDC)) {
322     stylusNDC (x1, y1, 0);
323     stylusNDC (xt, yt, 1);
324   }
325   
326   for (int i = 2; i < n; i++) {
327     x1 = x2; y1 = y2;                   // NDC endpoint of last line 
328     x2 = x[i];  y2 = y[i];
329     mc_to_ndc.transformPoint (&x2, &y2);
330     xt = x2;
331     yt = y2;
332     if (clip_rect (x1, y1, xt, yt, viewNDC)) {
333       stylusNDC (x1, y1, 0);
334       stylusNDC (xt, yt, 1);
335     }
336   }
337 }
338
339
340 void 
341 SGP::markerAbs (double x, double y)
342 {
343   if (m_bRecalcTransform)
344     calc_transform();
345
346   double xndc = x;
347   double yndc = y;
348   mc_to_ndc.transformPoint (&xndc, &yndc);
349   markerNDC (xndc, yndc); 
350   stylusNDC (xndc, yndc, false);            // move to location 
351   m_dCurrentWorldX = x;
352   m_dCurrentWorldY = y;
353 }
354
355
356 void 
357 SGP::markerRel (double x, double y)
358 {
359   markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
360 }
361
362
363 void 
364 SGP::pointAbs (double x, double y)
365 {
366   if (m_bRecalcTransform)
367     calc_transform();
368   double xndc = x, yndc = y;
369   mc_to_ndc.transformPoint (&xndc, &yndc);
370   pointNDC (xndc, yndc);
371   stylusNDC (xndc, yndc, false);            // move to location 
372   m_dCurrentWorldX = x;
373   m_dCurrentWorldY = y;
374 }
375
376
377 void 
378 SGP::pointRel (double x, double y)
379 {
380   pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
381 }
382
383
384 void
385 SGP::drawText (const string& rsMessage)
386 {
387   drawText (rsMessage.c_str());
388 }
389
390 void 
391 SGP::drawText (const char *pszMessage)
392 {
393   if (m_bRecalcTransform)
394     calc_transform();
395
396   double xndc = m_dCurrentWorldX;
397   double yndc = m_dCurrentWorldY;
398   mc_to_ndc.transformPoint (&xndc, &yndc);
399
400   stylusNDC (xndc, yndc, false);            // move to location 
401
402 #if HAVE_G2_H
403   if (m_driver.isG2()) {
404     g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
405   }
406 #endif
407 #if HAVE_WXWINDOWS
408   if (m_driver.isWX()) {
409     wxString str (pszMessage);
410     m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
411   }
412 #endif
413 }
414
415
416 // NAME
417 //   drawRect                           Draw box in graphics mode
418 //
419 // SYNOPSIS
420 //   drawbox (xmin, ymin, xmax, ymax)
421 //   double xmin, ymin                  Lower left corner of box
422 //   double xmax, ymax                  Upper left corner of box
423 //
424 // NOTES
425 //   This routine leaves the current position of graphic cursor at lower
426 //   left corner of box.
427
428 void
429 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
430 {
431         moveAbs (xmin, ymin);
432         lineAbs (xmax, ymin);
433         lineAbs (xmax, ymax);
434         lineAbs (xmin, ymax);
435         lineAbs (xmin, ymin);
436 }
437
438 // FUNCTION
439 // sgp2_circle - draw circle of radius r at current center              
440  
441 void 
442 SGP::drawCircle (const double r)
443 {
444         drawArc (0.0, 7.0, r);
445 }
446
447 // =============================================================
448 // draw arc around current center.  pass angles and radius      
449 //==============================================================
450
451 void 
452 SGP::drawArc (double start, double stop, const double r)
453 {
454   if ((stop-start) > 2 * PI)
455     stop = start + 2 * PI;
456   if ((start-stop) > 2 * PI)
457     stop = start + 2 * PI;
458   while (start >= stop)
459     stop += 2*PI;
460
461   double x = r * cos ((double) start);
462   double y = r * sin ((double) start);
463   moveRel (x, y);          // move from center to start of arc 
464
465   double theta = 5 * PI / 180;
466   double c = cos(theta);
467   double s = sin(theta);
468
469   double angle, xp, yp;
470   for (angle = start; angle < stop - theta; angle += theta) {
471     xp = c * x - s * y;
472     yp = s * x + c * y;
473     lineRel (xp - x, yp - y);
474     x = xp; y = yp;
475   }
476
477   c = cos (stop - angle);
478   s = sin (stop - angle);
479   xp = c * x - s * y;
480   yp = s * x + c * y;
481   lineRel (xp - x, yp - y);
482
483   x = r * cos ((double) stop);
484   y = r * sin ((double) stop);
485   moveRel (-x, -y);             // move back to center of circle 
486 }
487
488
489
490 ///////////////////////////////////////////////////////////////////////
491 // Coordinate Transformations
492 ///////////////////////////////////////////////////////////////////////
493
494
495 void
496 SGP::transformNDCtoMC (double* x, double* y)
497 {
498   if (m_bRecalcTransform)
499     calc_transform();
500   ndc_to_mc.transformPoint (x, y);
501 }
502
503
504 void
505 SGP::transformMCtoNDC (double* x, double* y)
506 {
507   if (m_bRecalcTransform)
508     calc_transform();
509   mc_to_ndc.transformPoint (x, y);
510 }
511
512
513 void
514 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
515 {
516   if (m_bRecalcTransform)
517     calc_transform();
518   *x = xIn;
519   *y = yIn;
520   mc_to_ndc.transformPoint (x, y);
521 }
522
523
524 // NAME
525 //      calc_transform                  Calculate transform matrices
526
527 void
528 SGP::calc_transform ()
529 {
530   double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
531   double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
532     
533   wc_to_ndc.setIdentity();
534   wc_to_ndc.mtx[0][0] = scaleX;
535   wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
536   wc_to_ndc.mtx[1][1] = scaleY;
537   wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
538
539   mc_to_ndc = m_ctm * wc_to_ndc;
540   ndc_to_mc = mc_to_ndc.invert();
541
542   m_bRecalcTransform = false;
543 }
544
545 void
546 SGP::ctmClear ()
547 {
548   m_ctm.setIdentity();
549   calc_transform();
550 }
551
552 void 
553 SGP::ctmSet (const TransformationMatrix2D& m)
554 {
555   m_ctm = m;
556   calc_transform();
557 }
558
559
560 void
561 SGP::preTranslate  (double x, double y)
562 {
563     TransformationMatrix2D m;
564
565     m.setTranslate (x, y);
566     ctmSet (m * m_ctm);
567 }
568
569
570 void 
571 SGP::postTranslate (double x, double y)
572 {
573     TransformationMatrix2D m;
574
575     m.setTranslate (x, y);
576     ctmSet (m_ctm * m);
577 }
578
579
580 void 
581 SGP::preScale (double sx, double sy)
582 {
583     TransformationMatrix2D m;
584
585     m.setScale (sx, sy);
586     ctmSet (m * m_ctm);
587 }
588
589
590 void 
591 SGP::postScale (double sx, double sy)
592 {
593     TransformationMatrix2D m;
594
595     m.setScale (sx, sy);
596     m_ctm = m_ctm * m;
597     ctmSet (m_ctm * m);
598 }
599
600
601 void 
602 SGP::preRotate (double theta)
603 {
604     TransformationMatrix2D m;
605
606     m.setRotate (theta);
607     m_ctm = m * m_ctm;
608     ctmSet (m * m_ctm);
609 }
610
611
612 void 
613 SGP::postRotate (double theta)
614 {
615     TransformationMatrix2D m;
616
617     m.setRotate (theta);
618     ctmSet (m_ctm * m);
619 }
620
621
622 void 
623 SGP::preShear (double shrx, double shry)
624 {
625     TransformationMatrix2D m;
626
627     m.setShear (shrx, shry);
628     ctmSet (m * m_ctm);
629 }
630
631
632 void 
633 SGP::postShear (double shrx, double shry)
634 {
635     TransformationMatrix2D m;
636
637     m.setShear (shrx, shry);
638     ctmSet (m_ctm * m);
639 }
640
641
642 ////////////////////////////////////////////////////////////////////////
643 //  Bitmap Markers
644 ////////////////////////////////////////////////////////////////////////
645
646 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
647 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] = 
648 {
649     {'\000', '\000', '\010', '\000', '\000'},    // small dot 
650     {'\000', '\034', '\024', '\034', '\000'},    // empty square 
651     {'\000', '\034', '\034', '\034', '\000'},    // filled square 
652     {'\000', '\010', '\024', '\010', '\000'},    // empty diamond 
653     {'\000', '\010', '\034', '\010', '\000'},    // filled diamond 
654     {'\010', '\010', '\076', '\010', '\010'},    // cross 
655     {'\000', '\024', '\010', '\024', '\000'},    // X 
656     {'\034', '\042', '\042', '\042', '\034'},    // open circle 
657     {'\034', '\076', '\076', '\076', '\034'},    // filled circle 
658     {'\076', '\042', '\042', '\042', '\076'},    // big open square 
659     {'\010', '\024', '\042', '\024', '\010'},    // big open diamond 
660 };