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