Applied initial patches for wx2.8 compatibility
[ctsim.git] / libctgraphics / sgp.cpp
index cc87671d0c45388327cba801bc1c69b2a0cce518..9757fe9fb03449e911e7d0fcee351488be47540a 100644 (file)
@@ -1,13 +1,13 @@
 /*****************************************************************************
 ** FILE IDENTIFICATION
 **
-**     Name:       sgp.c               Simple Graphics Package
-**     Programmer: Kevin Rosenberg
+**      Name:       sgp.cpp             Simple Graphics Package
+**      Programmer: Kevin Rosenberg
 **
 **  This is part of the CTSim program
-**  Copyright (C) 1983-2000 Kevin Rosenberg
+**  Copyright (c) 1983-2001 Kevin Rosenberg
 **
-**  $Id: sgp.cpp,v 1.3 2000/06/19 19:16:17 kevin Exp $
+**  $Id$
 **
 **  This program is free software; you can redistribute it and/or modify
 **  it under the terms of the GNU General Public License (version 2) as
 #include "sgp.h"
 
 
-static SGP_ID _sgp2_cwin = NULL;
+SGP_RGBColor SGP::s_aRGBColor[] =
+{
+  SGP_RGBColor (0, 0, 0),
+  SGP_RGBColor (0, 0, 128),
+  SGP_RGBColor (0, 128, 0),
+  SGP_RGBColor (0, 128, 128),
+  SGP_RGBColor (128, 0, 0),
+  SGP_RGBColor (128, 0, 128),
+  SGP_RGBColor (128, 128, 0),
+  SGP_RGBColor (80, 80, 80),
+  SGP_RGBColor (160, 160, 160),
+  SGP_RGBColor (0, 0, 255),
+  SGP_RGBColor (0, 255, 0),
+  SGP_RGBColor (0, 255, 255),
+  SGP_RGBColor (255, 0, 0),
+  SGP_RGBColor (255, 0, 255),
+  SGP_RGBColor (255, 255, 0),
+  SGP_RGBColor (255, 255, 255),
+};
 
-extern CHARSPEC cspec;
+int SGP::s_iRGBColorCount = sizeof(s_aRGBColor) / sizeof(class SGP_RGBColor);
 
+#ifdef HAVE_WXWINDOWS
+SGPDriver::SGPDriver (wxDC* pDC, int xsize, int ysize)
+  : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_idDriver(0), m_pDC(pDC)
+{
+  m_idDriver |= SGPDRIVER_WXWINDOWS;
+}
+#endif
 
-/* NAME
- *   sgp2_init                         Initialize 2 graphics system
- *
- * SYNOPSIS
- *   sgp2_init()
- */
+SGPDriver::SGPDriver (const char* szWinTitle, int xsize, int ysize)
+  : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_sWindowTitle(szWinTitle), m_idDriver(0)
+{
+#ifdef HAVE_G2_H
+  m_idG2 = g2_open_X11X (m_iPhysicalXSize, m_iPhysicalYSize, 10, 10, const_cast<char*>(szWinTitle), const_cast<char*>(szWinTitle), NULL, -1, -1);
+  m_idDriver |= SGPDRIVER_G2;
+#endif
+}
 
-SGP_ID
-sgp2_init (int xsize, int ysize, const char *win_title )
+SGPDriver::~SGPDriver ()
 {
-    SGP_ID gid;
+#if HAVE_G2_H
+  if (isG2())
+    g2_close (m_idG2);
+#endif
+}
 
-    gid = new SGP_WINDOW;
-    _sgp2_cwin = gid;
-    if (xsize <= 0)
-       xsize = 640;
-    if (ysize <= 0)
-       ysize = 480;
 
-    gid->pw_xsize = xsize;
-    gid->pw_ysize = ysize;
-    strncpy(gid->title, win_title, sizeof(gid->title));
+// NAME
+//   SGP::SGP        Constructor for Simple Graphics Package
 
-    gid->recalc_mc_to_ndc = TRUE;
-    gid->recalc_ndc_to_mc = TRUE;
-    ident_gmtx_2 (gid->wc_to_ndc_x);
-    ident_gmtx_2 (gid->mc_to_ndc_x);
-    ident_gmtx_2 (gid->ndc_to_mc_x);
-    ident_gmtx_2 (gid->ctm_2_x);
+SGP::SGP (const SGPDriver& driver)
+  : m_driver (driver)
+{
+  m_iPhysicalXSize = m_driver.getPhysicalXSize();
+  m_iPhysicalYSize = m_driver.getPhysicalYSize();
 
-    sgp2_window (0., 0., 1., 1.);
-    sgp2_viewport (0., 0., 1., 1.);
-    ctm_clr_2 ();
-    sgp2_move_abs (0., 0.);
+  wc_to_ndc.setIdentity ();
+  mc_to_ndc.setIdentity();
+  ndc_to_mc.setIdentity();
+  m_ctm.setIdentity();
 
-#if HAVE_G2_H
-    gid->g2_id = g2_open_X11X (gid->pw_xsize, gid->pw_ysize, 10, 10, gid->title, gid->title, NULL, -1, -1);
+#ifdef HAVE_WXWINDOWS
+  initFromDC (driver.idWX());
 #endif
 
-    _sgp2_init_dev (_sgp2_cwin);
-    
-    return (gid);
+  setWindow (0., 0., 1., 1.);
+  setViewport (0., 0., 1., 1.);
+  moveAbs (0., 0.);
+  stylusNDC (0., 0., false);
+
+  setTextAngle (0.);
+  setTextPointSize (12);
+  setColor (C_BLACK);
+  setLineStyle (LS_SOLID);
+  setMarker (MARKER_POINT);
 }
 
 
