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