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