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 (12);
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 (wxROMAN, wxNORMAL, wxNORMAL, wxNORMAL);
121 m_pFont->SetPointSize (iTestPointSize);
122 m_pFont->SetWeight (wxNORMAL);
123 m_pFont->SetStyle (wxNORMAL);
124 m_pFont->SetFamily (wxROMAN);
126 m_pFont->SetFaceName(wxString("times new roman"));
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);
140 if (m_driver.isWX()) {
141 // m_driver.idWX()->SetFont (wxNullFont);
148 SGP::stylusNDC (double x, double y, bool beam)
150 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
151 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
153 yp = m_iPhysicalYSize - yp;
158 m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
162 g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
165 m_iCurrentPhysicalX = xp;
166 m_iCurrentPhysicalY = yp;
170 SGP::markerNDC (double x, double y)
172 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
173 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
175 yp = m_iPhysicalYSize - yp;
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);
186 m_iCurrentPhysicalX = xp;
187 m_iCurrentPhysicalY = yp;
191 SGP::pointNDC (double x, double y)
193 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
194 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
196 yp = m_iPhysicalYSize - yp;
200 m_driver.idWX()->DrawPoint (xp, yp);
202 m_iCurrentPhysicalX = xp;
203 m_iCurrentPhysicalY = yp;
208 // clear Clear Window
215 g2_clear (m_driver.idG2());
218 if (m_driver.isWX()) {
220 brushWhite.SetColour(255,255,255);
221 m_driver.idWX()->SetBackground(brushWhite);
222 m_driver.idWX()->Clear();
223 m_driver.idWX()->SetBackground(wxNullBrush);
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);
236 // sgp2_window Set window in world coordinates
240 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
242 if (xmin >= xmax || ymin >= ymax) {
243 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
251 m_bRecalcTransform = true;
256 // sgp2_viewport Set viewport in NDC
259 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
261 if (xmin >= xmax || ymin >= ymax) {
262 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
270 m_bRecalcTransform = true;
272 viewNDC[0] = xmin; // Array for clip_rect()
279 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
288 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
298 // frameViewport draw box around viewport
301 SGP::frameViewport (void)
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);
311 SGP::setTextColor (int iFGcolor, int iBGcolor)
314 if (m_driver.isWX()) {
316 wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
317 m_driver.idWX()->SetTextForeground (colour);
320 wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
321 m_driver.idWX()->SetTextBackground (colour);
328 SGP::setColor (int icol)
330 if (icol >= 0 && icol < s_iRGBColorCount) {
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);
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);
348 SGP::setPenWidth (int iWidth)
352 if (m_driver.isWX()) {
353 m_pen.SetWidth (iWidth);
354 m_driver.idWX()->SetPen (m_pen);
361 SGP::setRasterOp (int ro)
364 if (m_driver.isWX()) {
371 wxFxn = wxAND_INVERT;
374 wxFxn = wxAND_REVERSE;
404 wxFxn = wxOR_REVERSE;
410 wxFxn = wxSRC_INVERT;
417 m_driver.idWX()->SetLogicalFunction (wxFxn);
424 SGP::setMarker (int idMarker)
426 m_iMarker = idMarker;
429 //==============================================================
430 // set line style. Pass 16 bit repeating pattern
431 //==============================================================
433 SGP::setLineStyle (int style)
435 m_iLinestyle = style;
438 if (m_driver.isWX()) {
439 switch (m_iLinestyle) {
441 m_pen.SetStyle (wxSOLID);
444 m_pen.SetStyle (wxLONG_DASH);
447 m_pen.SetStyle (wxSHORT_DASH);
450 m_pen.SetStyle (wxDOT_DASH);
453 m_pen.SetStyle (wxCROSS_HATCH);
456 m_pen.SetStyle (wxDOT);
459 m_pen.SetStyle (wxSOLID);
462 m_driver.idWX()->SetPen (m_pen);
467 //==============================================================
469 //*==============================================================
472 SGP::lineAbs (double x, double y)
474 if (m_bRecalcTransform)
477 double x1 = m_dCurrentWorldX;
478 double y1 = m_dCurrentWorldY;
479 mc_to_ndc.transformPoint (&x1, &y1);
483 mc_to_ndc.transformPoint (&x2, &y2);
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
490 m_dCurrentWorldX = x;
491 m_dCurrentWorldY = y;
495 SGP::moveAbs (double x, double y)
497 m_dCurrentWorldX = x;
498 m_dCurrentWorldY = y; /* moves are not clipped */
502 SGP::lineRel (double x, double y)
504 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
508 SGP::moveRel (double x, double y)
510 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
514 // Height is in master coordinates
516 SGP::setTextSize (double height)
518 height /= (yw_max - yw_min); // convert to NDC
521 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
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);
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 m_driver.idWX()->SetFont (*m_pFont);
567 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
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;
579 *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
580 *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
586 SGP::getCharHeight ()
588 double dHeight = (1. / 25.);
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;
597 dHeight *= (yw_max - yw_min); // scale to world coordinates
604 double dWidth = (1. / 80.);
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
613 dWidth *= (xw_max - xw_min); //scale to world coordinates
618 SGP::setTextAngle (double angle)
620 m_dTextAngle = convertRadiansToDegrees(angle);
624 SGP::polylineAbs (double x[], double y[], int n)
626 if (m_bRecalcTransform)
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);
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
637 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
638 stylusNDC (x1, y1, false);
639 stylusNDC (xt, yt, true);
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);
648 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
649 stylusNDC (x1, y1, false);
650 stylusNDC (xt, yt, true);
657 SGP::markerAbs (double x, double y)
659 if (m_bRecalcTransform)
664 mc_to_ndc.transformPoint (&xndc, &yndc);
665 markerNDC (xndc, yndc);
666 m_dCurrentWorldX = x;
667 m_dCurrentWorldY = y;
672 SGP::markerRel (double x, double y)
674 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
679 SGP::pointAbs (double x, double y)
681 if (m_bRecalcTransform)
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;
692 SGP::pointRel (double x, double y)
694 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
699 SGP::drawText (const std::string& rsMessage)
701 drawText (rsMessage.c_str());
705 SGP::drawText (const char *pszMessage)
707 if (m_bRecalcTransform)
710 double xndc = m_dCurrentWorldX;
711 double yndc = m_dCurrentWorldY;
712 mc_to_ndc.transformPoint (&xndc, &yndc);
714 stylusNDC (xndc, yndc, false); // move to location
717 if (m_driver.isG2()) {
718 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
722 if (m_driver.isWX()) {
723 wxString str(wxConvCurrent->cMB2WC(pszMessage));
724 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
731 // drawRect Draw box in graphics mode
734 // drawbox (xmin, ymin, xmax, ymax)
735 // double xmin, ymin Lower left corner of box
736 // double xmax, ymax Upper left corner of box
739 // This routine leaves the current position of graphic cursor at lower
740 // left corner of box.
743 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
745 moveAbs (xmin, ymin);
746 lineAbs (xmax, ymin);
747 lineAbs (xmax, ymax);
748 lineAbs (xmin, ymax);
749 lineAbs (xmin, ymin);
753 // sgp2_circle - draw circle of radius r at current center
756 SGP::drawCircle (const double r)
758 drawArc (r, 0.0, TWOPI);
761 //==============================================================
762 // draw arc around current center. angles in radius
763 //==============================================================
766 SGP::drawArc (const double r, double start, double stop)
774 double xCent = m_dCurrentWorldX;
775 double yCent = m_dCurrentWorldY;
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
781 const double thetaIncrement = (5 * (TWOPI / 360)); // 5 degree increments
782 double cosTheta = cos (thetaIncrement);
783 double sinTheta = sin (thetaIncrement);
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);
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);
799 moveAbs (xCent, yCent); // move back to center of circle
804 ///////////////////////////////////////////////////////////////////////
805 // Coordinate Transformations
806 ///////////////////////////////////////////////////////////////////////
810 SGP::transformNDCtoMC (double* x, double* y)
812 if (m_bRecalcTransform)
814 ndc_to_mc.transformPoint (x, y);
819 SGP::transformMCtoNDC (double* x, double* y)
821 if (m_bRecalcTransform)
823 mc_to_ndc.transformPoint (x, y);
828 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
830 if (m_bRecalcTransform)
834 mc_to_ndc.transformPoint (x, y);
839 // calc_transform Calculate transform matrices
842 SGP::calc_transform ()
844 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
845 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
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;
853 mc_to_ndc = m_ctm * wc_to_ndc;
854 ndc_to_mc = mc_to_ndc.invert();
856 m_bRecalcTransform = false;
867 SGP::ctmSet (const TransformationMatrix2D& m)
875 SGP::preTranslate (double x, double y)
877 TransformationMatrix2D m;
879 m.setTranslate (x, y);
885 SGP::postTranslate (double x, double y)
887 TransformationMatrix2D m;
889 m.setTranslate (x, y);
895 SGP::preScale (double sx, double sy)
897 TransformationMatrix2D m;
905 SGP::postScale (double sx, double sy)
907 TransformationMatrix2D m;
916 SGP::preRotate (double theta)
918 TransformationMatrix2D m;
927 SGP::postRotate (double theta)
929 TransformationMatrix2D m;
937 SGP::preShear (double shrx, double shry)
939 TransformationMatrix2D m;
941 m.setShear (shrx, shry);
947 SGP::postShear (double shrx, double shry)
949 TransformationMatrix2D m;
951 m.setShear (shrx, shry);
956 ////////////////////////////////////////////////////////////////////////
958 ////////////////////////////////////////////////////////////////////////
960 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
961 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
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
979 SGP::setDC (wxDC* pDC)
981 if (m_driver.isWX()) {
984 setTextPointSize (m_iTextPointSize);