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