+#ifdef HAVE_WXWINDOWS
 void
-sgp2_close (SGP_ID gid)
-{
-#if HAVE_G2_H    
-    g2_close (gid->g2_id);
+SGP::initFromDC (wxDC* pDC)
+{
+  m_pen.SetWidth (1);
+
+  if (m_driver.isWX()) {
+    static const double dScreenDPI = 82;
+    static const double dPointsPerInch = 72.;
+    m_dPointsPerPixel = dPointsPerInch / dScreenDPI;
+    const int iTestPointSize = 12;
+    m_pFont = new wxFont (wxROMAN, wxNORMAL, wxNORMAL, wxNORMAL);
+    m_pFont->SetPointSize (iTestPointSize);
+    m_pFont->SetWeight (wxNORMAL);
+    m_pFont->SetStyle (wxNORMAL);
+    m_pFont->SetFamily (wxROMAN);
+#ifdef MSVC
+    m_pFont->SetFaceName(wxString("times new roman"));
 #endif
-    if (gid == _sgp2_cwin)
-       _sgp2_cwin = NULL;
+    m_driver.idWX()->SetFont (*m_pFont);
+    double dTestCharHeight = m_driver.idWX()->GetCharHeight();
+    m_dPointsPerPixel = iTestPointSize / dTestCharHeight;
+        m_driver.idWX()->SetBackground (*wxWHITE_BRUSH);
+  }
+}
+#endif
+
 
-    delete gid;
+SGP::~SGP()
+{
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+ //   m_driver.idWX()->SetFont (wxNullFont);
+    delete m_pFont;
+  }
+#endif
 }
 
 void
-sgp2_set_active_win (SGP_ID gid)
+SGP::stylusNDC (double x, double y, bool beam)
 {
-    _sgp2_cwin = gid;
+  int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
+  int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
+  if (m_driver.isWX())
+    yp = m_iPhysicalYSize - yp;
+
+  if (beam) {
+#if HAVE_WXWINDOWS
+    if (m_driver.isWX())
+      m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
+#endif
+#if HAVE_G2_H
+    if (m_driver.isG2())
+      g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
+#endif
+  }
+  m_iCurrentPhysicalX = xp;
+  m_iCurrentPhysicalY = yp;
+}
+
+void
+SGP::markerNDC (double x, double y)
+{
+  int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
+  int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
+  if (m_driver.isWX())
+    yp = m_iPhysicalYSize - yp;
+
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+      m_driver.idWX()->DrawPoint (xp, yp);
+      m_driver.idWX()->DrawPoint (xp-1, yp-1);
+      m_driver.idWX()->DrawPoint (xp+1, yp+1);
+      m_driver.idWX()->DrawPoint (xp+1, yp-1);
+      m_driver.idWX()->DrawPoint (xp-1, yp+1);
+  }
+#endif
+  m_iCurrentPhysicalX = xp;
+  m_iCurrentPhysicalY = yp;
 }
 
-SGP_ID
-sgp2_get_active_win (void)
+void
+SGP::pointNDC (double x, double y)
 {
-    return (_sgp2_cwin);
+  int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
+  int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
+  if (m_driver.isWX())
+    yp = m_iPhysicalYSize - yp;
+
+#if HAVE_WXWINDOWS
+    if (m_driver.isWX())
+      m_driver.idWX()->DrawPoint (xp, yp);
+#endif
+  m_iCurrentPhysicalX = xp;
+  m_iCurrentPhysicalY = yp;
 }
 
 
-/* NAME
- *     sgp2_clear          Clear window
- */
+// NAME
+//    clear     Clear Window
 
-void 
-sgp2_clear ()
+void
+SGP::eraseWindow ()
 {
-#if HAVE_G2
-    if (_sgp_cwin != NULL)
-       g2_clear (gid->g2_id);
+#if HAVE_G2_H
+  if (m_driver.isG2())
+    g2_clear (m_driver.idG2());
+#endif
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+        wxBrush brushWhite;
+        brushWhite.SetColour(255,255,255);
+        m_driver.idWX()->SetBackground(brushWhite);
+        m_driver.idWX()->Clear();
+        m_driver.idWX()->SetBackground(wxNullBrush);
+#if 1
+        wxPen pen;
+        pen.SetColour(255,255,255);
+        m_driver.idWX()->SetBrush (brushWhite);
+        m_driver.idWX()->DrawRectangle (0, 0, m_iPhysicalXSize, m_iPhysicalYSize);
+        m_driver.idWX()->SetBrush (wxNullBrush);
+#endif
+  }
 #endif
 }
 
-/* NAME
- *     sgp2_window             Set window in world coordinates
- */
+// NAME
+//      sgp2_window             Set window in world coordinates
+
 
 void
-sgp2_window (double xmin, double ymin, double xmax, double ymax)
+SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
 {
-    if (_sgp2_cwin == NULL)
-       return;
-
-    if (xmin >= xmax || ymin >= ymax) {
-       sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
-       return;
-    }
+  if (xmin >= xmax || ymin >= ymax) {
+    sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
+    return;
+  }
 
-    _sgp2_cwin->xw_min = xmin;
-    _sgp2_cwin->yw_min = ymin;
-    _sgp2_cwin->xw_max = xmax;
-    _sgp2_cwin->yw_max = ymax;
-    calc_wc_to_ndc();
+  xw_min = xmin;
+  yw_min = ymin;
+  xw_max = xmax;
+  yw_max = ymax;
+  m_bRecalcTransform = true;
 }
 
 
-/* NAME
- *     sgp2_viewport                   Set viewport in NDC
- */
+// NAME
+//      sgp2_viewport                   Set viewport in NDC
 
 void
