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