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