-sgp2_viewport (double xmin, double ymin, double xmax, double ymax)
+SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
 {
-    if (_sgp2_cwin == NULL)
-       return;
+  if (xmin >= xmax || ymin >= ymax) {
+    sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
+    return;
+  }
 
-    if (xmin >= xmax || ymin >= ymax) {
-       sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
-       return;
-    }
-    
-    _sgp2_cwin->xv_min = xmin;
-    _sgp2_cwin->yv_min = ymin;
-    _sgp2_cwin->xv_max = xmax;
-    _sgp2_cwin->yv_max = ymax;
-    calc_wc_to_ndc();
-    
-    _sgp2_cwin->view[0] = xmin;                        /* Array for clip_rect() */
-    _sgp2_cwin->view[1] = ymin;
-    _sgp2_cwin->view[2] = xmax;
-    _sgp2_cwin->view[3] = ymax;
-}
+  xv_min = xmin;
+  yv_min = ymin;
+  xv_max = xmax;
+  yv_max = ymax;
+  m_bRecalcTransform = true;
 
-
-/* NAME
- *     sgp2_frame_vpt          draw box around viewport
- */
+  viewNDC[0] = xmin;                    // Array for clip_rect()
+  viewNDC[1] = ymin;
+  viewNDC[2] = xmax;
+  viewNDC[3] = ymax;
+}
 
 void
-sgp2_frame_vpt (void)
+SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
 {
-    if (_sgp2_cwin == NULL)
-       return;
+    xmin = xv_min;
+    ymin = yv_min;
+    xmax = xv_max;
+    ymax = yv_max;
+}
 
-    _sgp2_stylus (_sgp2_cwin, _sgp2_cwin->xv_min, _sgp2_cwin->yv_min, 0);
-    _sgp2_stylus (_sgp2_cwin, _sgp2_cwin->xv_max, _sgp2_cwin->yv_min, 1);
-    _sgp2_stylus (_sgp2_cwin, _sgp2_cwin->xv_max, _sgp2_cwin->yv_max, 1);
-    _sgp2_stylus (_sgp2_cwin, _sgp2_cwin->xv_min, _sgp2_cwin->yv_max, 1);
-    _sgp2_stylus (_sgp2_cwin, _sgp2_cwin->xv_min, _sgp2_cwin->yv_min, 1);
+void
+SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
+{
+    xmin = xw_min;
+    ymin = yw_min;
+    xmax = xw_max;
+    ymax = yw_max;
 }
 
 
-/* NAME
- *     calc_wc_to_ndc                  Calculate transform matrix
- */
+// NAME
+//      frameViewport           draw box around viewport
 
 void
-calc_wc_to_ndc (void)
+SGP::frameViewport (void)
 {
-    double sx, sy;
-    
-    if (_sgp2_cwin == NULL)
-       return;
-
-    sx = (_sgp2_cwin->xv_max - _sgp2_cwin->xv_min) / (_sgp2_cwin->xw_max - _sgp2_cwin->xw_min);
-    sy = (_sgp2_cwin->yv_max - _sgp2_cwin->yv_min) / (_sgp2_cwin->yw_max - _sgp2_cwin->yw_min);
-    
-    ident_gmtx_2 (_sgp2_cwin->wc_to_ndc_x);
-    _sgp2_cwin->wc_to_ndc_x[0][0] = sx;
-    _sgp2_cwin->wc_to_ndc_x[2][0] = _sgp2_cwin->xv_min - sx * _sgp2_cwin->xw_min;
-    _sgp2_cwin->wc_to_ndc_x[1][1] = sy;
-    _sgp2_cwin->wc_to_ndc_x[2][1] = _sgp2_cwin->yv_min - sy * _sgp2_cwin->yw_min;
-    
-    _sgp2_cwin->recalc_mc_to_ndc = TRUE;
-    _sgp2_cwin->recalc_ndc_to_mc = TRUE;
+  stylusNDC (xv_min, yv_min, false);
+  stylusNDC (xv_max, yv_min, true);
+  stylusNDC (xv_max, yv_max, true);
+  stylusNDC (xv_min, yv_max, true);
+  stylusNDC (xv_min, yv_min, true);
 }
 
+void
+SGP::setTextColor (int iFGcolor, int iBGcolor)
+{
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+    if (iFGcolor >= 0) {
+      wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
+      m_driver.idWX()->SetTextForeground (colour);
+    }
+    if (iBGcolor >= 0) {
+      wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
+      m_driver.idWX()->SetTextBackground (colour);
+    }
+  }
+#endif
+}
 
-void 
-calc_ndc_to_mc (void)
+void
+SGP::setColor (int icol)
 {
-    if (_sgp2_cwin == NULL)
-       return;
+  if (icol >= 0 && icol < s_iRGBColorCount) {
+#if HAVE_G2_H
+    if (m_driver.isG2()) {
+      int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
+      g2_pen (m_driver.idG2(), iInk);
+    }
+#endif
+#if HAVE_WXWINDOWS
+    if (m_driver.isWX()) {
+      wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
+      m_pen.SetColour (colour);
+      m_driver.idWX()->SetPen (m_pen);
+    }
+#endif
+  }
+}
 
