1 /*****************************************************************************
4 ** Name: sgp.cpp Simple Graphics Package
5 ** Programmer: Kevin Rosenberg
7 ** This is part of the CTSim program
8 ** Copyright (c) 1983-2009 Kevin Rosenberg
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.
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.
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 ******************************************************************************/
26 #include "ctsupport.h"
30 SGP_RGBColor SGP::s_aRGBColor[] =
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),
50 int SGP::s_iRGBColorCount = sizeof(s_aRGBColor) / sizeof(class SGP_RGBColor);
53 SGPDriver::SGPDriver (wxDC* pDC, int xsize, int ysize)
54 : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_idDriver(0), m_pDC(pDC)
56 m_idDriver |= SGPDRIVER_WXWINDOWS;
60 SGPDriver::SGPDriver (const char* szWinTitle, int xsize, int ysize)
61 : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_sWindowTitle(szWinTitle), m_idDriver(0)
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;
69 SGPDriver::~SGPDriver ()
79 // SGP::SGP Constructor for Simple Graphics Package
81 SGP::SGP (const SGPDriver& driver)
84 m_iPhysicalXSize = m_driver.getPhysicalXSize();
85 m_iPhysicalYSize = m_driver.getPhysicalYSize();
87 wc_to_ndc.setIdentity ();
88 mc_to_ndc.setIdentity();
89 ndc_to_mc.setIdentity();
93 initFromDC (driver.idWX());
96 setWindow (0., 0., 1., 1.);
97 setViewport (0., 0., 1., 1.);
99 stylusNDC (0., 0., false);
102 setTextPointSize (8);
104 setLineStyle (LS_SOLID);
105 setMarker (MARKER_POINT);
109 #ifdef HAVE_WXWINDOWS
111 SGP::initFromDC (wxDC* pDC)
115 if (m_driver.isWX()) {
116 static const double dScreenDPI = 82;
117 static const double dPointsPerInch = 72.;
118 m_dPointsPerPixel = dPointsPerInch / dScreenDPI;
119 const int iTestPointSize = 12;
120 m_pFont = new wxFont (iTestPointSize, wxFONTFAMILY_ROMAN,
121 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
123 m_pFont->SetFaceName(wxString("times new roman"));
125 m_driver.idWX()->SetFont (*m_pFont);
126 double dTestCharHeight = m_driver.idWX()->GetCharHeight();
127 m_dPointsPerPixel = iTestPointSize / dTestCharHeight;
128 m_driver.idWX()->SetBackground (*wxWHITE_BRUSH);
137 if (m_driver.isWX()) {
138 // m_driver.idWX()->SetFont (wxNullFont);
145 SGP::stylusNDC (double x, double y, bool beam)
147 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
148 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
150 yp = m_iPhysicalYSize - yp;
155 m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
159 g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
162 m_iCurrentPhysicalX = xp;
163 m_iCurrentPhysicalY = yp;
167 SGP::markerNDC (double x, double y)
169 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
170 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
172 yp = m_iPhysicalYSize - yp;
175 if (m_driver.isWX()) {
176 m_driver.idWX()->DrawPoint (xp, yp);
177 m_driver.idWX()->DrawPoint (xp-1, yp-1);
178 m_driver.idWX()->DrawPoint (xp+1, yp+1);
179 m_driver.idWX()->DrawPoint (xp+1, yp-1);
180 m_driver.idWX()->DrawPoint (xp-1, yp+1);
183 m_iCurrentPhysicalX = xp;
184 m_iCurrentPhysicalY = yp;
188 SGP::pointNDC (double x, double y)
190 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
191 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
193 yp = m_iPhysicalYSize - yp;
197 m_driver.idWX()->DrawPoint (xp, yp);
199 m_iCurrentPhysicalX = xp;
200 m_iCurrentPhysicalY = yp;
205 // clear Clear Window
212 g2_clear (m_driver.idG2());
215 if (m_driver.isWX()) {
217 brushWhite.SetColour(255,255,255);
218 m_driver.idWX()->SetBackground(brushWhite);
219 m_driver.idWX()->Clear();
220 m_driver.idWX()->SetBackground(wxNullBrush);
223 pen.SetColour(255,255,255);
224 m_driver.idWX()->SetBrush (brushWhite);
225 m_driver.idWX()->DrawRectangle (0, 0, m_iPhysicalXSize, m_iPhysicalYSize);
226 m_driver.idWX()->SetBrush (wxNullBrush);
233 // sgp2_window Set window in world coordinates
237 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
239 if (xmin >= xmax || ymin >= ymax) {
240 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
248 m_bRecalcTransform = true;
253 // sgp2_viewport Set viewport in NDC
256 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
258 if (xmin >= xmax || ymin >= ymax) {
259 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
267 m_bRecalcTransform = true;
269 viewNDC[0] = xmin; // Array for clip_rect()
276 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
285 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
295 // frameViewport draw box around viewport
298 SGP::frameViewport (void)
300 stylusNDC (xv_min, yv_min, false);
301 stylusNDC (xv_max, yv_min, true);
302 stylusNDC (xv_max, yv_max, true);
303 stylusNDC (xv_min, yv_max, true);
304 stylusNDC (xv_min, yv_min, true);
308 SGP::setTextColor (int iFGcolor, int iBGcolor)
311 if (m_driver.isWX()) {
313 wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
314 m_driver.idWX()->SetTextForeground (colour);
317 wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
318 m_driver.idWX()->SetTextBackground (colour);
325 SGP::setColor (int icol)
327 if (icol >= 0 && icol < s_iRGBColorCount) {
329 if (m_driver.isG2()) {
330 int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
331 g2_pen (m_driver.idG2(), iInk);
335 if (m_driver.isWX()) {
336 wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
337 m_pen.SetColour (colour);
338 m_driver.idWX()->SetPen (m_pen);
345 SGP::setPenWidth (int iWidth)
349 if (m_driver.isWX()) {
350 m_pen.SetWidth (iWidth);
351 m_driver.idWX()->SetPen (m_pen);
358 SGP::setRasterOp (int ro)
361 if (m_driver.isWX()) {
362 wxRasterOperationMode wxFxn = wxSET;
368 wxFxn = wxAND_INVERT;
371 wxFxn = wxAND_REVERSE;
401 wxFxn = wxOR_REVERSE;
407 wxFxn = wxSRC_INVERT;
413 m_driver.idWX()->SetLogicalFunction (wxFxn);
420 SGP::setMarker (int idMarker)
422 m_iMarker = idMarker;
425 //==============================================================
426 // set line style. Pass 16 bit repeating pattern
427 //==============================================================
429 SGP::setLineStyle (int style)
431 m_iLinestyle = style;
434 if (m_driver.isWX()) {
435 switch (m_iLinestyle) {
437 m_pen.SetStyle (wxSOLID);
440 m_pen.SetStyle (wxLONG_DASH);
443 m_pen.SetStyle (wxSHORT_DASH);
446 m_pen.SetStyle (wxDOT_DASH);
449 m_pen.SetStyle (wxCROSS_HATCH);
452 m_pen.SetStyle (wxDOT);
455 m_pen.SetStyle (wxSOLID);
458 m_driver.idWX()->SetPen (m_pen);
463 //==============================================================
465 //*==============================================================
468 SGP::lineAbs (double x, double y)
470 if (m_bRecalcTransform)
473 double x1 = m_dCurrentWorldX;
474 double y1 = m_dCurrentWorldY;
475 mc_to_ndc.transformPoint (&x1, &y1);
479 mc_to_ndc.transformPoint (&x2, &y2);
481 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
482 stylusNDC (x1, y1, false); // move to first point
483 stylusNDC (x2, y2, true); // draw to second point
486 m_dCurrentWorldX = x;
487 m_dCurrentWorldY = y;
491 SGP::moveAbs (double x, double y)
493 m_dCurrentWorldX = x;
494 m_dCurrentWorldY = y; /* moves are not clipped */
498 SGP::lineRel (double x, double y)
500 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
504 SGP::moveRel (double x, double y)
506 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
510 // Height is in master coordinates
512 SGP::setTextSize (double height)
514 height /= (yw_max - yw_min); // convert to NDC
517 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
520 if (m_driver.isWX()) {
521 double dHeightPixels = height * m_iPhysicalYSize;
522 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
523 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
525 sys_error (ERR_TRACE, "Setting text size to %d points",
526 nearest<int>(dHeightPoints));
529 m_driver.idWX()->SetFont (*m_pFont);
535 SGP::setTextNDCSize (double height)
537 double dHeightPixels = height * m_iPhysicalYSize;
540 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
543 if (m_driver.isWX()) {
544 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
545 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
546 m_driver.idWX()->SetFont (*m_pFont);
552 SGP::setTextPointSize (double height)
555 // if (m_driver.isG2())
556 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
559 if (m_driver.isWX()) {
560 m_iTextPointSize = static_cast<int>(height+0.5);
561 m_pFont->SetPointSize (m_iTextPointSize);
563 sys_error (ERR_TRACE, "Setting point size to %d", m_iTextPointSize);
565 m_driver.idWX()->SetFont (*m_pFont);
571 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
574 if (m_driver.isWX()) {
575 wxCoord deviceW, deviceH;
576 wxString str (wxConvCurrent->cMB2WC(szText));
577 m_driver.idWX()->GetTextExtent (str, &deviceW, &deviceH);
578 if (m_dTextAngle == 90 || m_dTextAngle == -90) {
579 wxCoord temp = deviceW;
583 *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
584 *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
590 SGP::getCharHeight ()
592 double dHeight = (1. / 50.);
595 if (m_driver.isWX()) {
596 dHeight = m_driver.idWX()->GetCharHeight();
597 dHeight /= static_cast<double>(m_iPhysicalYSize);
598 dHeight /= (yv_max - yv_min); // scale to viewport;
601 dHeight *= (yw_max - yw_min); // scale to world coordinates
608 double dWidth = (1. / 80.);
611 if (m_driver.isWX()) {
612 dWidth = m_driver.idWX()->GetCharWidth();
613 dWidth /= static_cast<double>(m_iPhysicalXSize);
614 dWidth /= (xv_max - xv_min); // scale to viewport
617 dWidth *= (xw_max - xw_min); //scale to world coordinates
622 SGP::setTextAngle (double angle)
624 m_dTextAngle = convertRadiansToDegrees(angle);
628 SGP::polylineAbs (double x[], double y[], int n)
630 if (m_bRecalcTransform)
633 double x1 = x[0], y1 = y[0];
634 mc_to_ndc.transformPoint (&x1, &y1);
635 double x2 = x[1], y2 = y[1];
636 mc_to_ndc.transformPoint (&x2, &y2);
638 double xt = x2; // don't pass (x2,y2) to clip, we need them
639 double yt = y2; // as the beginning point of the next line
641 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
642 stylusNDC (x1, y1, false);
643 stylusNDC (xt, yt, true);
646 for (int i = 2; i < n; i++) {
647 x1 = x2; y1 = y2; // NDC endpoint of last line
648 x2 = x[i]; y2 = y[i];
649 mc_to_ndc.transformPoint (&x2, &y2);
652 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
653 stylusNDC (x1, y1, false);
654 stylusNDC (xt, yt, true);
661 SGP::markerAbs (double x, double y)
663 if (m_bRecalcTransform)
668 mc_to_ndc.transformPoint (&xndc, &yndc);
669 markerNDC (xndc, yndc);
670 m_dCurrentWorldX = x;
671 m_dCurrentWorldY = y;
676 SGP::markerRel (double x, double y)
678 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
683 SGP::pointAbs (double x, double y)
685 if (m_bRecalcTransform)
687 double xndc = x, yndc = y;
688 mc_to_ndc.transformPoint (&xndc, &yndc);
689 pointNDC (xndc, yndc);
690 m_dCurrentWorldX = x;
691 m_dCurrentWorldY = y;
696 SGP::pointRel (double x, double y)
698 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
703 SGP::drawText (const std::string& rsMessage)
705 drawText (rsMessage.c_str());
709 SGP::drawText (const char *pszMessage)
711 if (m_bRecalcTransform)
714 double xndc = m_dCurrentWorldX;
715 double yndc = m_dCurrentWorldY;
716 mc_to_ndc.transformPoint (&xndc, &yndc);
718 stylusNDC (xndc, yndc, false); // move to location
721 if (m_driver.isG2()) {
722 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
726 if (m_driver.isWX()) {
727 wxString str(wxConvCurrent->cMB2WC(pszMessage));
728 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
735 // drawRect Draw box in graphics mode
738 // drawbox (xmin, ymin, xmax, ymax)
739 // double xmin, ymin Lower left corner of box
740 // double xmax, ymax Upper left corner of box
743 // This routine leaves the current position of graphic cursor at lower
744 // left corner of box.
747 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
749 moveAbs (xmin, ymin);
750 lineAbs (xmax, ymin);
751 lineAbs (xmax, ymax);
752 lineAbs (xmin, ymax);
753 lineAbs (xmin, ymin);
757 // sgp2_circle - draw circle of radius r at current center
760 SGP::drawCircle (const double r)
762 drawArc (r, 0.0, TWOPI);
765 //==============================================================
766 // draw arc around current center. angles in radius
767 //==============================================================
770 SGP::drawArc (const double r, double start, double stop)
778 double xCent = m_dCurrentWorldX;
779 double yCent = m_dCurrentWorldY;
781 double x = r * cos (start);
782 double y = r * sin (start);
783 moveAbs (xCent + x, yCent + y); // move from center to start of arc
785 const double thetaIncrement = (5 * (TWOPI / 360)); // 5 degree increments
786 double cosTheta = cos (thetaIncrement);
787 double sinTheta = sin (thetaIncrement);
790 for (angle = start; angle < stop; angle += thetaIncrement) {
791 double xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
792 double yp = sinTheta * x + cosTheta * y;
793 lineAbs (xCent + xp, yCent + yp);
797 double c = cos (stop - angle);
798 double s = sin (stop - angle);
799 double xp = c * x - s * y;
800 double yp = s * x + c * y;
801 lineAbs (xCent + xp, yCent + yp);
803 moveAbs (xCent, yCent); // move back to center of circle
808 ///////////////////////////////////////////////////////////////////////
809 // Coordinate Transformations
810 ///////////////////////////////////////////////////////////////////////
814 SGP::transformNDCtoMC (double* x, double* y)
816 if (m_bRecalcTransform)
818 ndc_to_mc.transformPoint (x, y);
823 SGP::transformMCtoNDC (double* x, double* y)
825 if (m_bRecalcTransform)
827 mc_to_ndc.transformPoint (x, y);
832 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
834 if (m_bRecalcTransform)
838 mc_to_ndc.transformPoint (x, y);
843 // calc_transform Calculate transform matrices
846 SGP::calc_transform ()
848 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
849 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
851 wc_to_ndc.setIdentity();
852 wc_to_ndc.mtx[0][0] = scaleX;
853 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
854 wc_to_ndc.mtx[1][1] = scaleY;
855 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
857 mc_to_ndc = m_ctm * wc_to_ndc;
858 ndc_to_mc = mc_to_ndc.invert();
860 m_bRecalcTransform = false;
871 SGP::ctmSet (const TransformationMatrix2D& m)
879 SGP::preTranslate (double x, double y)
881 TransformationMatrix2D m;
883 m.setTranslate (x, y);
889 SGP::postTranslate (double x, double y)
891 TransformationMatrix2D m;
893 m.setTranslate (x, y);
899 SGP::preScale (double sx, double sy)
901 TransformationMatrix2D m;
909 SGP::postScale (double sx, double sy)
911 TransformationMatrix2D m;
920 SGP::preRotate (double theta)
922 TransformationMatrix2D m;
931 SGP::postRotate (double theta)
933 TransformationMatrix2D m;
941 SGP::preShear (double shrx, double shry)
943 TransformationMatrix2D m;
945 m.setShear (shrx, shry);
951 SGP::postShear (double shrx, double shry)
953 TransformationMatrix2D m;
955 m.setShear (shrx, shry);
960 ////////////////////////////////////////////////////////////////////////
962 ////////////////////////////////////////////////////////////////////////
964 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
965 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
967 {'\000', '\000', '\010', '\000', '\000'}, // small dot
968 {'\000', '\034', '\024', '\034', '\000'}, // empty square
969 {'\000', '\034', '\034', '\034', '\000'}, // filled square
970 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
971 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
972 {'\010', '\010', '\076', '\010', '\010'}, // cross
973 {'\000', '\024', '\010', '\024', '\000'}, // X
974 {'\034', '\042', '\042', '\042', '\034'}, // open circle
975 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
976 {'\076', '\042', '\042', '\042', '\076'}, // big open square
977 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
983 SGP::setDC (wxDC* pDC)
985 if (m_driver.isWX()) {
988 setTextPointSize (m_iTextPointSize);