r313: *** empty log 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-2000 Kevin Rosenberg
9 **
10 **  $Id: sgp.cpp,v 1.23 2000/12/25 21:54:26 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;\r
102     m_pFont = new wxFont (wxROMAN, wxNORMAL, wxNORMAL, wxNORMAL);
103     m_pFont->SetPointSize (iTestPointSize);\r
104     m_pFont->SetWeight (wxNORMAL);\r
105     m_pFont->SetStyle (wxNORMAL);
106     m_pFont->SetFamily (wxROMAN);\r
107 #ifdef MSVC\r
108     m_pFont->SetFaceName(wxString("times new roman"));\r
109 #endif\r
110     m_driver.idWX()->SetFont (*m_pFont);
111     double dTestCharHeight = m_driver.idWX()->GetCharHeight();
112     m_dPointsPerPixel = iTestPointSize / dTestCharHeight;\r
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);\r
125   setLineStyle (LS_SOLID);
126 }
127
128 SGP::~SGP()\r
129 {\r
130 #if HAVE_WXWINDOWS\r
131   if (m_driver.isWX()) {\r
132     m_driver.idWX()->SetFont (wxNullFont);\r
133     delete m_pFont;\r
134   }\r
135 #endif\r
136 }\r
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()) {\r
183         wxBrush brushWhite;\r
184         brushWhite.SetColour(255,255,255);\r
185         m_driver.idWX()->SetBackground(brushWhite);\r
186         m_driver.idWX()->Clear();\r
187         m_driver.idWX()->SetBackground(wxNullBrush);\r
188 #if 1\r
189         wxPen pen;\r
190         pen.SetColour(255,255,255);\r
191         m_driver.idWX()->SetBrush (brushWhite);\r
192         m_driver.idWX()->DrawRectangle (0, 0, m_iPhysicalXSize, m_iPhysicalYSize);\r
193         m_driver.idWX()->SetBrush (wxNullBrush);\r
194 #endif\r
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 {\r
398   m_iLinestyle = style;\r
399
400 #if HAVE_WXWINDOWS\r
401   if (m_driver.isWX()) {\r
402     switch (m_iLinestyle) {\r
403     case LS_SOLID:\r
404       m_pen.SetStyle (wxSOLID);\r
405       break;\r
406     case LS_DASH1:\r
407       m_pen.SetStyle (wxLONG_DASH);\r
408       break;\r
409     case LS_DASH2:\r
410       m_pen.SetStyle (wxSHORT_DASH);\r
411       break;\r
412     case LS_DASH3:\r
413       m_pen.SetStyle (wxDOT_DASH);\r
414       break;\r
415     case LS_DASH4:\r
416       m_pen.SetStyle (wxCROSS_HATCH);\r
417       break;\r
418     case LS_DOTTED:\r
419       m_pen.SetStyle (wxDOT);\r
420       break;\r
421     default:\r
422       m_pen.SetStyle (wxSOLID);\r
423       break;\r
424     }\r
425     m_driver.idWX()->SetPen (m_pen);\r
426   }\r
427 #endif\r
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     *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
537     *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
538   }
539 #endif
540 }
541
542 double
543 SGP::getCharHeight ()
544 {
545   double dHeight = (1. / 25.);
546
547 #if HAVE_WXWINDOWS
548   if (m_driver.isWX()) {
549     dHeight = m_driver.idWX()->GetCharHeight();
550     dHeight /= static_cast<double>(m_iPhysicalYSize);\r
551         dHeight /= (yv_max - yv_min); // scale to viewport;
552   }
553 #endif
554   dHeight *= (yw_max - yw_min);  // scale to world coordinates
555   return dHeight;
556 }
557
558 double
559 SGP::getCharWidth ()
560 {
561   double dWidth = (1. / 80.);
562
563 #if HAVE_WXWINDOWS
564   if (m_driver.isWX()) {
565     dWidth = m_driver.idWX()->GetCharWidth();
566     dWidth /= static_cast<double>(m_iPhysicalXSize);\r
567         dWidth /= (xv_max - xv_min); // scale to viewport
568   }
569 #endif
570   dWidth *= (xw_max - xw_min); //scale to world coordinates
571   return dWidth;
572 }
573
574 void
575 SGP::setTextAngle (double angle)
576 {
577   m_dTextAngle = convertRadiansToDegrees(angle);
578 }
579
580 void 
581 SGP::polylineAbs (double x[], double y[], int n)
582 {
583   if (m_bRecalcTransform)
584     calc_transform();
585
586   double x1 = x[0], y1 = y[0];
587   mc_to_ndc.transformPoint (&x1, &y1);
588   double x2 = x[1], y2 = y[1];
589   mc_to_ndc.transformPoint (&x2, &y2);
590
591   double xt = x2;       // don't pass (x2,y2) to clip, we need them 
592   double yt = y2;       // as the beginning point of the next line 
593
594   if (clip_rect (x1, y1, xt, yt, viewNDC)) {
595     stylusNDC (x1, y1, false);
596     stylusNDC (xt, yt, true);
597   }
598   
599   for (int i = 2; i < n; i++) {
600     x1 = x2; y1 = y2;                   // NDC endpoint of last line 
601     x2 = x[i];  y2 = y[i];
602     mc_to_ndc.transformPoint (&x2, &y2);
603     xt = x2;
604     yt = y2;
605     if (clip_rect (x1, y1, xt, yt, viewNDC)) {
606       stylusNDC (x1, y1, false);
607       stylusNDC (xt, yt, true);
608     }
609   }
610 }
611
612
613 void 
614 SGP::markerAbs (double x, double y)
615 {
616   if (m_bRecalcTransform)
617     calc_transform();
618
619   double xndc = x;
620   double yndc = y;
621   mc_to_ndc.transformPoint (&xndc, &yndc);
622   markerNDC (xndc, yndc); 
623   stylusNDC (xndc, yndc, false);            // move to location 
624   m_dCurrentWorldX = x;
625   m_dCurrentWorldY = y;
626 }
627
628
629 void 
630 SGP::markerRel (double x, double y)
631 {
632   markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
633 }
634
635
636 void 
637 SGP::pointAbs (double x, double y)
638 {
639   if (m_bRecalcTransform)
640     calc_transform();
641   double xndc = x, yndc = y;
642   mc_to_ndc.transformPoint (&xndc, &yndc);
643   pointNDC (xndc, yndc);
644   stylusNDC (xndc, yndc, false);            // move to location 
645   m_dCurrentWorldX = x;
646   m_dCurrentWorldY = y;
647 }
648
649
650 void 
651 SGP::pointRel (double x, double y)
652 {
653   pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
654 }
655
656
657 void
658 SGP::drawText (const std::string& rsMessage)
659 {
660   drawText (rsMessage.c_str());
661 }
662
663 void 
664 SGP::drawText (const char *pszMessage)
665 {
666   if (m_bRecalcTransform)
667     calc_transform();
668
669   double xndc = m_dCurrentWorldX;
670   double yndc = m_dCurrentWorldY;
671   mc_to_ndc.transformPoint (&xndc, &yndc);
672
673   stylusNDC (xndc, yndc, false);            // move to location 
674
675 #if HAVE_G2_H
676   if (m_driver.isG2()) {
677     g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
678   }
679 #endif
680 #if HAVE_WXWINDOWS
681   if (m_driver.isWX()) {
682     wxString str (pszMessage);
683     m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
684   }
685 #endif
686 }
687
688
689 // NAME
690 //   drawRect                           Draw box in graphics mode
691 //
692 // SYNOPSIS
693 //   drawbox (xmin, ymin, xmax, ymax)
694 //   double xmin, ymin                  Lower left corner of box
695 //   double xmax, ymax                  Upper left corner of box
696 //
697 // NOTES
698 //   This routine leaves the current position of graphic cursor at lower
699 //   left corner of box.
700
701 void
702 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
703 {
704         moveAbs (xmin, ymin);
705         lineAbs (xmax, ymin);
706         lineAbs (xmax, ymax);
707         lineAbs (xmin, ymax);
708         lineAbs (xmin, ymin);
709 }
710
711 // FUNCTION
712 // sgp2_circle - draw circle of radius r at current center              
713  
714 void 
715 SGP::drawCircle (const double r)
716 {
717         drawArc (r, 0.0, TWOPI);
718 }
719
720 //==============================================================
721 // draw arc around current center.  angles in radius    
722 //==============================================================
723
724 void 
725 SGP::drawArc (const double r, double start, double stop)
726 {
727   if (start > stop) {
728     double temp = start;
729     start = stop;
730     stop = temp;
731   }
732
733   double x = r * cos ((double) start);
734   double y = r * sin ((double) start);
735   moveRel (x, y);          // move from center to start of arc 
736
737   const double thetaIncrement = (5 * (TWOPI / 360)); 
738   double cosTheta = cos(thetaIncrement);
739   double sinTheta = sin(thetaIncrement);
740
741   double angle, xp, yp;
742   for (angle = start; angle < stop - thetaIncrement; angle += thetaIncrement) {
743     xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
744     yp = sinTheta * x + cosTheta * y;
745     lineAbs (xp, yp);
746     x = xp; y = yp;
747   }
748
749   double c = cos (stop - angle);
750   double s = sin (stop - angle);
751   xp = c * x - s * y;
752   yp = s * x + c * y;
753   lineAbs (xp, yp);
754
755   x = r * cos (stop);
756   y = r * sin (stop);
757   moveRel (-x, -y);             // move back to center of circle 
758 }
759
760
761
762 ///////////////////////////////////////////////////////////////////////
763 // Coordinate Transformations
764 ///////////////////////////////////////////////////////////////////////
765
766
767 void
768 SGP::transformNDCtoMC (double* x, double* y)
769 {
770   if (m_bRecalcTransform)
771     calc_transform();
772   ndc_to_mc.transformPoint (x, y);
773 }
774
775
776 void
777 SGP::transformMCtoNDC (double* x, double* y)
778 {
779   if (m_bRecalcTransform)
780     calc_transform();
781   mc_to_ndc.transformPoint (x, y);
782 }
783
784
785 void
786 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
787 {
788   if (m_bRecalcTransform)
789     calc_transform();
790   *x = xIn;
791   *y = yIn;
792   mc_to_ndc.transformPoint (x, y);
793 }
794
795
796 // NAME
797 //      calc_transform                  Calculate transform matrices
798
799 void
800 SGP::calc_transform ()
801 {
802   double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
803   double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
804     
805   wc_to_ndc.setIdentity();
806   wc_to_ndc.mtx[0][0] = scaleX;
807   wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
808   wc_to_ndc.mtx[1][1] = scaleY;
809   wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
810
811   mc_to_ndc = m_ctm * wc_to_ndc;
812   ndc_to_mc = mc_to_ndc.invert();
813
814   m_bRecalcTransform = false;
815 }
816
817 void
818 SGP::ctmClear ()
819 {
820   m_ctm.setIdentity();
821   calc_transform();
822 }
823
824 void 
825 SGP::ctmSet (const TransformationMatrix2D& m)
826 {
827   m_ctm = m;
828   calc_transform();
829 }
830
831
832 void
833 SGP::preTranslate  (double x, double y)
834 {
835     TransformationMatrix2D m;
836
837     m.setTranslate (x, y);
838     ctmSet (m * m_ctm);
839 }
840
841
842 void 
843 SGP::postTranslate (double x, double y)
844 {
845     TransformationMatrix2D m;
846
847     m.setTranslate (x, y);
848     ctmSet (m_ctm * m);
849 }
850
851
852 void 
853 SGP::preScale (double sx, double sy)
854 {
855     TransformationMatrix2D m;
856
857     m.setScale (sx, sy);
858     ctmSet (m * m_ctm);
859 }
860
861
862 void 
863 SGP::postScale (double sx, double sy)
864 {
865     TransformationMatrix2D m;
866
867     m.setScale (sx, sy);
868     m_ctm = m_ctm * m;
869     ctmSet (m_ctm * m);
870 }
871
872
873 void 
874 SGP::preRotate (double theta)
875 {
876     TransformationMatrix2D m;
877
878     m.setRotate (theta);
879     m_ctm = m * m_ctm;
880     ctmSet (m * m_ctm);
881 }
882
883
884 void 
885 SGP::postRotate (double theta)
886 {
887     TransformationMatrix2D m;
888
889     m.setRotate (theta);
890     ctmSet (m_ctm * m);
891 }
892
893
894 void 
895 SGP::preShear (double shrx, double shry)
896 {
897     TransformationMatrix2D m;
898
899     m.setShear (shrx, shry);
900     ctmSet (m * m_ctm);
901 }
902
903
904 void 
905 SGP::postShear (double shrx, double shry)
906 {
907     TransformationMatrix2D m;
908
909     m.setShear (shrx, shry);
910     ctmSet (m_ctm * m);
911 }
912
913
914 ////////////////////////////////////////////////////////////////////////
915 //  Bitmap Markers
916 ////////////////////////////////////////////////////////////////////////
917
918 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
919 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] = 
920 {
921     {'\000', '\000', '\010', '\000', '\000'},    // small dot 
922     {'\000', '\034', '\024', '\034', '\000'},    // empty square 
923     {'\000', '\034', '\034', '\034', '\000'},    // filled square 
924     {'\000', '\010', '\024', '\010', '\000'},    // empty diamond 
925     {'\000', '\010', '\034', '\010', '\000'},    // filled diamond 
926     {'\010', '\010', '\076', '\010', '\010'},    // cross 
927     {'\000', '\024', '\010', '\024', '\000'},    // X 
928     {'\034', '\042', '\042', '\042', '\034'},    // open circle 
929     {'\034', '\076', '\076', '\076', '\034'},    // filled circle 
930     {'\076', '\042', '\042', '\042', '\076'},    // big open square 
931     {'\010', '\024', '\042', '\024', '\010'},    // big open diamond 
932 };
933
934
935 #if HAVE_WXWINDOWS
936 void
937 SGP::setDC (wxDC* pDC)
938 {
939   if (m_driver.isWX())
940     m_driver.setDC(pDC);
941 }
942 #endif