-    if (_sgp2_cwin->recalc_mc_to_ndc) {
-       mult_gmtx_2 (_sgp2_cwin->ctm_2_x, _sgp2_cwin->wc_to_ndc_x, _sgp2_cwin->mc_to_ndc_x);
-       _sgp2_cwin->recalc_mc_to_ndc = FALSE;
+void
+SGP::setPenWidth (int iWidth)
+{
+  if (iWidth >= 0) {
+#if HAVE_WXWINDOWS
+    if (m_driver.isWX()) {
+      m_pen.SetWidth (iWidth);
+      m_driver.idWX()->SetPen (m_pen);
     }
-    
-    invert_gmtx_2 (_sgp2_cwin->mc_to_ndc_x, _sgp2_cwin->ndc_to_mc_x);
-    _sgp2_cwin->recalc_ndc_to_mc = FALSE;
+#endif
+  }
 }
 
+void
+SGP::setRasterOp (int ro)
+{
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+    int wxFxn = -1;
+    switch (ro) {
+    case RO_AND:
+      wxFxn = wxAND;
+      break;
+    case RO_AND_INVERT:
+      wxFxn = wxAND_INVERT;
+      break;
+    case RO_AND_REVERSE:
+      wxFxn = wxAND_REVERSE;
+      break;
+    case RO_CLEAR:
+      wxFxn = wxCLEAR;
+      break;
+    case RO_COPY:
+      wxFxn = wxCOPY;
+      break;
+    case RO_EQUIV:
+      wxFxn = wxEQUIV;
+      break;
+    case RO_INVERT:
+      wxFxn = wxINVERT;
+      break;
+    case RO_NAND:
+      wxFxn = wxNAND;
+      break;
+    case RO_NOR:
+      wxFxn = wxNOR;
+      break;
+    case RO_NO_OP:
+      wxFxn = wxNO_OP;
+      break;
+    case RO_OR:
+      wxFxn = wxOR;
+      break;
+    case RO_OR_INVERT:
+      wxFxn = wxOR_INVERT;
+      break;
+    case RO_OR_REVERSE:
+      wxFxn = wxOR_REVERSE;
+      break;
+    case RO_SET:
+      wxFxn = wxSET;
+      break;
+    case RO_SRC_INVERT:
+      wxFxn = wxSRC_INVERT;
+      break;
+    case RO_XOR:
+      wxFxn = wxXOR;
+      break;
+    }
+    if (wxFxn >= 0)
+      m_driver.idWX()->SetLogicalFunction (wxFxn);
+  }
+#endif
+}
 
-/* NAME
- *     wc_to_ndc                       Map from world coordinates to NDC
- */
 
-void 
-wc_to_ndc (double xw, double yw, double *xn, double *yn)
+void
+SGP::setMarker (int idMarker)
 {
-    if (_sgp2_cwin == NULL)
-       return;
+  m_iMarker = idMarker;
+}
 
-    if (_sgp2_cwin->recalc_mc_to_ndc) {
-       mult_gmtx_2 (_sgp2_cwin->ctm_2_x, _sgp2_cwin->wc_to_ndc_x, _sgp2_cwin->mc_to_ndc_x);
-       _sgp2_cwin->recalc_mc_to_ndc = FALSE;
+//==============================================================
+// set line style.  Pass 16 bit repeating pattern
+//==============================================================
+void
+SGP::setLineStyle (int style)
+{
+  m_iLinestyle = style;
+
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+    switch (m_iLinestyle) {
+    case LS_SOLID:
+      m_pen.SetStyle (wxSOLID);
+      break;
+    case LS_DASH1:
+      m_pen.SetStyle (wxLONG_DASH);
+      break;
+    case LS_DASH2:
+      m_pen.SetStyle (wxSHORT_DASH);
+      break;
+    case LS_DASH3:
+      m_pen.SetStyle (wxDOT_DASH);
+      break;
+    case LS_DASH4:
+      m_pen.SetStyle (wxCROSS_HATCH);
+      break;
+    case LS_DOTTED:
+      m_pen.SetStyle (wxDOT);
+      break;
+    default:
+      m_pen.SetStyle (wxSOLID);
+      break;
     }
-
-    *xn = xw * _sgp2_cwin->mc_to_ndc_x[0][0] + yw * _sgp2_cwin->mc_to_ndc_x[1][0] + _sgp2_cwin->mc_to_ndc_x[2][0];
-    *yn = xw * _sgp2_cwin->mc_to_ndc_x[0][1] + yw * _sgp2_cwin->mc_to_ndc_x[1][1] + _sgp2_cwin->mc_to_ndc_x[2][1];
+    m_driver.idWX()->SetPen (m_pen);
+  }
+#endif
 }
 
+//==============================================================
+// absolute draw to
+//*==============================================================
 
-/*==============================================================*/
-/* map from normalized device coords. to world coordinates     */
-/*==============================================================*/
 void
-ndc_to_wc (double xn, double yn, double *xw, double *yw)
+SGP::lineAbs (double x, double y)
 {
-    if (_sgp2_cwin == NULL)
-       return;
+  if (m_bRecalcTransform)
+    calc_transform();
 
-    if (_sgp2_cwin->recalc_ndc_to_mc) {
-       calc_ndc_to_mc();
-       _sgp2_cwin->recalc_ndc_to_mc = FALSE;
-    }
+  double x1 = m_dCurrentWorldX;
+  double y1 = m_dCurrentWorldY;
+  mc_to_ndc.transformPoint (&x1, &y1);
+
+  double x2 = x;
+  double y2 = y;
+  mc_to_ndc.transformPoint (&x2, &y2);
 
-    *xw = xn * _sgp2_cwin->ndc_to_mc_x[0][0] + yn * _sgp2_cwin->ndc_to_mc_x[1][0] + _sgp2_cwin->ndc_to_mc_x[2][0];
-    *yw = xn * _sgp2_cwin->ndc_to_mc_x[0][1] + yn * _sgp2_cwin->ndc_to_mc_x[1][1] + _sgp2_cwin->ndc_to_mc_x[2][1];
+  if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
+    stylusNDC (x1, y1, false);  // move to first point
+    stylusNDC (x2, y2, true);  // draw to second point
+  }
+
+  m_dCurrentWorldX = x;
+  m_dCurrentWorldY = y;
 }
 
