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