Improve wxFont usage
[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     int wxFxn = -1;
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     }
413     if (wxFxn >= 0)
414       m_driver.idWX()->SetLogicalFunction (wxFxn);
415   }
416 #endif
417 }
418
419
420 void
421 SGP::setMarker (int idMarker)
422 {
423   m_iMarker = idMarker;
424 }
425
426 //==============================================================
427 // set line style.  Pass 16 bit repeating pattern
428 //==============================================================
429 void
430 SGP::setLineStyle (int style)
431 {
432   m_iLinestyle = style;
433
434 #if HAVE_WXWINDOWS
435   if (m_driver.isWX()) {
436     switch (m_iLinestyle) {
437     case LS_SOLID:
438       m_pen.SetStyle (wxSOLID);
439       break;
440     case LS_DASH1:
441       m_pen.SetStyle (wxLONG_DASH);
442       break;
443     case LS_DASH2:
444       m_pen.SetStyle (wxSHORT_DASH);
445       break;
446     case LS_DASH3:
447       m_pen.SetStyle (wxDOT_DASH);
448       break;
449     case LS_DASH4:
450       m_pen.SetStyle (wxCROSS_HATCH);
451       break;
452     case LS_DOTTED:
453       m_pen.SetStyle (wxDOT);
454       break;
455     default:
456       m_pen.SetStyle (wxSOLID);
457       break;
458     }
459     m_driver.idWX()->SetPen (m_pen);
460   }
461 #endif
462 }
463
464 //==============================================================
465 // absolute draw to
466 //*==============================================================
467
468 void
469 SGP::lineAbs (double x, double y)
470 {
471   if (m_bRecalcTransform)
472     calc_transform();
473
474   double x1 = m_dCurrentWorldX;
475   double y1 = m_dCurrentWorldY;
476   mc_to_ndc.transformPoint (&x1, &y1);
477
478   double x2 = x;
479   double y2 = y;
480   mc_to_ndc.transformPoint (&x2, &y2);
481
482   if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
483     stylusNDC (x1, y1, false);  // move to first point
484     stylusNDC (x2, y2, true);  // draw to second point
485   }
486
487   m_dCurrentWorldX = x;
488   m_dCurrentWorldY = y;
489 }
490
491 void
492 SGP::moveAbs (double x, double y)
493 {
494     m_dCurrentWorldX = x;
495     m_dCurrentWorldY = y;                       /* moves are not clipped */
496 }
497
498 void
499 SGP::lineRel (double x, double y)
500 {
501   lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
502 }
503
504 void
505 SGP::moveRel (double x, double y)
506 {
507   moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
508 }
509
510
511 // Height is in master coordinates
512 void
513 SGP::setTextSize (double height)
514 {
515     height /= (yw_max - yw_min);  // convert to NDC
516 #if HAVE_G2_H
517   if (m_driver.isG2())
518     g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
519 #endif
520 #if HAVE_WXWINDOWS
521   if (m_driver.isWX()) {
522       double dHeightPixels = height * m_iPhysicalYSize;
523       double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
524       m_pFont->SetPointSize (nearest<int>(dHeightPoints));
525       sys_error (ERR_TRACE, "Setting text size to %d points", 
526                  nearest<int>(dHeightPoints));
527
528       m_driver.idWX()->SetFont (*m_pFont);
529   }
530 #endif
531 }
532
533 void
534 SGP::setTextNDCSize (double height)
535 {
536     double dHeightPixels = height * m_iPhysicalYSize;
537 #if HAVE_G2_H
538   if (m_driver.isG2())
539     g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
540 #endif
541 #if HAVE_WXWINDOWS
542   if (m_driver.isWX()) {
543       double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
544       m_pFont->SetPointSize (nearest<int>(dHeightPoints));
545       m_driver.idWX()->SetFont (*m_pFont);
546   }
547 #endif
548 }
549
550 void
551 SGP::setTextPointSize (double height)
552 {
553 #if HAVE_G2_H
554     //  if (m_driver.isG2())
555     //    g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
556 #endif
557 #if HAVE_WXWINDOWS
558   if (m_driver.isWX()) {
559     m_iTextPointSize = static_cast<int>(height+0.5);
560     m_pFont->SetPointSize (m_iTextPointSize);
561     sys_error (ERR_TRACE, "Setting point size to %d", m_iTextPointSize);
562     m_driver.idWX()->SetFont (*m_pFont);
563   }
564 #endif
565 }
566
567 void
568 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
569 {
570 #if HAVE_WXWINDOWS
571   if (m_driver.isWX()) {
572     wxCoord deviceW, deviceH;
573     wxString str (wxConvCurrent->cMB2WC(szText));
574     m_driver.idWX()->GetTextExtent (str, &deviceW, &deviceH);
575     if (m_dTextAngle == 90 || m_dTextAngle == -90) {
576       wxCoord temp = deviceW;
577       deviceW = deviceH;
578       deviceH = temp;
579     }
580     *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
581     *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
582   }
583 #endif
584 }
585
586 double
587 SGP::getCharHeight ()
588 {
589   double dHeight = (1. / 50.);
590
591 #if HAVE_WXWINDOWS
592   if (m_driver.isWX()) {
593     dHeight = m_driver.idWX()->GetCharHeight();
594     dHeight /= static_cast<double>(m_iPhysicalYSize);
595     dHeight /= (yv_max - yv_min); // scale to viewport;
596   }
597 #endif
598   dHeight *= (yw_max - yw_min);  // scale to world coordinates
599   return dHeight;
600 }
601
602 double
603 SGP::getCharWidth ()
604 {
605   double dWidth = (1. / 80.);
606
607 #if HAVE_WXWINDOWS
608   if (m_driver.isWX()) {
609     dWidth = m_driver.idWX()->GetCharWidth();
610     dWidth /= static_cast<double>(m_iPhysicalXSize);
611     dWidth /= (xv_max - xv_min); // scale to viewport
612   }
613 #endif
614   dWidth *= (xw_max - xw_min); //scale to world coordinates
615   return dWidth;
616 }
617
618 void
619 SGP::setTextAngle (double angle)
620 {
621   m_dTextAngle = convertRadiansToDegrees(angle);
622 }
623
624 void
625 SGP::polylineAbs (double x[], double y[], int n)
626 {
627   if (m_bRecalcTransform)
628     calc_transform();
629
630   double x1 = x[0], y1 = y[0];
631   mc_to_ndc.transformPoint (&x1, &y1);
632   double x2 = x[1], y2 = y[1];
633   mc_to_ndc.transformPoint (&x2, &y2);
634
635   double xt = x2;       // don't pass (x2,y2) to clip, we need them
636   double yt = y2;       // as the beginning point of the next line
637
638   if (clip_rect (x1, y1, xt, yt, viewNDC)) {
639     stylusNDC (x1, y1, false);
640     stylusNDC (xt, yt, true);
641   }
642
643   for (int i = 2; i < n; i++) {
644     x1 = x2; y1 = y2;                   // NDC endpoint of last line
645     x2 = x[i];  y2 = y[i];
646     mc_to_ndc.transformPoint (&x2, &y2);
647     xt = x2;
648     yt = y2;
649     if (clip_rect (x1, y1, xt, yt, viewNDC)) {
650       stylusNDC (x1, y1, false);
651       stylusNDC (xt, yt, true);
652     }
653   }
654 }
655
656
657 void
658 SGP::markerAbs (double x, double y)
659 {
660   if (m_bRecalcTransform)
661     calc_transform();
662
663   double xndc = x;
664   double yndc = y;
665   mc_to_ndc.transformPoint (&xndc, &yndc);
666   markerNDC (xndc, yndc);
667   m_dCurrentWorldX = x;
668   m_dCurrentWorldY = y;
669 }
670
671
672 void
673 SGP::markerRel (double x, double y)
674 {
675   markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
676 }
677
678
679 void
680 SGP::pointAbs (double x, double y)
681 {
682   if (m_bRecalcTransform)
683     calc_transform();
684   double xndc = x, yndc = y;
685   mc_to_ndc.transformPoint (&xndc, &yndc);
686   pointNDC (xndc, yndc);
687   m_dCurrentWorldX = x;
688   m_dCurrentWorldY = y;
689 }
690
691
692 void
693 SGP::pointRel (double x, double y)
694 {
695   pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
696 }
697
698
699 void
700 SGP::drawText (const std::string& rsMessage)
701 {
702   drawText (rsMessage.c_str());
703 }
704
705 void
706 SGP::drawText (const char *pszMessage)
707 {
708   if (m_bRecalcTransform)
709     calc_transform();
710
711   double xndc = m_dCurrentWorldX;
712   double yndc = m_dCurrentWorldY;
713   mc_to_ndc.transformPoint (&xndc, &yndc);
714
715   stylusNDC (xndc, yndc, false);            // move to location
716
717 #if HAVE_G2_H
718   if (m_driver.isG2()) {
719     g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
720   }
721 #endif
722 #if HAVE_WXWINDOWS
723   if (m_driver.isWX()) {
724     wxString str(wxConvCurrent->cMB2WC(pszMessage));
725     m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
726   }
727 #endif
728 }
729
730
731 // NAME
732 //   drawRect                           Draw box in graphics mode
733 //
734 // SYNOPSIS
735 //   drawbox (xmin, ymin, xmax, ymax)
736 //   double xmin, ymin                  Lower left corner of box
737 //   double xmax, ymax                  Upper left corner of box
738 //
739 // NOTES
740 //   This routine leaves the current position of graphic cursor at lower
741 //   left corner of box.
742
743 void
744 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
745 {
746         moveAbs (xmin, ymin);
747         lineAbs (xmax, ymin);
748         lineAbs (xmax, ymax);
749         lineAbs (xmin, ymax);
750         lineAbs (xmin, ymin);
751 }
752
753 // FUNCTION
754 // sgp2_circle - draw circle of radius r at current center
755
756 void
757 SGP::drawCircle (const double r)
758 {
759         drawArc (r, 0.0, TWOPI);
760 }
761
762 //==============================================================
763 // draw arc around current center.  angles in radius
764 //==============================================================
765
766 void
767 SGP::drawArc (const double r, double start, double stop)
768 {
769   if (start > stop) {
770     double temp = start;
771     start = stop;
772     stop = temp;
773   }
774
775   double xCent = m_dCurrentWorldX;
776   double yCent = m_dCurrentWorldY;
777
778   double x = r * cos (start);
779   double y = r * sin (start);
780   moveAbs (xCent + x, yCent + y);          // move from center to start of arc
781
782   const double thetaIncrement = (5 * (TWOPI / 360));  // 5 degree increments
783   double cosTheta = cos (thetaIncrement);
784   double sinTheta = sin (thetaIncrement);
785
786   double angle;
787   for (angle = start; angle < stop; angle += thetaIncrement) {
788     double xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
789     double yp = sinTheta * x + cosTheta * y;
790     lineAbs (xCent + xp, yCent + yp);
791     x = xp; y = yp;
792   }
793
794   double c = cos (stop - angle);
795   double s = sin (stop - angle);
796   double xp = c * x - s * y;
797   double yp = s * x + c * y;
798   lineAbs (xCent + xp, yCent + yp);
799
800   moveAbs (xCent, yCent);               // move back to center of circle
801 }
802
803
804
805 ///////////////////////////////////////////////////////////////////////
806 // Coordinate Transformations
807 ///////////////////////////////////////////////////////////////////////
808
809
810 void
811 SGP::transformNDCtoMC (double* x, double* y)
812 {
813   if (m_bRecalcTransform)
814     calc_transform();
815   ndc_to_mc.transformPoint (x, y);
816 }
817
818
819 void
820 SGP::transformMCtoNDC (double* x, double* y)
821 {
822   if (m_bRecalcTransform)
823     calc_transform();
824   mc_to_ndc.transformPoint (x, y);
825 }
826
827
828 void
829 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
830 {
831   if (m_bRecalcTransform)
832     calc_transform();
833   *x = xIn;
834   *y = yIn;
835   mc_to_ndc.transformPoint (x, y);
836 }
837
838
839 // NAME
840 //      calc_transform                  Calculate transform matrices
841
842 void
843 SGP::calc_transform ()
844 {
845   double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
846   double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
847
848   wc_to_ndc.setIdentity();
849   wc_to_ndc.mtx[0][0] = scaleX;
850   wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
851   wc_to_ndc.mtx[1][1] = scaleY;
852   wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
853
854   mc_to_ndc = m_ctm * wc_to_ndc;
855   ndc_to_mc = mc_to_ndc.invert();
856
857   m_bRecalcTransform = false;
858 }
859
860 void
861 SGP::ctmClear ()
862 {
863   m_ctm.setIdentity();
864   calc_transform();
865 }
866
867 void
868 SGP::ctmSet (const TransformationMatrix2D& m)
869 {
870   m_ctm = m;
871   calc_transform();
872 }
873
874
875 void
876 SGP::preTranslate  (double x, double y)
877 {
878     TransformationMatrix2D m;
879
880     m.setTranslate (x, y);
881     ctmSet (m * m_ctm);
882 }
883
884
885 void
886 SGP::postTranslate (double x, double y)
887 {
888     TransformationMatrix2D m;
889
890     m.setTranslate (x, y);
891     ctmSet (m_ctm * m);
892 }
893
894
895 void
896 SGP::preScale (double sx, double sy)
897 {
898     TransformationMatrix2D m;
899
900     m.setScale (sx, sy);
901     ctmSet (m * m_ctm);
902 }
903
904
905 void
906 SGP::postScale (double sx, double sy)
907 {
908     TransformationMatrix2D m;
909
910     m.setScale (sx, sy);
911     m_ctm = m_ctm * m;
912     ctmSet (m_ctm * m);
913 }
914
915
916 void
917 SGP::preRotate (double theta)
918 {
919     TransformationMatrix2D m;
920
921     m.setRotate (theta);
922     m_ctm = m * m_ctm;
923     ctmSet (m * m_ctm);
924 }
925
926
927 void
928 SGP::postRotate (double theta)
929 {
930     TransformationMatrix2D m;
931
932     m.setRotate (theta);
933     ctmSet (m_ctm * m);
934 }
935
936
937 void
938 SGP::preShear (double shrx, double shry)
939 {
940     TransformationMatrix2D m;
941
942     m.setShear (shrx, shry);
943     ctmSet (m * m_ctm);
944 }
945
946
947 void
948 SGP::postShear (double shrx, double shry)
949 {
950     TransformationMatrix2D m;
951
952     m.setShear (shrx, shry);
953     ctmSet (m_ctm * m);
954 }
955
956
957 ////////////////////////////////////////////////////////////////////////
958 //  Bitmap Markers
959 ////////////////////////////////////////////////////////////////////////
960
961 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
962 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
963 {
964     {'\000', '\000', '\010', '\000', '\000'},    // small dot
965     {'\000', '\034', '\024', '\034', '\000'},    // empty square
966     {'\000', '\034', '\034', '\034', '\000'},    // filled square
967     {'\000', '\010', '\024', '\010', '\000'},    // empty diamond
968     {'\000', '\010', '\034', '\010', '\000'},    // filled diamond
969     {'\010', '\010', '\076', '\010', '\010'},    // cross
970     {'\000', '\024', '\010', '\024', '\000'},    // X
971     {'\034', '\042', '\042', '\042', '\034'},    // open circle
972     {'\034', '\076', '\076', '\076', '\034'},    // filled circle
973     {'\076', '\042', '\042', '\042', '\076'},    // big open square
974     {'\010', '\024', '\042', '\024', '\010'},    // big open diamond
975 };
976
977
978 #if HAVE_WXWINDOWS
979 void
980 SGP::setDC (wxDC* pDC)
981 {
982   if (m_driver.isWX()) {
983     m_driver.setDC(pDC);
984     initFromDC (pDC);
985     setTextPointSize (m_iTextPointSize);
986   }
987 }
988 #endif