+void
+SGP::moveAbs (double x, double y)
+{
+    m_dCurrentWorldX = x;
+    m_dCurrentWorldY = y;                       /* moves are not clipped */
+}
 
-/*==============================================================*/
-/* set the color                                               */
-/*==============================================================*/
-void 
-sgp2_color (int icol)
+void
+SGP::lineRel (double x, double y)
 {
-    setcolor(icol);
+  lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
 }
 
-/*==============================================================*/
-/* set line style.  Pass 16 bit repeating pattern              */
-/*==============================================================*/
-void 
-sgp2_line_style (int style)
+void
+SGP::moveRel (double x, double y)
 {
-    setlinestyle(style);
+  moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
 }
 
-/*==============================================================*/
-/* absolute draw to                                            */
-/*==============================================================*/
-void 
-sgp2_line_abs (double x, double y)
+
+// Height is in master coordinates
+void
+SGP::setTextSize (double height)
 {
-    double x1, y1, x2, y2;
+    height /= (yw_max - yw_min);  // convert to NDC
+#if HAVE_G2_H
+  if (m_driver.isG2())
+    g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
+#endif
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+      double dHeightPixels = height * m_iPhysicalYSize;
+      double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
+      m_pFont->SetPointSize (nearest<int>(dHeightPoints));
+      m_driver.idWX()->SetFont (*m_pFont);
+  }
+#endif
+}
 
-    if (_sgp2_cwin == NULL)
-       return;
+void
+SGP::setTextNDCSize (double height)
+{
+    double dHeightPixels = height * m_iPhysicalYSize;
+#if HAVE_G2_H
+  if (m_driver.isG2())
+    g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
+#endif
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+      double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
+      m_pFont->SetPointSize (nearest<int>(dHeightPoints));
+      m_driver.idWX()->SetFont (*m_pFont);
+  }
+#endif
+}
 
-    wc_to_ndc (_sgp2_cwin->curx, _sgp2_cwin->cury, &x1, &y1);
-    wc_to_ndc (x, y, &x2, &y2);
+void
+SGP::setTextPointSize (double height)
+{
+#if HAVE_G2_H
+    //  if (m_driver.isG2())
+    //    g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
+#endif
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+    m_iTextPointSize = static_cast<int>(height+0.5);
+      m_pFont->SetPointSize (m_iTextPointSize);
+      m_driver.idWX()->SetFont (*m_pFont);
+  }
+#endif
+}
 
-    if (clip_rect (x1, y1, x2, y2, _sgp2_cwin->view) == TRUE) { /* clip to viewport */
-       _sgp2_stylus (_sgp2_cwin, x1, y1, 0);           /* move to first point */
-       _sgp2_stylus (_sgp2_cwin, x2, y2, 1);           /* draw to second point */
+void
+SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
+{
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+    wxCoord deviceW, deviceH;
+    m_driver.idWX()->GetTextExtent (wxConvCurrent->cMB2WC(szText), &deviceW, &deviceH);
+    if (m_dTextAngle == 90 || m_dTextAngle == -90) {
+      wxCoord temp = deviceW;
+      deviceW = deviceH;
+      deviceH = temp;
     }
-
-    _sgp2_cwin->curx = x;
-    _sgp2_cwin->cury = y;
+    *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
+    *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
+  }
+#endif
 }
 
-/*==============================================================*/
-/* absolute move to                                            */
-/*==============================================================*/
-void 
-sgp2_move_abs (double x, double y)
+double
+SGP::getCharHeight ()
 {
-    if (_sgp2_cwin == NULL)
-       return;
+  double dHeight = (1. / 25.);
 
-    _sgp2_cwin->curx = x;
-    _sgp2_cwin->cury = y;                      /* moves are not clipped */
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+    dHeight = m_driver.idWX()->GetCharHeight();
+    dHeight /= static_cast<double>(m_iPhysicalYSize);
+          dHeight /= (yv_max - yv_min); // scale to viewport;
+  }
+#endif
+  dHeight *= (yw_max - yw_min);  // scale to world coordinates
+  return dHeight;
 }
 
-/*==============================================================*/
-/* vector draw                                                 */
-/*==============================================================*/
-void 
-sgp2_line_rel (double x, double y)
+double
+SGP::getCharWidth ()
 {
-    if (_sgp2_cwin != NULL)
-       sgp2_line_abs (x + _sgp2_cwin->curx, y + _sgp2_cwin->cury);
+  double dWidth = (1. / 80.);
+
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+    dWidth = m_driver.idWX()->GetCharWidth();
+    dWidth /= static_cast<double>(m_iPhysicalXSize);
+          dWidth /= (xv_max - xv_min); // scale to viewport
+  }
+#endif
+  dWidth *= (xw_max - xw_min); //scale to world coordinates
+  return dWidth;
 }
 
-/*==============================================================*/
-/* vector move                                                 */
-/*==============================================================*/
-void 
-sgp2_move_rel (double x, double y)
+void
+SGP::setTextAngle (double angle)
 {
-    if (_sgp2_cwin != NULL)
-       sgp2_move_abs (x + _sgp2_cwin->curx, y + _sgp2_cwin->cury);
+  m_dTextAngle = convertRadiansToDegrees(angle);
 }
 
