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()) {
368 wxFxn = wxAND_INVERT;
371 wxFxn = wxAND_REVERSE;
401 wxFxn = wxOR_REVERSE;
407 wxFxn = wxSRC_INVERT;
414 m_driver.idWX()->SetLogicalFunction (wxFxn);
421 SGP::setMarker (int idMarker)
423 m_iMarker = idMarker;
426 //==============================================================
427 // set line style. Pass 16 bit repeating pattern
428 //==============================================================
430 SGP::setLineStyle (int style)
432 m_iLinestyle = style;
435 if (m_driver.isWX()) {
436 switch (m_iLinestyle) {
438 m_pen.SetStyle (wxSOLID);
441 m_pen.SetStyle (wxLONG_DASH);
444 m_pen.SetStyle (wxSHORT_DASH);
447 m_pen.SetStyle (wxDOT_DASH);
450 m_pen.SetStyle (wxCROSS_HATCH);
453 m_pen.SetStyle (wxDOT);
456 m_pen.SetStyle (wxSOLID);
459 m_driver.idWX()->SetPen (m_pen);
464 //==============================================================
466 //*==============================================================
469 SGP::lineAbs (double x, double y)
471 if (m_bRecalcTransform)
474 double x1 = m_dCurrentWorldX;
475 double y1 = m_dCurrentWorldY;
476 mc_to_ndc.transformPoint (&x1, &y1);
480 mc_to_ndc.transformPoint (&x2, &y2);
482 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
483 stylusNDC (x1, y1, false); // move to first point
484 stylusNDC (x2, y2, true); // draw to second point
487 m_dCurrentWorldX = x;
488 m_dCurrentWorldY = y;
492 SGP::moveAbs (double x, double y)
494 m_dCurrentWorldX = x;
495 m_dCurrentWorldY = y; /* moves are not clipped */
499 SGP::lineRel (double x, double y)
501 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
505 SGP::moveRel (double x, double y)
507 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
511 // Height is in master coordinates
513 SGP::setTextSize (double height)
515 height /= (yw_max - yw_min); // convert to NDC
518 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
521 if (m_driver.isWX()) {
522 double dHeightPixels = height * m_iPhysicalYSize;
523 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
524 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
525 sys_error (ERR_TRACE, "Setting text size to %d points",
526 nearest<int>(dHeightPoints));
528 m_driver.idWX()->SetFont (*m_pFont);
534 SGP::setTextNDCSize (double height)
536 double dHeightPixels = height * m_iPhysicalYSize;
539 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
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);
551 SGP::setTextPointSize (double height)
554 // if (m_driver.isG2())
555 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
558 if (m_driver.isWX()) {
559 m_iTextPointSize = static_cast<int>(height+0.5);
560 m_pFont->SetPointSize (m_iTextPointSize);
561 sys_error (ERR_TRACE, "Setting point size to %d", m_iTextPointSize);
562 m_driver.idWX()->SetFont (*m_pFont);
568 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
571 if (m_driver.isWX()) {
572 wxCoord deviceW, deviceH;
573 wxString str (wxConvCurrent->cMB2WC(szText));
574 m_driver.idWX()->GetTextExtent (str, &deviceW, &deviceH);
575 if (m_dTextAngle == 90 || m_dTextAngle == -90) {
576 wxCoord temp = deviceW;
580 *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
581 *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
587 SGP::getCharHeight ()
589 double dHeight = (1. / 50.);
592 if (m_driver.isWX()) {
593 dHeight = m_driver.idWX()->GetCharHeight();
594 dHeight /= static_cast<double>(m_iPhysicalYSize);
595 dHeight /= (yv_max - yv_min); // scale to viewport;
598 dHeight *= (yw_max - yw_min); // scale to world coordinates
605 double dWidth = (1. / 80.);
608 if (m_driver.isWX()) {
609 dWidth = m_driver.idWX()->GetCharWidth();
610 dWidth /= static_cast<double>(m_iPhysicalXSize);
611 dWidth /= (xv_max - xv_min); // scale to viewport
614 dWidth *= (xw_max - xw_min); //scale to world coordinates
619 SGP::setTextAngle (double angle)
621 m_dTextAngle = convertRadiansToDegrees(angle);
625 SGP::polylineAbs (double x[], double y[], int n)
627 if (m_bRecalcTransform)
630 double x1 = x[0], y1 = y[0];
631 mc_to_ndc.transformPoint (&x1, &y1);
632 double x2 = x[1], y2 = y[1];
633 mc_to_ndc.transformPoint (&x2, &y2);
635 double xt = x2; // don't pass (x2,y2) to clip, we need them
636 double yt = y2; // as the beginning point of the next line
638 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
639 stylusNDC (x1, y1, false);
640 stylusNDC (xt, yt, true);
643 for (int i = 2; i < n; i++) {
644 x1 = x2; y1 = y2; // NDC endpoint of last line
645 x2 = x[i]; y2 = y[i];
646 mc_to_ndc.transformPoint (&x2, &y2);
649 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
650 stylusNDC (x1, y1, false);
651 stylusNDC (xt, yt, true);
658 SGP::markerAbs (double x, double y)
660 if (m_bRecalcTransform)
665 mc_to_ndc.transformPoint (&xndc, &yndc);
666 markerNDC (xndc, yndc);
667 m_dCurrentWorldX = x;
668 m_dCurrentWorldY = y;
673 SGP::markerRel (double x, double y)
675 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
680 SGP::pointAbs (double x, double y)
682 if (m_bRecalcTransform)
684 double xndc = x, yndc = y;
685 mc_to_ndc.transformPoint (&xndc, &yndc);
686 pointNDC (xndc, yndc);
687 m_dCurrentWorldX = x;
688 m_dCurrentWorldY = y;
693 SGP::pointRel (double x, double y)
695 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
700 SGP::drawText (const std::string& rsMessage)
702 drawText (rsMessage.c_str());
706 SGP::drawText (const char *pszMessage)
708 if (m_bRecalcTransform)
711 double xndc = m_dCurrentWorldX;
712 double yndc = m_dCurrentWorldY;
713 mc_to_ndc.transformPoint (&xndc, &yndc);
715 stylusNDC (xndc, yndc, false); // move to location
718 if (m_driver.isG2()) {
719 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
723 if (m_driver.isWX()) {
724 wxString str(wxConvCurrent->cMB2WC(pszMessage));
725 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
732 // drawRect Draw box in graphics mode
735 // drawbox (xmin, ymin, xmax, ymax)
736 // double xmin, ymin Lower left corner of box
737 // double xmax, ymax Upper left corner of box
740 // This routine leaves the current position of graphic cursor at lower
741 // left corner of box.
744 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
746 moveAbs (xmin, ymin);
747 lineAbs (xmax, ymin);
748 lineAbs (xmax, ymax);
749 lineAbs (xmin, ymax);
750 lineAbs (xmin, ymin);
754 // sgp2_circle - draw circle of radius r at current center
757 SGP::drawCircle (const double r)
759 drawArc (r, 0.0, TWOPI);
762 //==============================================================
763 // draw arc around current center. angles in radius
764 //==============================================================
767 SGP::drawArc (const double r, double start, double stop)
775 double xCent = m_dCurrentWorldX;
776 double yCent = m_dCurrentWorldY;
778 double x = r * cos (start);
779 double y = r * sin (start);
780 moveAbs (xCent + x, yCent + y); // move from center to start of arc
782 const double thetaIncrement = (5 * (TWOPI / 360)); // 5 degree increments
783 double cosTheta = cos (thetaIncrement);
784 double sinTheta = sin (thetaIncrement);
787 for (angle = start; angle < stop; angle += thetaIncrement) {
788 double xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
789 double yp = sinTheta * x + cosTheta * y;
790 lineAbs (xCent + xp, yCent + yp);
794 double c = cos (stop - angle);
795 double s = sin (stop - angle);
796 double xp = c * x - s * y;
797 double yp = s * x + c * y;
798 lineAbs (xCent + xp, yCent + yp);
800 moveAbs (xCent, yCent); // move back to center of circle
805 ///////////////////////////////////////////////////////////////////////
806 // Coordinate Transformations
807 ///////////////////////////////////////////////////////////////////////
811 SGP::transformNDCtoMC (double* x, double* y)
813 if (m_bRecalcTransform)
815 ndc_to_mc.transformPoint (x, y);
820 SGP::transformMCtoNDC (double* x, double* y)
822 if (m_bRecalcTransform)
824 mc_to_ndc.transformPoint (x, y);
829 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
831 if (m_bRecalcTransform)
835 mc_to_ndc.transformPoint (x, y);
840 // calc_transform Calculate transform matrices
843 SGP::calc_transform ()
845 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
846 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
848 wc_to_ndc.setIdentity();
849 wc_to_ndc.mtx[0][0] = scaleX;
850 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
851 wc_to_ndc.mtx[1][1] = scaleY;
852 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
854 mc_to_ndc = m_ctm * wc_to_ndc;
855 ndc_to_mc = mc_to_ndc.invert();
857 m_bRecalcTransform = false;
868 SGP::ctmSet (const TransformationMatrix2D& m)
876 SGP::preTranslate (double x, double y)
878 TransformationMatrix2D m;
880 m.setTranslate (x, y);
886 SGP::postTranslate (double x, double y)
888 TransformationMatrix2D m;
890 m.setTranslate (x, y);
896 SGP::preScale (double sx, double sy)
898 TransformationMatrix2D m;
906 SGP::postScale (double sx, double sy)
908 TransformationMatrix2D m;
917 SGP::preRotate (double theta)
919 TransformationMatrix2D m;
928 SGP::postRotate (double theta)
930 TransformationMatrix2D m;
938 SGP::preShear (double shrx, double shry)
940 TransformationMatrix2D m;
942 m.setShear (shrx, shry);
948 SGP::postShear (double shrx, double shry)
950 TransformationMatrix2D m;
952 m.setShear (shrx, shry);
957 ////////////////////////////////////////////////////////////////////////
959 ////////////////////////////////////////////////////////////////////////
961 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
962 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
964 {'\000', '\000', '\010', '\000', '\000'}, // small dot
965 {'\000', '\034', '\024', '\034', '\000'}, // empty square
966 {'\000', '\034', '\034', '\034', '\000'}, // filled square
967 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
968 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
969 {'\010', '\010', '\076', '\010', '\010'}, // cross
970 {'\000', '\024', '\010', '\024', '\000'}, // X
971 {'\034', '\042', '\042', '\042', '\034'}, // open circle
972 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
973 {'\076', '\042', '\042', '\042', '\076'}, // big open square
974 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
980 SGP::setDC (wxDC* pDC)
982 if (m_driver.isWX()) {
985 setTextPointSize (m_iTextPointSize);