Update copyright date; remove old CVS keyword
[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 (12);
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 (wxROMAN, wxNORMAL, wxNORMAL, wxNORMAL);
121     m_pFont->SetPointSize (iTestPointSize);
122     m_pFont->SetWeight (wxNORMAL);
123     m_pFont->SetStyle (wxNORMAL);
124     m_pFont->SetFamily (wxROMAN);
125 #ifdef MSVC
126     m_pFont->SetFaceName(wxString("times new roman"));
127 #endif
128     m_driver.idWX()->SetFont (*m_pFont);
129     double dTestCharHeight = m_driver.idWX()->GetCharHeight();
130     m_dPointsPerPixel = iTestPointSize / dTestCharHeight;
131         m_driver.idWX()->SetBackground (*wxWHITE_BRUSH);
132   }
133 }
134 #endif
135
136
137 SGP::~SGP()
138 {
139 #if HAVE_WXWINDOWS
140   if (m_driver.isWX()) {
141  //   m_driver.idWX()->SetFont (wxNullFont);
142     delete m_pFont;
143   }
144 #endif
145 }
146
147 void
148 SGP::stylusNDC (double x, double y, bool beam)
149 {
150   int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
151   int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
152   if (m_driver.isWX())
153     yp = m_iPhysicalYSize - yp;
154
155   if (beam) {
156 #if HAVE_WXWINDOWS
157     if (m_driver.isWX())
158       m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
159 #endif
160 #if HAVE_G2_H
161     if (m_driver.isG2())
162       g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
163 #endif
164   }
165   m_iCurrentPhysicalX = xp;
166   m_iCurrentPhysicalY = yp;
167 }
168
169 void
170 SGP::markerNDC (double x, double y)
171 {
172   int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
173   int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
174   if (m_driver.isWX())
175     yp = m_iPhysicalYSize - yp;
176
177 #if HAVE_WXWINDOWS
178   if (m_driver.isWX()) {
179       m_driver.idWX()->DrawPoint (xp, yp);
180       m_driver.idWX()->DrawPoint (xp-1, yp-1);
181       m_driver.idWX()->DrawPoint (xp+1, yp+1);
182       m_driver.idWX()->DrawPoint (xp+1, yp-1);
183       m_driver.idWX()->DrawPoint (xp-1, yp+1);
184   }
185 #endif
186   m_iCurrentPhysicalX = xp;
187   m_iCurrentPhysicalY = yp;
188 }
189
190 void
191 SGP::pointNDC (double x, double y)
192 {
193   int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
194   int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
195   if (m_driver.isWX())
196     yp = m_iPhysicalYSize - yp;
197
198 #if HAVE_WXWINDOWS
199     if (m_driver.isWX())
200       m_driver.idWX()->DrawPoint (xp, yp);
201 #endif
202   m_iCurrentPhysicalX = xp;
203   m_iCurrentPhysicalY = yp;
204 }
205
206
207 // NAME
208 //    clear     Clear Window
209
210 void
211 SGP::eraseWindow ()
212 {
213 #if HAVE_G2_H
214   if (m_driver.isG2())
215     g2_clear (m_driver.idG2());
216 #endif
217 #if HAVE_WXWINDOWS
218   if (m_driver.isWX()) {
219         wxBrush brushWhite;
220         brushWhite.SetColour(255,255,255);
221         m_driver.idWX()->SetBackground(brushWhite);
222         m_driver.idWX()->Clear();
223         m_driver.idWX()->SetBackground(wxNullBrush);
224 #if 1
225         wxPen pen;
226         pen.SetColour(255,255,255);
227         m_driver.idWX()->SetBrush (brushWhite);
228         m_driver.idWX()->DrawRectangle (0, 0, m_iPhysicalXSize, m_iPhysicalYSize);
229         m_driver.idWX()->SetBrush (wxNullBrush);
230 #endif
231   }
232 #endif
233 }
234
235 // NAME
236 //      sgp2_window             Set window in world coordinates
237
238
239 void
240 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
241 {
242   if (xmin >= xmax || ymin >= ymax) {
243     sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
244     return;
245   }
246
247   xw_min = xmin;
248   yw_min = ymin;
249   xw_max = xmax;
250   yw_max = ymax;
251   m_bRecalcTransform = true;
252 }
253
254
255 // NAME
256 //      sgp2_viewport                   Set viewport in NDC
257
258 void
259 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
260 {
261   if (xmin >= xmax || ymin >= ymax) {
262     sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
263     return;
264   }
265
266   xv_min = xmin;
267   yv_min = ymin;
268   xv_max = xmax;
269   yv_max = ymax;
270   m_bRecalcTransform = true;
271
272   viewNDC[0] = xmin;                    // Array for clip_rect()
273   viewNDC[1] = ymin;
274   viewNDC[2] = xmax;
275   viewNDC[3] = ymax;
276 }
277
278 void
279 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
280 {
281     xmin = xv_min;
282     ymin = yv_min;
283     xmax = xv_max;
284     ymax = yv_max;
285 }
286
287 void
288 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
289 {
290     xmin = xw_min;
291     ymin = yw_min;
292     xmax = xw_max;
293     ymax = yw_max;
294 }
295
296
297 // NAME
298 //      frameViewport           draw box around viewport
299
300 void
301 SGP::frameViewport (void)
302 {
303   stylusNDC (xv_min, yv_min, false);
304   stylusNDC (xv_max, yv_min, true);
305   stylusNDC (xv_max, yv_max, true);
306   stylusNDC (xv_min, yv_max, true);
307   stylusNDC (xv_min, yv_min, true);
308 }
309
310 void
311 SGP::setTextColor (int iFGcolor, int iBGcolor)
312 {
313 #if HAVE_WXWINDOWS
314   if (m_driver.isWX()) {
315     if (iFGcolor >= 0) {
316       wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
317       m_driver.idWX()->SetTextForeground (colour);
318     }
319     if (iBGcolor >= 0) {
320       wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
321       m_driver.idWX()->SetTextBackground (colour);
322     }
323   }
324 #endif
325 }
326
327 void
328 SGP::setColor (int icol)
329 {
330   if (icol >= 0 && icol < s_iRGBColorCount) {
331 #if HAVE_G2_H
332     if (m_driver.isG2()) {
333       int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
334       g2_pen (m_driver.idG2(), iInk);
335     }
336 #endif
337 #if HAVE_WXWINDOWS
338     if (m_driver.isWX()) {
339       wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
340       m_pen.SetColour (colour);
341       m_driver.idWX()->SetPen (m_pen);
342     }
343 #endif
344   }
345 }
346
347 void
348 SGP::setPenWidth (int iWidth)
349 {
350   if (iWidth >= 0) {
351 #if HAVE_WXWINDOWS
352     if (m_driver.isWX()) {
353       m_pen.SetWidth (iWidth);
354       m_driver.idWX()->SetPen (m_pen);
355     }
356 #endif
357   }
358 }
359
360 void
361 SGP::setRasterOp (int ro)
362 {
363 #if HAVE_WXWINDOWS
364   if (m_driver.isWX()) {
365     int wxFxn = -1;
366     switch (ro) {
367     case RO_AND:
368       wxFxn = wxAND;
369       break;
370     case RO_AND_INVERT:
371       wxFxn = wxAND_INVERT;
372       break;
373     case RO_AND_REVERSE:
374       wxFxn = wxAND_REVERSE;
375       break;
376     case RO_CLEAR:
377       wxFxn = wxCLEAR;
378       break;
379     case RO_COPY:
380       wxFxn = wxCOPY;
381       break;
382     case RO_EQUIV:
383       wxFxn = wxEQUIV;
384       break;
385     case RO_INVERT:
386       wxFxn = wxINVERT;
387       break;
388     case RO_NAND:
389       wxFxn = wxNAND;
390       break;
391     case RO_NOR:
392       wxFxn = wxNOR;
393       break;
394     case RO_NO_OP:
395       wxFxn = wxNO_OP;
396       break;
397     case RO_OR:
398       wxFxn = wxOR;
399       break;
400     case RO_OR_INVERT:
401       wxFxn = wxOR_INVERT;
402       break;
403     case RO_OR_REVERSE:
404       wxFxn = wxOR_REVERSE;
405       break;
406     case RO_SET:
407       wxFxn = wxSET;
408       break;
409     case RO_SRC_INVERT:
410       wxFxn = wxSRC_INVERT;
411       break;
412     case RO_XOR:
413       wxFxn = wxXOR;
414       break;
415     }
416     if (wxFxn >= 0)
417       m_driver.idWX()->SetLogicalFunction (wxFxn);
418   }
419 #endif
420 }
421
422
423 void
424 SGP::setMarker (int idMarker)
425 {
426   m_iMarker = idMarker;
427 }
428
429 //==============================================================
430 // set line style.  Pass 16 bit repeating pattern
431 //==============================================================
432 void
433 SGP::setLineStyle (int style)
434 {
435   m_iLinestyle = style;
436
437 #if HAVE_WXWINDOWS
438   if (m_driver.isWX()) {
439     switch (m_iLinestyle) {
440     case LS_SOLID:
441       m_pen.SetStyle (wxSOLID);
442       break;
443     case LS_DASH1:
444       m_pen.SetStyle (wxLONG_DASH);
445       break;
446     case LS_DASH2:
447       m_pen.SetStyle (wxSHORT_DASH);
448       break;
449     case LS_DASH3:
450       m_pen.SetStyle (wxDOT_DASH);
451       break;
452     case LS_DASH4:
453       m_pen.SetStyle (wxCROSS_HATCH);
454       break;
455     case LS_DOTTED:
456       m_pen.SetStyle (wxDOT);
457       break;
458     default:
459       m_pen.SetStyle (wxSOLID);
460       break;
461     }
462     m_driver.idWX()->SetPen (m_pen);
463   }
464 #endif
465 }
466
467 //==============================================================
468 // absolute draw to
469 //*==============================================================
470
471 void
472 SGP::lineAbs (double x, double y)
473 {
474   if (m_bRecalcTransform)
475     calc_transform();
476
477   double x1 = m_dCurrentWorldX;
478   double y1 = m_dCurrentWorldY;
479   mc_to_ndc.transformPoint (&x1, &y1);
480
481   double x2 = x;
482   double y2 = y;
483   mc_to_ndc.transformPoint (&x2, &y2);
484
485   if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
486     stylusNDC (x1, y1, false);  // move to first point
487     stylusNDC (x2, y2, true);  // draw to second point
488   }
489
490   m_dCurrentWorldX = x;
491   m_dCurrentWorldY = y;
492 }
493
494 void
495 SGP::moveAbs (double x, double y)
496 {
497     m_dCurrentWorldX = x;
498     m_dCurrentWorldY = y;                       /* moves are not clipped */
499 }
500
501 void
502 SGP::lineRel (double x, double y)
503 {
504   lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
505 }
506
507 void
508 SGP::moveRel (double x, double y)
509 {
510   moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
511 }
512
513
514 // Height is in master coordinates
515 void
516 SGP::setTextSize (double height)
517 {
518     height /= (yw_max - yw_min);  // convert to NDC
519 #if HAVE_G2_H
520   if (m_driver.isG2())
521     g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
522 #endif
523 #if HAVE_WXWINDOWS
524   if (m_driver.isWX()) {
525       double dHeightPixels = height * m_iPhysicalYSize;
526       double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
527       m_pFont->SetPointSize (nearest<int>(dHeightPoints));
528       m_driver.idWX()->SetFont (*m_pFont);
529   }
530 #endif
531 }
532
533 void
534 SGP::setTextNDCSize (double height)
535 {
536     double dHeightPixels = height * m_iPhysicalYSize;
537 #if HAVE_G2_H
538   if (m_driver.isG2())
539     g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
540 #endif
541 #if HAVE_WXWINDOWS
542   if (m_driver.isWX()) {
543       double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
544       m_pFont->SetPointSize (nearest<int>(dHeightPoints));
545       m_driver.idWX()->SetFont (*m_pFont);
546   }
547 #endif
548 }
549
550 void
551 SGP::setTextPointSize (double height)
552 {
553 #if HAVE_G2_H
554     //  if (m_driver.isG2())
555     //    g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
556 #endif
557 #if HAVE_WXWINDOWS
558   if (m_driver.isWX()) {
559     m_iTextPointSize = static_cast<int>(height+0.5);
560       m_pFont->SetPointSize (m_iTextPointSize);
561       m_driver.idWX()->SetFont (*m_pFont);
562   }
563 #endif
564 }
565
566 void
567 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
568 {
569 #if HAVE_WXWINDOWS
570   if (m_driver.isWX()) {
571     wxCoord deviceW, deviceH;
572     wxString str (wxConvCurrent->cMB2WC(szText));
573     m_driver.idWX()->GetTextExtent (str, &deviceW, &deviceH);
574     if (m_dTextAngle == 90 || m_dTextAngle == -90) {
575       wxCoord temp = deviceW;
576       deviceW = deviceH;
577       deviceH = temp;
578     }
579     *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
580     *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
581   }
582 #endif
583 }
584
585 double
586 SGP::getCharHeight ()
587 {
588   double dHeight = (1. / 25.);
589
590 #if HAVE_WXWINDOWS
591   if (m_driver.isWX()) {
592     dHeight = m_driver.idWX()->GetCharHeight();
593     dHeight /= static_cast<double>(m_iPhysicalYSize);
594           dHeight /= (yv_max - yv_min); // scale to viewport;
595   }
596 #endif
597   dHeight *= (yw_max - yw_min);  // scale to world coordinates
598   return dHeight;
599 }
600
601 double
602 SGP::getCharWidth ()
603 {
604   double dWidth = (1. / 80.);
605
606 #if HAVE_WXWINDOWS
607   if (m_driver.isWX()) {
608     dWidth = m_driver.idWX()->GetCharWidth();
609     dWidth /= static_cast<double>(m_iPhysicalXSize);
610           dWidth /= (xv_max - xv_min); // scale to viewport
611   }
612 #endif
613   dWidth *= (xw_max - xw_min); //scale to world coordinates
614   return dWidth;
615 }
616
617 void
618 SGP::setTextAngle (double angle)
619 {
620   m_dTextAngle = convertRadiansToDegrees(angle);
621 }
622
623 void
624 SGP::polylineAbs (double x[], double y[], int n)
625 {
626   if (m_bRecalcTransform)
627     calc_transform();
628
629   double x1 = x[0], y1 = y[0];
630   mc_to_ndc.transformPoint (&x1, &y1);
631   double x2 = x[1], y2 = y[1];
632   mc_to_ndc.transformPoint (&x2, &y2);
633
634   double xt = x2;       // don't pass (x2,y2) to clip, we need them
635   double yt = y2;       // as the beginning point of the next line
636
637   if (clip_rect (x1, y1, xt, yt, viewNDC)) {
638     stylusNDC (x1, y1, false);
639     stylusNDC (xt, yt, true);
640   }
641
642   for (int i = 2; i < n; i++) {
643     x1 = x2; y1 = y2;                   // NDC endpoint of last line
644     x2 = x[i];  y2 = y[i];
645     mc_to_ndc.transformPoint (&x2, &y2);
646     xt = x2;
647     yt = y2;
648     if (clip_rect (x1, y1, xt, yt, viewNDC)) {
649       stylusNDC (x1, y1, false);
650       stylusNDC (xt, yt, true);
651     }
652   }
653 }
654
655
656 void
657 SGP::markerAbs (double x, double y)
658 {
659   if (m_bRecalcTransform)
660     calc_transform();
661
662   double xndc = x;
663   double yndc = y;
664   mc_to_ndc.transformPoint (&xndc, &yndc);
665   markerNDC (xndc, yndc);
666   m_dCurrentWorldX = x;
667   m_dCurrentWorldY = y;
668 }
669
670
671 void
672 SGP::markerRel (double x, double y)
673 {
674   markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
675 }
676
677
678 void
679 SGP::pointAbs (double x, double y)
680 {
681   if (m_bRecalcTransform)
682     calc_transform();
683   double xndc = x, yndc = y;
684   mc_to_ndc.transformPoint (&xndc, &yndc);
685   pointNDC (xndc, yndc);
686   m_dCurrentWorldX = x;
687   m_dCurrentWorldY = y;
688 }
689
690
691 void
692 SGP::pointRel (double x, double y)
693 {
694   pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
695 }
696
697
698 void
699 SGP::drawText (const std::string& rsMessage)
700 {
701   drawText (rsMessage.c_str());
702 }
703
704 void
705 SGP::drawText (const char *pszMessage)
706 {
707   if (m_bRecalcTransform)
708     calc_transform();
709
710   double xndc = m_dCurrentWorldX;
711   double yndc = m_dCurrentWorldY;
712   mc_to_ndc.transformPoint (&xndc, &yndc);
713
714   stylusNDC (xndc, yndc, false);            // move to location
715
716 #if HAVE_G2_H
717   if (m_driver.isG2()) {
718     g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
719   }
720 #endif
721 #if HAVE_WXWINDOWS
722   if (m_driver.isWX()) {
723     wxString str(wxConvCurrent->cMB2WC(pszMessage));
724     m_driver.idWX()->DrawRotatedText (str, 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