-/*==============================================================*/
-/* draw text                                                   */
-/*==============================================================*/
-void 
-sgp2_draw_text (char *message)
+void
+SGP::polylineAbs (double x[], double y[], int n)
+{
+  if (m_bRecalcTransform)
+    calc_transform();
+
+  double x1 = x[0], y1 = y[0];
+  mc_to_ndc.transformPoint (&x1, &y1);
+  double x2 = x[1], y2 = y[1];
+  mc_to_ndc.transformPoint (&x2, &y2);
+
+  double xt = x2;       // don't pass (x2,y2) to clip, we need them
+  double yt = y2;       // as the beginning point of the next line
+
+  if (clip_rect (x1, y1, xt, yt, viewNDC)) {
+    stylusNDC (x1, y1, false);
+    stylusNDC (xt, yt, true);
+  }
+
+  for (int i = 2; i < n; i++) {
+    x1 = x2; y1 = y2;                   // NDC endpoint of last line
+    x2 = x[i];  y2 = y[i];
+    mc_to_ndc.transformPoint (&x2, &y2);
+    xt = x2;
+    yt = y2;
+    if (clip_rect (x1, y1, xt, yt, viewNDC)) {
+      stylusNDC (x1, y1, false);
+      stylusNDC (xt, yt, true);
+    }
+  }
+}
+
+
+void
+SGP::markerAbs (double x, double y)
 {
-    double sx,sy;
-    if (_sgp2_cwin == NULL)
-       return;
+  if (m_bRecalcTransform)
+    calc_transform();
 
-    wc_to_ndc (_sgp2_cwin->curx, _sgp2_cwin->cury, &sx, &sy);
-    _sgp2_stylus (_sgp2_cwin, sx, sy, 0);      /* move to location */
-    _sgp2_dev_text (_sgp2_cwin, message);
+  double xndc = x;
+  double yndc = y;
+  mc_to_ndc.transformPoint (&xndc, &yndc);
+  markerNDC (xndc, yndc);
+  m_dCurrentWorldX = x;
+  m_dCurrentWorldY = y;
 }
 
+
 void
-charsize (double wid, double height)
+SGP::markerRel (double x, double y)
 {
-    _sgp2_set_text (_sgp2_cwin, wid, height, cspec.textangle, cspec.font);
+  markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
 }
 
+
 void
-textangle (double angle)
+SGP::pointAbs (double x, double y)
 {
-    _sgp2_set_text (_sgp2_cwin, cspec.width, cspec.height, angle, cspec.font);
+  if (m_bRecalcTransform)
+    calc_transform();
+  double xndc = x, yndc = y;
+  mc_to_ndc.transformPoint (&xndc, &yndc);
+  pointNDC (xndc, yndc);
+  m_dCurrentWorldX = x;
+  m_dCurrentWorldY = y;
 }
 
-void 
-sgp2_polyline_abs (double x[], double y[], int n)
+
+void
+SGP::pointRel (double x, double y)
 {
-    double x1, y1, x2, y2;
-    int i;
-    double xt, yt;
+  pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
+}
 
-    if (_sgp2_cwin == NULL || n < 2)
-       return;
 
-    wc_to_ndc (x[0], y[0], &x1, &y1);
-    wc_to_ndc (x[1], y[1], &x2, &y2);
+void
+SGP::drawText (const std::string& rsMessage)
+{
+  drawText (rsMessage.c_str());
+}
 
