r296: *** 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.21 2000/12/18 06:32: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, 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);
509   }
510 #endif
511   dHeight *= (yw_max - yw_min);
512   return dHeight;
513 }
514
515 double
516 SGP::getCharWidth ()
517 {
518   double dWidth = (1. / 80.);
519
520 #if HAVE_WXWINDOWS
521   if (m_driver.isWX()) {
522     dWidth = m_driver.idWX()->GetCharWidth();
523     dWidth /= static_cast<double>(m_iPhysicalXSize);
524   }
525 #endif
526   dWidth *= (xw_max - xw_min);
527   return dWidth;
528 }
529
530 void
531 SGP::setTextAngle (double angle)
532 {
533   m_dTextAngle = convertRadiansToDegrees(angle);
534 }
535
536 void 
537 SGP::polylineAbs (double x[], double y[], int n)
538 {
539   if (m_bRecalcTransform)
540     calc_transform();
541
542   double x1 = x[0], y1 = y[0];
543   mc_to_ndc.transformPoint (&x1, &y1);
544   double x2 = x[1], y2 = y[1];
545   mc_to_ndc.transformPoint (&x2, &y2);
546
547   double xt = x2;       // don't pass (x2,y2) to clip, we need them 
548   double yt = y2;       // as the beginning point of the next line 
549
550   if (clip_rect (x1, y1, xt, yt, viewNDC)) {
551     stylusNDC (x1, y1, false);
552     stylusNDC (xt, yt, true);
553   }
554   
555   for (int i = 2; i < n; i++) {
556     x1 = x2; y1 = y2;                   // NDC endpoint of last line 
557     x2 = x[i];  y2 = y[i];
558     mc_to_ndc.transformPoint (&x2, &y2);
559     xt = x2;
560     yt = y2;
561     if (clip_rect (x1, y1, xt, yt, viewNDC)) {
562       stylusNDC (x1, y1, false);
563       stylusNDC (xt, yt, true);
564     }
565   }
566 }
567
568
569 void 
570 SGP::markerAbs (double x, double y)
571 {
572   if (m_bRecalcTransform)
573     calc_transform();
574
575   double xndc = x;
576   double yndc = y;
577   mc_to_ndc.transformPoint (&xndc, &yndc);
578   markerNDC (xndc, yndc); 
579   stylusNDC (xndc, yndc, false);            // move to location 
580   m_dCurrentWorldX = x;
581   m_dCurrentWorldY = y;
582 }
583
584
585 void 
586 SGP::markerRel (double x, double y)
587 {
588   markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
589 }
590
591
592 void 
593 SGP::pointAbs (double x, double y)
594 {
595   if (m_bRecalcTransform)
596     calc_transform();
597   double xndc = x, yndc = y;
598   mc_to_ndc.transformPoint (&xndc, &yndc);
599   pointNDC (xndc, yndc);
600   stylusNDC (xndc, yndc, false);            // move to location 
601   m_dCurrentWorldX = x;
602   m_dCurrentWorldY = y;
603 }
604
605
606 void 
607 SGP::pointRel (double x, double y)
608 {
609   pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
610 }
611
612
613 void
614 SGP::drawText (const std::string& rsMessage)
615 {
616   drawText (rsMessage.c_str());
617 }
618
619 void 
620 SGP::drawText (const char *pszMessage)
621 {
622   if (m_bRecalcTransform)
623     calc_transform();
624
625   double xndc = m_dCurrentWorldX;
626   double yndc = m_dCurrentWorldY;
627   mc_to_ndc.transformPoint (&xndc, &yndc);
628
629   stylusNDC (xndc, yndc, false);            // move to location 
630
631 #if HAVE_G2_H
632   if (m_driver.isG2()) {
633     g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
634   }
635 #endif
636 #if HAVE_WXWINDOWS
637   if (m_driver.isWX()) {
638     wxString str (pszMessage);
639     m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
640   }
641 #endif
642 }
643
644
645 // NAME
646 //   drawRect                           Draw box in graphics mode
647 //
648 // SYNOPSIS
649 //   drawbox (xmin, ymin, xmax, ymax)
650 //   double xmin, ymin                  Lower left corner of box
651 //   double xmax, ymax                  Upper left corner of box
652 //
653 // NOTES
654 //   This routine leaves the current position of graphic cursor at lower
655 //   left corner of box.
656
657 void
658 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
659 {
660         moveAbs (xmin, ymin);
661         lineAbs (xmax, ymin);
662         lineAbs (xmax, ymax);
663         lineAbs (xmin, ymax);
664         lineAbs (xmin, ymin);
665 }
666
667 // FUNCTION
668 // sgp2_circle - draw circle of radius r at current center              
669  
670 void 
671 SGP::drawCircle (const double r)
672 {
673         drawArc (r, 0.0, TWOPI);
674 }
675
676 //==============================================================
677 // draw arc around current center.  angles in radius    
678 //==============================================================
679
680 void 
681 SGP::drawArc (const double r, double start, double stop)
682 {
683   if (start > stop) {
684     double temp = start;
685     start = stop;
686     stop = temp;
687   }
688
689   double x = r * cos ((double) start);
690   double y = r * sin ((double) start);
691   moveRel (x, y);          // move from center to start of arc 
692
693   const double thetaIncrement = (5 * (TWOPI / 360)); 
694   double cosTheta = cos(thetaIncrement);
695   double sinTheta = sin(thetaIncrement);
696
697   double angle, xp, yp;
698   for (angle = start; angle < stop - thetaIncrement; angle += thetaIncrement) {
699     xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
700     yp = sinTheta * x + cosTheta * y;
701     lineAbs (xp, yp);
702     x = xp; y = yp;
703   }
704
705   double c = cos (stop - angle);
706   double s = sin (stop - angle);
707   xp = c * x - s * y;
708   yp = s * x + c * y;
709   lineAbs (xp, yp);
710
711   x = r * cos (stop);
712   y = r * sin (stop);
713   moveRel (-x, -y);             // move back to center of circle 
714 }
715
716
717
718 ///////////////////////////////////////////////////////////////////////
719 // Coordinate Transformations
720 ///////////////////////////////////////////////////////////////////////
721
722
723 void
724 SGP::transformNDCtoMC (double* x, double* y)
725 {
726   if (m_bRecalcTransform)
727     calc_transform();
728   ndc_to_mc.transformPoint (x, y);
729 }
730
731
732 void
733 SGP::transformMCtoNDC (double* x, double* y)
734 {
735   if (m_bRecalcTransform)
736     calc_transform();
737   mc_to_ndc.transformPoint (x, y);
738 }
739
740
741 void
742 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
743 {
744   if (m_bRecalcTransform)
745     calc_transform();
746   *x = xIn;
747   *y = yIn;
748   mc_to_ndc.transformPoint (x, y);
749 }
750
751
752 // NAME
753 //      calc_transform                  Calculate transform matrices
754
755 void
756 SGP::calc_transform ()
757 {
758   double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
759   double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
760     
761   wc_to_ndc.setIdentity();
762   wc_to_ndc.mtx[0][0] = scaleX;
763   wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
764   wc_to_ndc.mtx[1][1] = scaleY;
765   wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
766
767   mc_to_ndc = m_ctm * wc_to_ndc;
768   ndc_to_mc = mc_to_ndc.invert();
769
770   m_bRecalcTransform = false;
771 }
772
773 void
774 SGP::ctmClear ()
775 {
776   m_ctm.setIdentity();
777   calc_transform();
778 }
779
780 void 
781 SGP::ctmSet (const TransformationMatrix2D& m)
782 {
783   m_ctm = m;
784   calc_transform();
785 }
786
787
788 void
789 SGP::preTranslate  (double x, double y)
790 {
791     TransformationMatrix2D m;
792
793     m.setTranslate (x, y);
794     ctmSet (m * m_ctm);
795 }
796
797
798 void 
799 SGP::postTranslate (double x, double y)
800 {
801     TransformationMatrix2D m;
802
803     m.setTranslate (x, y);
804     ctmSet (m_ctm * m);
805 }
806
807
808 void 
809 SGP::preScale (double sx, double sy)
810 {
811     TransformationMatrix2D m;
812
813     m.setScale (sx, sy);
814     ctmSet (m * m_ctm);
815 }
816
817
818 void 
819 SGP::postScale (double sx, double sy)
820 {
821     TransformationMatrix2D m;
822
823     m.setScale (sx, sy);
824     m_ctm = m_ctm * m;
825     ctmSet (m_ctm * m);
826 }
827
828
829 void 
830 SGP::preRotate (double theta)
831 {
832     TransformationMatrix2D m;
833
834     m.setRotate (theta);
835     m_ctm = m * m_ctm;
836     ctmSet (m * m_ctm);
837 }
838
839
840 void 
841 SGP::postRotate (double theta)
842 {
843     TransformationMatrix2D m;
844
845     m.setRotate (theta);
846     ctmSet (m_ctm * m);
847 }
848
849
850 void 
851 SGP::preShear (double shrx, double shry)
852 {
853     TransformationMatrix2D m;
854
855     m.setShear (shrx, shry);
856     ctmSet (m * m_ctm);
857 }
858
859
860 void 
861 SGP::postShear (double shrx, double shry)
862 {
863     TransformationMatrix2D m;
864
865     m.setShear (shrx, shry);
866     ctmSet (m_ctm * m);
867 }
868
869
870 ////////////////////////////////////////////////////////////////////////
871 //  Bitmap Markers
872 ////////////////////////////////////////////////////////////////////////
873
874 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
875 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] = 
876 {
877     {'\000', '\000', '\010', '\000', '\000'},    // small dot 
878     {'\000', '\034', '\024', '\034', '\000'},    // empty square 
879     {'\000', '\034', '\034', '\034', '\000'},    // filled square 
880     {'\000', '\010', '\024', '\010', '\000'},    // empty diamond 
881     {'\000', '\010', '\034', '\010', '\000'},    // filled diamond 
882     {'\010', '\010', '\076', '\010', '\010'},    // cross 
883     {'\000', '\024', '\010', '\024', '\000'},    // X 
884     {'\034', '\042', '\042', '\042', '\034'},    // open circle 
885     {'\034', '\076', '\076', '\076', '\034'},    // filled circle 
886     {'\076', '\042', '\042', '\042', '\076'},    // big open square 
887     {'\010', '\024', '\042', '\024', '\010'},    // big open diamond 
888 };
889
890
891 #if HAVE_WXWINDOWS
892 void
893 SGP::setDC (wxDC* pDC)
894 {
895   if (m_driver.isWX())
896     m_driver.setDC(pDC);
897 }
898 #endif