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