1 /*****************************************************************************
4 ** Name: sgp.cpp Simple Graphics Package
5 ** Programmer: Kevin Rosenberg
7 ** This is part of the CTSim program
8 ** Copyright (c) 1983-2001 Kevin Rosenberg
10 ** $Id: sgp.cpp,v 1.30 2001/03/10 23:14:16 kevin Exp $
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.
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.
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 ******************************************************************************/
28 #include "ctsupport.h"
32 RGBColor SGP::s_aRGBColor[] =
37 RGBColor (0, 128, 128),
39 RGBColor (128, 0, 128),
40 RGBColor (128, 128, 0),
41 RGBColor (80, 80, 80),
42 RGBColor (160, 160, 160),
45 RGBColor (0, 255, 255),
47 RGBColor (255, 0, 255),
48 RGBColor (255, 255, 0),
49 RGBColor (255, 255, 255),
52 int SGP::s_iRGBColorCount = sizeof(s_aRGBColor) / sizeof(class RGBColor);
55 SGPDriver::SGPDriver (wxDC* pDC, int xsize, int ysize)
56 : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_idDriver(0), m_pDC(pDC)
58 m_idDriver |= SGPDRIVER_WXWINDOWS;
62 SGPDriver::SGPDriver (const char* szWinTitle, int xsize, int ysize)
63 : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_sWindowTitle(szWinTitle), m_idDriver(0)
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;
71 SGPDriver::~SGPDriver ()
81 // SGP::SGP Constructor for Simple Graphics Package
83 SGP::SGP (const SGPDriver& driver)
86 m_iPhysicalXSize = m_driver.getPhysicalXSize();
87 m_iPhysicalYSize = m_driver.getPhysicalYSize();
89 wc_to_ndc.setIdentity ();
90 mc_to_ndc.setIdentity();
91 ndc_to_mc.setIdentity();
95 initFromDC (driver.idWX());
98 setWindow (0., 0., 1., 1.);
99 setViewport (0., 0., 1., 1.);
101 stylusNDC (0., 0., false);
104 setTextPointSize (12);
106 setLineStyle (LS_SOLID);
110 #ifdef HAVE_WXWINDOWS
112 SGP::initFromDC (wxDC* pDC)
116 if (m_driver.isWX()) {
117 static const double dScreenDPI = 82;
118 static const double dPointsPerInch = 72.;
119 m_dPointsPerPixel = dPointsPerInch / dScreenDPI;
120 const int iTestPointSize = 12;
121 m_pFont = new wxFont (wxROMAN, wxNORMAL, wxNORMAL, wxNORMAL);
122 m_pFont->SetPointSize (iTestPointSize);
123 m_pFont->SetWeight (wxNORMAL);
124 m_pFont->SetStyle (wxNORMAL);
125 m_pFont->SetFamily (wxROMAN);
127 m_pFont->SetFaceName(wxString("times new roman"));
129 m_driver.idWX()->SetFont (*m_pFont);
130 double dTestCharHeight = m_driver.idWX()->GetCharHeight();
131 m_dPointsPerPixel = iTestPointSize / dTestCharHeight;
132 m_driver.idWX()->SetBackground (*wxWHITE_BRUSH);
141 if (m_driver.isWX()) {
142 // m_driver.idWX()->SetFont (wxNullFont);
149 SGP::stylusNDC (double x, double y, bool beam)
151 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
152 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
154 yp = m_iPhysicalYSize - yp;
159 m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
163 g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
166 m_iCurrentPhysicalX = xp;
167 m_iCurrentPhysicalY = yp;
171 SGP::markerNDC (double x, double y)
176 SGP::pointNDC (double x, double y)
178 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
179 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
181 yp = m_iPhysicalYSize - yp;
185 m_driver.idWX()->DrawPoint (xp, yp);
187 m_iCurrentPhysicalX = xp;
188 m_iCurrentPhysicalY = yp;
193 // clear Clear Window
200 g2_clear (m_driver.idG2());
203 if (m_driver.isWX()) {
205 brushWhite.SetColour(255,255,255);
206 m_driver.idWX()->SetBackground(brushWhite);
207 m_driver.idWX()->Clear();
208 m_driver.idWX()->SetBackground(wxNullBrush);
211 pen.SetColour(255,255,255);
212 m_driver.idWX()->SetBrush (brushWhite);
213 m_driver.idWX()->DrawRectangle (0, 0, m_iPhysicalXSize, m_iPhysicalYSize);
214 m_driver.idWX()->SetBrush (wxNullBrush);
221 // sgp2_window Set window in world coordinates
225 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
227 if (xmin >= xmax || ymin >= ymax) {
228 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
236 m_bRecalcTransform = true;
241 // sgp2_viewport Set viewport in NDC
244 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
246 if (xmin >= xmax || ymin >= ymax) {
247 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
255 m_bRecalcTransform = true;
257 viewNDC[0] = xmin; // Array for clip_rect()
264 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
273 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
283 // frameViewport draw box around viewport
286 SGP::frameViewport (void)
288 stylusNDC (xv_min, yv_min, false);
289 stylusNDC (xv_max, yv_min, true);
290 stylusNDC (xv_max, yv_max, true);
291 stylusNDC (xv_min, yv_max, true);
292 stylusNDC (xv_min, yv_min, true);
296 SGP::setTextColor (int iFGcolor, int iBGcolor)
299 if (m_driver.isWX()) {
301 wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
302 m_driver.idWX()->SetTextForeground (colour);
305 wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
306 m_driver.idWX()->SetTextBackground (colour);
313 SGP::setColor (int icol)
315 if (icol >= 0 && icol < s_iRGBColorCount) {
317 if (m_driver.isG2()) {
318 int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
319 g2_pen (m_driver.idG2(), iInk);
323 if (m_driver.isWX()) {
324 wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
325 m_pen.SetColour (colour);
326 m_driver.idWX()->SetPen (m_pen);
333 SGP::setPenWidth (int iWidth)
337 if (m_driver.isWX()) {
338 m_pen.SetWidth (iWidth);
339 m_driver.idWX()->SetPen (m_pen);
346 SGP::setRasterOp (int ro)
349 if (m_driver.isWX()) {
356 wxFxn = wxAND_INVERT;
359 wxFxn = wxAND_REVERSE;
389 wxFxn = wxOR_REVERSE;
395 wxFxn = wxSRC_INVERT;
402 m_driver.idWX()->SetLogicalFunction (wxFxn);
409 SGP::setMarker (int idMarke, int iColor)
413 //==============================================================
414 // set line style. Pass 16 bit repeating pattern
415 //==============================================================
417 SGP::setLineStyle (int style)
419 m_iLinestyle = style;
422 if (m_driver.isWX()) {
423 switch (m_iLinestyle) {
425 m_pen.SetStyle (wxSOLID);
428 m_pen.SetStyle (wxLONG_DASH);
431 m_pen.SetStyle (wxSHORT_DASH);
434 m_pen.SetStyle (wxDOT_DASH);
437 m_pen.SetStyle (wxCROSS_HATCH);
440 m_pen.SetStyle (wxDOT);
443 m_pen.SetStyle (wxSOLID);
446 m_driver.idWX()->SetPen (m_pen);
451 //==============================================================
453 //*==============================================================
456 SGP::lineAbs (double x, double y)
458 if (m_bRecalcTransform)
461 double x1 = m_dCurrentWorldX;
462 double y1 = m_dCurrentWorldY;
463 mc_to_ndc.transformPoint (&x1, &y1);
467 mc_to_ndc.transformPoint (&x2, &y2);
469 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
470 stylusNDC (x1, y1, false); // move to first point
471 stylusNDC (x2, y2, true); // draw to second point
474 m_dCurrentWorldX = x;
475 m_dCurrentWorldY = y;
479 SGP::moveAbs (double x, double y)
481 m_dCurrentWorldX = x;
482 m_dCurrentWorldY = y; /* moves are not clipped */
486 SGP::lineRel (double x, double y)
488 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
492 SGP::moveRel (double x, double y)
494 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
498 // Height is in master coordinates
500 SGP::setTextSize (double height)
502 height /= (yw_max - yw_min); // convert to NDC
505 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
508 if (m_driver.isWX()) {
509 double dHeightPixels = height * m_iPhysicalYSize;
510 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
511 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
512 m_driver.idWX()->SetFont (*m_pFont);
518 SGP::setTextNDCSize (double height)
520 double dHeightPixels = height * m_iPhysicalYSize;
523 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
526 if (m_driver.isWX()) {
527 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
528 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
529 m_driver.idWX()->SetFont (*m_pFont);
535 SGP::setTextPointSize (double height)
538 // if (m_driver.isG2())
539 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
542 if (m_driver.isWX()) {
543 m_iTextPointSize = static_cast<int>(height+0.5);
544 m_pFont->SetPointSize (m_iTextPointSize);
545 m_driver.idWX()->SetFont (*m_pFont);
551 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
554 if (m_driver.isWX()) {
555 wxString sText (szText);
556 wxCoord deviceW, deviceH;
557 m_driver.idWX()->GetTextExtent (sText, &deviceW, &deviceH);
558 if (m_dTextAngle == 90 || m_dTextAngle == -90) {
559 wxCoord temp = deviceW;
563 *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
564 *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
570 SGP::getCharHeight ()
572 double dHeight = (1. / 25.);
575 if (m_driver.isWX()) {
576 dHeight = m_driver.idWX()->GetCharHeight();
577 dHeight /= static_cast<double>(m_iPhysicalYSize);
578 dHeight /= (yv_max - yv_min); // scale to viewport;
581 dHeight *= (yw_max - yw_min); // scale to world coordinates
588 double dWidth = (1. / 80.);
591 if (m_driver.isWX()) {
592 dWidth = m_driver.idWX()->GetCharWidth();
593 dWidth /= static_cast<double>(m_iPhysicalXSize);
594 dWidth /= (xv_max - xv_min); // scale to viewport
597 dWidth *= (xw_max - xw_min); //scale to world coordinates
602 SGP::setTextAngle (double angle)
604 m_dTextAngle = convertRadiansToDegrees(angle);
608 SGP::polylineAbs (double x[], double y[], int n)
610 if (m_bRecalcTransform)
613 double x1 = x[0], y1 = y[0];
614 mc_to_ndc.transformPoint (&x1, &y1);
615 double x2 = x[1], y2 = y[1];
616 mc_to_ndc.transformPoint (&x2, &y2);
618 double xt = x2; // don't pass (x2,y2) to clip, we need them
619 double yt = y2; // as the beginning point of the next line
621 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
622 stylusNDC (x1, y1, false);
623 stylusNDC (xt, yt, true);
626 for (int i = 2; i < n; i++) {
627 x1 = x2; y1 = y2; // NDC endpoint of last line
628 x2 = x[i]; y2 = y[i];
629 mc_to_ndc.transformPoint (&x2, &y2);
632 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
633 stylusNDC (x1, y1, false);
634 stylusNDC (xt, yt, true);
641 SGP::markerAbs (double x, double y)
643 if (m_bRecalcTransform)
648 mc_to_ndc.transformPoint (&xndc, &yndc);
649 markerNDC (xndc, yndc);
650 stylusNDC (xndc, yndc, false); // move to location
651 m_dCurrentWorldX = x;
652 m_dCurrentWorldY = y;
657 SGP::markerRel (double x, double y)
659 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
664 SGP::pointAbs (double x, double y)
666 if (m_bRecalcTransform)
668 double xndc = x, yndc = y;
669 mc_to_ndc.transformPoint (&xndc, &yndc);
670 pointNDC (xndc, yndc);
671 m_dCurrentWorldX = x;
672 m_dCurrentWorldY = y;
677 SGP::pointRel (double x, double y)
679 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
684 SGP::drawText (const std::string& rsMessage)
686 drawText (rsMessage.c_str());
690 SGP::drawText (const char *pszMessage)
692 if (m_bRecalcTransform)
695 double xndc = m_dCurrentWorldX;
696 double yndc = m_dCurrentWorldY;
697 mc_to_ndc.transformPoint (&xndc, &yndc);
699 stylusNDC (xndc, yndc, false); // move to location
702 if (m_driver.isG2()) {
703 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
707 if (m_driver.isWX()) {
708 wxString str (pszMessage);
709 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
716 // drawRect Draw box in graphics mode
719 // drawbox (xmin, ymin, xmax, ymax)
720 // double xmin, ymin Lower left corner of box
721 // double xmax, ymax Upper left corner of box
724 // This routine leaves the current position of graphic cursor at lower
725 // left corner of box.
728 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
730 moveAbs (xmin, ymin);
731 lineAbs (xmax, ymin);
732 lineAbs (xmax, ymax);
733 lineAbs (xmin, ymax);
734 lineAbs (xmin, ymin);
738 // sgp2_circle - draw circle of radius r at current center
741 SGP::drawCircle (const double r)
743 drawArc (r, 0.0, TWOPI);
746 //==============================================================
747 // draw arc around current center. angles in radius
748 //==============================================================
751 SGP::drawArc (const double r, double start, double stop)
759 double xCent = m_dCurrentWorldX;
760 double yCent = m_dCurrentWorldY;
762 double x = r * cos (start);
763 double y = r * sin (start);
764 moveAbs (xCent + x, yCent + y); // move from center to start of arc
766 const double thetaIncrement = (5 * (TWOPI / 360)); // 5 degree increments
767 double cosTheta = cos (thetaIncrement);
768 double sinTheta = sin (thetaIncrement);
770 for (double angle = start; angle < stop; angle += thetaIncrement) {
771 double xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
772 double yp = sinTheta * x + cosTheta * y;
773 lineAbs (xCent + xp, yCent + yp);
777 double c = cos (stop - angle);
778 double s = sin (stop - angle);
779 double xp = c * x - s * y;
780 double yp = s * x + c * y;
781 lineAbs (xCent + xp, yCent + yp);
783 moveAbs (xCent, yCent); // move back to center of circle
788 ///////////////////////////////////////////////////////////////////////
789 // Coordinate Transformations
790 ///////////////////////////////////////////////////////////////////////
794 SGP::transformNDCtoMC (double* x, double* y)
796 if (m_bRecalcTransform)
798 ndc_to_mc.transformPoint (x, y);
803 SGP::transformMCtoNDC (double* x, double* y)
805 if (m_bRecalcTransform)
807 mc_to_ndc.transformPoint (x, y);
812 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
814 if (m_bRecalcTransform)
818 mc_to_ndc.transformPoint (x, y);
823 // calc_transform Calculate transform matrices
826 SGP::calc_transform ()
828 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
829 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
831 wc_to_ndc.setIdentity();
832 wc_to_ndc.mtx[0][0] = scaleX;
833 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
834 wc_to_ndc.mtx[1][1] = scaleY;
835 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
837 mc_to_ndc = m_ctm * wc_to_ndc;
838 ndc_to_mc = mc_to_ndc.invert();
840 m_bRecalcTransform = false;
851 SGP::ctmSet (const TransformationMatrix2D& m)
859 SGP::preTranslate (double x, double y)
861 TransformationMatrix2D m;
863 m.setTranslate (x, y);
869 SGP::postTranslate (double x, double y)
871 TransformationMatrix2D m;
873 m.setTranslate (x, y);
879 SGP::preScale (double sx, double sy)
881 TransformationMatrix2D m;
889 SGP::postScale (double sx, double sy)
891 TransformationMatrix2D m;
900 SGP::preRotate (double theta)
902 TransformationMatrix2D m;
911 SGP::postRotate (double theta)
913 TransformationMatrix2D m;
921 SGP::preShear (double shrx, double shry)
923 TransformationMatrix2D m;
925 m.setShear (shrx, shry);
931 SGP::postShear (double shrx, double shry)
933 TransformationMatrix2D m;
935 m.setShear (shrx, shry);
940 ////////////////////////////////////////////////////////////////////////
942 ////////////////////////////////////////////////////////////////////////
944 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
945 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
947 {'\000', '\000', '\010', '\000', '\000'}, // small dot
948 {'\000', '\034', '\024', '\034', '\000'}, // empty square
949 {'\000', '\034', '\034', '\034', '\000'}, // filled square
950 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
951 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
952 {'\010', '\010', '\076', '\010', '\010'}, // cross
953 {'\000', '\024', '\010', '\024', '\000'}, // X
954 {'\034', '\042', '\042', '\042', '\034'}, // open circle
955 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
956 {'\076', '\042', '\042', '\042', '\076'}, // big open square
957 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
963 SGP::setDC (wxDC* pDC)
965 if (m_driver.isWX()) {
968 setTextPointSize (m_iTextPointSize);