-    xt = x2;           /* don't pass (x2,y2) to clip, we need them */
-    yt = y2;           /* as the beginning point of the next line */
+void
+SGP::drawText (const char *pszMessage)
+{
+  if (m_bRecalcTransform)
+    calc_transform();
 
-    if (clip_rect (x1, y1, xt, yt, _sgp2_cwin->view) == TRUE) {
-       _sgp2_stylus (_sgp2_cwin, x1, y1, 0);
-       _sgp2_stylus (_sgp2_cwin, xt, yt, 1);
-    }
+  double xndc = m_dCurrentWorldX;
+  double yndc = m_dCurrentWorldY;
+  mc_to_ndc.transformPoint (&xndc, &yndc);
 
-    for (i = 2; i < n; i++) {
-       x1 = x2;                        /* NDC endpoint of last line */
-       y1 = y2;
-       wc_to_ndc (x[i], y[i], &x2, &y2);
-       xt = x2;
-       yt = y2;
-       if (clip_rect (x1, y1, xt, yt, _sgp2_cwin->view) == TRUE) {
-           _sgp2_stylus (_sgp2_cwin, x1, y1, 0);
-           _sgp2_stylus (_sgp2_cwin, xt, yt, 1);
-       }
-    }
+  stylusNDC (xndc, yndc, false);            // move to location
+
+#if HAVE_G2_H
+  if (m_driver.isG2()) {
+    g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
+  }
+#endif
+#if HAVE_WXWINDOWS
+  if (m_driver.isWX()) {
+    m_driver.idWX()->DrawRotatedText (wxConvCurrent->cMB2WC(pszMessage), m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
+  }
+#endif
 }
 
 
-void 
-sgp2_mark_abs (double x, double y)
+// NAME
+//   drawRect                           Draw box in graphics mode
+//
+// SYNOPSIS
+//   drawbox (xmin, ymin, xmax, ymax)
+//   double xmin, ymin                  Lower left corner of box
+//   double xmax, ymax                  Upper left corner of box
+//
+// NOTES
+//   This routine leaves the current position of graphic cursor at lower
+//   left corner of box.
+
+void
+SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
 {
-    double xndc, yndc;
+        moveAbs (xmin, ymin);
+        lineAbs (xmax, ymin);
+        lineAbs (xmax, ymax);
+        lineAbs (xmin, ymax);
+        lineAbs (xmin, ymin);
+}
 
-    if (_sgp2_cwin == NULL)
-       return;
+// FUNCTION
+// sgp2_circle - draw circle of radius r at current center
 
-    wc_to_ndc (x, y, &xndc, &yndc);
-    markndc (_sgp2_cwin, xndc, yndc);
-    _sgp2_cwin->curx = x;
-    _sgp2_cwin->cury = y;
+void
+SGP::drawCircle (const double r)
+{
+        drawArc (r, 0.0, TWOPI);
 }
 
+//==============================================================
+// draw arc around current center.  angles in radius
+//==============================================================
 
-void 
-sgp2_mark_rel (double x, double y)
+void
+SGP::drawArc (const double r, double start, double stop)
 {
-    sgp2_mark_abs (x + _sgp2_cwin->curx, y + _sgp2_cwin->cury);
+  if (start > stop) {
+    double temp = start;
+    start = stop;
+    stop = temp;
+  }
+
+  double xCent = m_dCurrentWorldX;
+  double yCent = m_dCurrentWorldY;
+
+  double x = r * cos (start);
+  double y = r * sin (start);
+  moveAbs (xCent + x, yCent + y);          // move from center to start of arc
+
+  const double thetaIncrement = (5 * (TWOPI / 360));  // 5 degree increments
+  double cosTheta = cos (thetaIncrement);
+  double sinTheta = sin (thetaIncrement);
+
+  double angle;
+  for (angle = start; angle < stop; angle += thetaIncrement) {
+    double xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
+    double yp = sinTheta * x + cosTheta * y;
+    lineAbs (xCent + xp, yCent + yp);
+    x = xp; y = yp;
+  }
+
+  double c = cos (stop - angle);
+  double s = sin (stop - angle);
+  double xp = c * x - s * y;
+  double yp = s * x + c * y;
+  lineAbs (xCent + xp, yCent + yp);
+
+  moveAbs (xCent, yCent);               // move back to center of circle
 }
 
 
-void 
-sgp2_point_abs (double x, double y)
+
+///////////////////////////////////////////////////////////////////////
+// Coordinate Transformations
+///////////////////////////////////////////////////////////////////////
+
+
+void
+SGP::transformNDCtoMC (double* x, double* y)
 {
-    double xndc, yndc;
+  if (m_bRecalcTransform)
+    calc_transform();
+  ndc_to_mc.transformPoint (x, y);
+}
 
-    if (_sgp2_cwin == NULL)
-       return;;
 
-    wc_to_ndc (x, y, &xndc, &yndc);
-    pntndc (_sgp2_cwin, xndc, yndc);
-    _sgp2_cwin->curx = x;
-    _sgp2_cwin->cury = y;
+void
+SGP::transformMCtoNDC (double* x, double* y)
+{
+  if (m_bRecalcTransform)
+    calc_transform();
+  mc_to_ndc.transformPoint (x, y);
 }
 
 
-void 
-sgp2_point_rel (double x, double y)
+void
+SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
 {
-    sgp2_point_abs (x + _sgp2_cwin->curx, y + _sgp2_cwin->cury);
+  if (m_bRecalcTransform)
+    calc_transform();
+  *x = xIn;
+  *y = yIn;
+  mc_to_ndc.transformPoint (x, y);
 }
 
 
-/* NAME
- *   sgp2_draw_rect                            Draw box in graphics mode
- *
- * SYNOPSIS
- *   drawbox (xmin, ymin, xmax, ymax)
- *   double xmin, ymin                 Lower left corner of box
- *   double xmax, ymax                 Upper left corner of box
- *
- * NOTES
- *   This routine leaves the current position of graphic cursor at lower
- *   left corner of box.
- */
+// NAME
+//      calc_transform                  Calculate transform matrices
 
 void
-sgp2_draw_rect(double xmin, double ymin, double xmax, double ymax)
+SGP::calc_transform ()
 {
-       sgp2_move_abs (xmin, ymin);
-       sgp2_line_abs (xmax, ymin);
-       sgp2_line_abs (xmax, ymax);
-       sgp2_line_abs (xmin, ymax);
-       sgp2_line_abs (xmin, ymin);
+  double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
+  double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
+
+  wc_to_ndc.setIdentity();
+  wc_to_ndc.mtx[0][0] = scaleX;
+  wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
+  wc_to_ndc.mtx[1][1] = scaleY;
+  wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
+
+  mc_to_ndc = m_ctm * wc_to_ndc;
+  ndc_to_mc = mc_to_ndc.invert();
+
+  m_bRecalcTransform = false;
 }
 
-/* FUNCTION
- * sgp2_circle - draw circle of radius r at current center             
- */
+void
+SGP::ctmClear ()
+{
+  m_ctm.setIdentity();
+  calc_transform();
+}
 
-void 
-sgp2_draw_circle (const double r)
+void
+SGP::ctmSet (const TransformationMatrix2D& m)
 {
-       sgp2_draw_arc (0.0, 7.0, r);
+  m_ctm = m;
+  calc_transform();
 }
 
-/*==============================================================*/
-/* draw arc around current center.  pass angles and radius     */
-/*==============================================================*/
 
-void 
-sgp2_draw_arc (double start, double stop, const double r)
+void
+SGP::preTranslate  (double x, double y)
 {
-       double c, s, theta, angle;
-       float x, y, xp, yp;
+    TransformationMatrix2D m;
+
+    m.setTranslate (x, y);
+    ctmSet (m * m_ctm);
+}
 
-       if ((stop-start) > 2 * PI)
-           stop = start + 2 * PI;
-       if ((start-stop) > 2 * PI)
-           stop = start + 2 * PI;
-       while (start >= stop)
-           stop += 2*PI;
 
-       x = r * cos ((double) start);
-       y = r * sin ((double) start);
-       sgp2_move_rel (x, y);          /* move from center to start of arc */
+void
+SGP::postTranslate (double x, double y)
+{
+    TransformationMatrix2D m;
 
-       theta = 5 * PI / 180;
-       c = cos(theta);
-       s = sin(theta);
+    m.setTranslate (x, y);
+    ctmSet (m_ctm * m);
+}
 
-       for (angle = start; angle < stop - theta; angle += theta) {
-           xp = c * x - s * y;
-           yp = s * x + c * y;
-           sgp2_line_rel (xp - x, yp - y);
-           x = xp; y = yp;
-       }
 
-       c = cos (stop - angle);
-       s = sin (stop - angle);
-       xp = c * x - s * y;
-       yp = s * x + c * y;
-       sgp2_line_rel (xp - x, yp - y);
+void
+SGP::preScale (double sx, double sy)
+{
+    TransformationMatrix2D m;
 
-       x = r * cos ((double) stop);
-       y = r * sin ((double) stop);
-       sgp2_move_rel (-x, -y);         /* move back to center of circle */
+    m.setScale (sx, sy);
+    ctmSet (m * m_ctm);
 }
 
 
-/*----------------------------------------------------------------------*/
-/*                  Current Transformation Matrix Routine              */
-/*----------------------------------------------------------------------*/
+void
+SGP::postScale (double sx, double sy)
+{
+    TransformationMatrix2D m;
 
+    m.setScale (sx, sy);
+    m_ctm = m_ctm * m;
+    ctmSet (m_ctm * m);
+}
 
-/* NAME
- *     ctm_clr_2                       Clear current transformation matrix
- *
- * SYNOPSIS
- *     ctm_clr_2()
- */
 
-void 
-ctm_clr_2 (void)
+void
+SGP::preRotate (double theta)
 {
-    GRFMTX_2D m;
+    TransformationMatrix2D m;
 
-    ident_gmtx_2 (m);
-    ctm_set_2 (m);
+    m.setRotate (theta);
+    m_ctm = m * m_ctm;
+    ctmSet (m * m_ctm);
 }
 
 
-/* NAME
- *     ctm_get_2                       Get ctm
- *
- * SYNOPSIS
- *     ctm_get_2 (m)
- * OUT GRFMTX_2D m                     Copy of ctm
- */
-
-void 
-ctm_get_2 (GRFMTX_2D m)
+void
+SGP::postRotate (double theta)
 {
-    int x, y;
-
-    if (_sgp2_cwin == NULL)
-       return;
+    TransformationMatrix2D m;
 
-    for (x = 0; x < 3; x++)
-       for (y = 0; y < 3; y++)
-           m[x][y] = _sgp2_cwin->ctm_2_x[x][y];
+    m.setRotate (theta);
+    ctmSet (m_ctm * m);
 }
 
 
-/* NAME
- *     ctm_set_2                       Set ctm to a matrix
- *
- * SYNOPSIS
- *     ctm_get_ctm_2 (m)
- * IN  GRFMTX m                        New ctm
- */
-
-void 
-ctm_set_2 (GRFMTX_2D m)
+void
+SGP::preShear (double shrx, double shry)
 {
-    int x, y;
-    if (_sgp2_cwin == NULL)
-       return;
-
-    for (x = 0; x < 3; x++)
-       for (y = 0; y < 3; y++)
-           _sgp2_cwin->ctm_2_x[x][y] = m[x][y];
+    TransformationMatrix2D m;
 
-    _sgp2_cwin->recalc_ndc_to_mc = TRUE;
-    _sgp2_cwin->recalc_mc_to_ndc = TRUE;
+    m.setShear (shrx, shry);
+    ctmSet (m * m_ctm);
 }
 
 
-void 
-ctm_pre_mult_2 (GRFMTX_2D m)
+void
+SGP::postShear (double shrx, double shry)
 {
-    GRFMTX_2D new_ctm;
+    TransformationMatrix2D m;
 
-    mult_gmtx_2 (m, _sgp2_cwin->ctm_2_x, new_ctm);
-    ctm_set_2 (new_ctm);
+    m.setShear (shrx, shry);
+    ctmSet (m_ctm * m);
 }
 
 
-void 
-ctm_post_mult_2 (GRFMTX_2D m)
+////////////////////////////////////////////////////////////////////////
+//  Bitmap Markers
+////////////////////////////////////////////////////////////////////////
+
+// Pixel patterns of marker symbols (1x1 to 5x5 matrix)
+const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
 {
-    GRFMTX_2D new_ctm;
+    {'\000', '\000', '\010', '\000', '\000'},    // small dot
+    {'\000', '\034', '\024', '\034', '\000'},    // empty square
+    {'\000', '\034', '\034', '\034', '\000'},    // filled square
+    {'\000', '\010', '\024', '\010', '\000'},    // empty diamond
+    {'\000', '\010', '\034', '\010', '\000'},    // filled diamond
+    {'\010', '\010', '\076', '\010', '\010'},    // cross
+    {'\000', '\024', '\010', '\024', '\000'},    // X
+    {'\034', '\042', '\042', '\042', '\034'},    // open circle
+    {'\034', '\076', '\076', '\076', '\034'},    // filled circle
+    {'\076', '\042', '\042', '\042', '\076'},    // big open square
+    {'\010', '\024', '\042', '\024', '\010'},    // big open diamond
+};
+
 
-    mult_gmtx_2 (_sgp2_cwin->ctm_2_x, m, new_ctm);
-    ctm_set_2 (new_ctm);
+#if HAVE_WXWINDOWS
+void
+SGP::setDC (wxDC* pDC)
+{
+  if (m_driver.isWX()) {
+    m_driver.setDC(pDC);
+    initFromDC (pDC);
+    setTextPointSize (m_iTextPointSize);
+  }
 }
+#endif