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