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