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