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;
368 wxFxn = wxAND_INVERT;
371 wxFxn = wxAND_REVERSE;
401 wxFxn = wxOR_REVERSE;
407 wxFxn = wxSRC_INVERT;
415 m_driver.idWX()->SetLogicalFunction (wxFxn);
422 SGP::setMarker (int idMarker)
424 m_iMarker = idMarker;
427 //==============================================================
428 // set line style. Pass 16 bit repeating pattern
429 //==============================================================
431 SGP::setLineStyle (int style)
433 m_iLinestyle = style;
436 if (m_driver.isWX()) {
437 switch (m_iLinestyle) {
439 m_pen.SetStyle (wxSOLID);
442 m_pen.SetStyle (wxLONG_DASH);
445 m_pen.SetStyle (wxSHORT_DASH);
448 m_pen.SetStyle (wxDOT_DASH);
451 m_pen.SetStyle (wxCROSS_HATCH);
454 m_pen.SetStyle (wxDOT);
457 m_pen.SetStyle (wxSOLID);
460 m_driver.idWX()->SetPen (m_pen);
465 //==============================================================
467 //*==============================================================
470 SGP::lineAbs (double x, double y)
472 if (m_bRecalcTransform)
475 double x1 = m_dCurrentWorldX;
476 double y1 = m_dCurrentWorldY;
477 mc_to_ndc.transformPoint (&x1, &y1);
481 mc_to_ndc.transformPoint (&x2, &y2);
483 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
484 stylusNDC (x1, y1, false); // move to first point
485 stylusNDC (x2, y2, true); // draw to second point
488 m_dCurrentWorldX = x;
489 m_dCurrentWorldY = y;
493 SGP::moveAbs (double x, double y)
495 m_dCurrentWorldX = x;
496 m_dCurrentWorldY = y; /* moves are not clipped */
500 SGP::lineRel (double x, double y)
502 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
506 SGP::moveRel (double x, double y)
508 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
512 // Height is in master coordinates
514 SGP::setTextSize (double height)
516 height /= (yw_max - yw_min); // convert to NDC
519 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
522 if (m_driver.isWX()) {
523 double dHeightPixels = height * m_iPhysicalYSize;
524 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
525 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
527 sys_error (ERR_TRACE, "Setting text size to %d points",
528 nearest<int>(dHeightPoints));
531 m_driver.idWX()->SetFont (*m_pFont);
537 SGP::setTextNDCSize (double height)
539 double dHeightPixels = height * m_iPhysicalYSize;
542 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
545 if (m_driver.isWX()) {
546 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
547 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
548 m_driver.idWX()->SetFont (*m_pFont);
554 SGP::setTextPointSize (double height)
557 // if (m_driver.isG2())
558 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
561 if (m_driver.isWX()) {
562 m_iTextPointSize = static_cast<int>(height+0.5);
563 m_pFont->SetPointSize (m_iTextPointSize);
565 sys_error (ERR_TRACE, "Setting point size to %d", m_iTextPointSize);
567 m_driver.idWX()->SetFont (*m_pFont);
573 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
576 if (m_driver.isWX()) {
577 wxCoord deviceW, deviceH;
578 wxString str (wxConvCurrent->cMB2WC(szText));
579 m_driver.idWX()->GetTextExtent (str, &deviceW, &deviceH);
580 if (m_dTextAngle == 90 || m_dTextAngle == -90) {
581 wxCoord temp = deviceW;
585 *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
586 *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
592 SGP::getCharHeight ()
594 double dHeight = (1. / 50.);
597 if (m_driver.isWX()) {
598 dHeight = m_driver.idWX()->GetCharHeight();
599 dHeight /= static_cast<double>(m_iPhysicalYSize);
600 dHeight /= (yv_max - yv_min); // scale to viewport;
603 dHeight *= (yw_max - yw_min); // scale to world coordinates
610 double dWidth = (1. / 80.);
613 if (m_driver.isWX()) {
614 dWidth = m_driver.idWX()->GetCharWidth();
615 dWidth /= static_cast<double>(m_iPhysicalXSize);
616 dWidth /= (xv_max - xv_min); // scale to viewport
619 dWidth *= (xw_max - xw_min); //scale to world coordinates
624 SGP::setTextAngle (double angle)
626 m_dTextAngle = convertRadiansToDegrees(angle);
630 SGP::polylineAbs (double x[], double y[], int n)
632 if (m_bRecalcTransform)
635 double x1 = x[0], y1 = y[0];
636 mc_to_ndc.transformPoint (&x1, &y1);
637 double x2 = x[1], y2 = y[1];
638 mc_to_ndc.transformPoint (&x2, &y2);
640 double xt = x2; // don't pass (x2,y2) to clip, we need them
641 double yt = y2; // as the beginning point of the next line
643 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
644 stylusNDC (x1, y1, false);
645 stylusNDC (xt, yt, true);
648 for (int i = 2; i < n; i++) {
649 x1 = x2; y1 = y2; // NDC endpoint of last line
650 x2 = x[i]; y2 = y[i];
651 mc_to_ndc.transformPoint (&x2, &y2);
654 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
655 stylusNDC (x1, y1, false);
656 stylusNDC (xt, yt, true);
663 SGP::markerAbs (double x, double y)
665 if (m_bRecalcTransform)
670 mc_to_ndc.transformPoint (&xndc, &yndc);
671 markerNDC (xndc, yndc);
672 m_dCurrentWorldX = x;
673 m_dCurrentWorldY = y;
678 SGP::markerRel (double x, double y)
680 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
685 SGP::pointAbs (double x, double y)
687 if (m_bRecalcTransform)
689 double xndc = x, yndc = y;
690 mc_to_ndc.transformPoint (&xndc, &yndc);
691 pointNDC (xndc, yndc);
692 m_dCurrentWorldX = x;
693 m_dCurrentWorldY = y;
698 SGP::pointRel (double x, double y)
700 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
705 SGP::drawText (const std::string& rsMessage)
707 drawText (rsMessage.c_str());
711 SGP::drawText (const char *pszMessage)
713 if (m_bRecalcTransform)
716 double xndc = m_dCurrentWorldX;
717 double yndc = m_dCurrentWorldY;
718 mc_to_ndc.transformPoint (&xndc, &yndc);
720 stylusNDC (xndc, yndc, false); // move to location
723 if (m_driver.isG2()) {
724 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
728 if (m_driver.isWX()) {
729 wxString str(wxConvCurrent->cMB2WC(pszMessage));
730 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
737 // drawRect Draw box in graphics mode
740 // drawbox (xmin, ymin, xmax, ymax)
741 // double xmin, ymin Lower left corner of box
742 // double xmax, ymax Upper left corner of box
745 // This routine leaves the current position of graphic cursor at lower
746 // left corner of box.
749 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
751 moveAbs (xmin, ymin);
752 lineAbs (xmax, ymin);
753 lineAbs (xmax, ymax);
754 lineAbs (xmin, ymax);
755 lineAbs (xmin, ymin);
759 // sgp2_circle - draw circle of radius r at current center
762 SGP::drawCircle (const double r)
764 drawArc (r, 0.0, TWOPI);
767 //==============================================================
768 // draw arc around current center. angles in radius
769 //==============================================================
772 SGP::drawArc (const double r, double start, double stop)
780 double xCent = m_dCurrentWorldX;
781 double yCent = m_dCurrentWorldY;
783 double x = r * cos (start);
784 double y = r * sin (start);
785 moveAbs (xCent + x, yCent + y); // move from center to start of arc
787 const double thetaIncrement = (5 * (TWOPI / 360)); // 5 degree increments
788 double cosTheta = cos (thetaIncrement);
789 double sinTheta = sin (thetaIncrement);
792 for (angle = start; angle < stop; angle += thetaIncrement) {
793 double xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
794 double yp = sinTheta * x + cosTheta * y;
795 lineAbs (xCent + xp, yCent + yp);
799 double c = cos (stop - angle);
800 double s = sin (stop - angle);
801 double xp = c * x - s * y;
802 double yp = s * x + c * y;
803 lineAbs (xCent + xp, yCent + yp);
805 moveAbs (xCent, yCent); // move back to center of circle
810 ///////////////////////////////////////////////////////////////////////
811 // Coordinate Transformations
812 ///////////////////////////////////////////////////////////////////////
816 SGP::transformNDCtoMC (double* x, double* y)
818 if (m_bRecalcTransform)
820 ndc_to_mc.transformPoint (x, y);
825 SGP::transformMCtoNDC (double* x, double* y)
827 if (m_bRecalcTransform)
829 mc_to_ndc.transformPoint (x, y);
834 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
836 if (m_bRecalcTransform)
840 mc_to_ndc.transformPoint (x, y);
845 // calc_transform Calculate transform matrices
848 SGP::calc_transform ()
850 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
851 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
853 wc_to_ndc.setIdentity();
854 wc_to_ndc.mtx[0][0] = scaleX;
855 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
856 wc_to_ndc.mtx[1][1] = scaleY;
857 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
859 mc_to_ndc = m_ctm * wc_to_ndc;
860 ndc_to_mc = mc_to_ndc.invert();
862 m_bRecalcTransform = false;
873 SGP::ctmSet (const TransformationMatrix2D& m)
881 SGP::preTranslate (double x, double y)
883 TransformationMatrix2D m;
885 m.setTranslate (x, y);
891 SGP::postTranslate (double x, double y)
893 TransformationMatrix2D m;
895 m.setTranslate (x, y);
901 SGP::preScale (double sx, double sy)
903 TransformationMatrix2D m;
911 SGP::postScale (double sx, double sy)
913 TransformationMatrix2D m;
922 SGP::preRotate (double theta)
924 TransformationMatrix2D m;
933 SGP::postRotate (double theta)
935 TransformationMatrix2D m;
943 SGP::preShear (double shrx, double shry)
945 TransformationMatrix2D m;
947 m.setShear (shrx, shry);
953 SGP::postShear (double shrx, double shry)
955 TransformationMatrix2D m;
957 m.setShear (shrx, shry);
962 ////////////////////////////////////////////////////////////////////////
964 ////////////////////////////////////////////////////////////////////////
966 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
967 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
969 {'\000', '\000', '\010', '\000', '\000'}, // small dot
970 {'\000', '\034', '\024', '\034', '\000'}, // empty square
971 {'\000', '\034', '\034', '\034', '\000'}, // filled square
972 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
973 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
974 {'\010', '\010', '\076', '\010', '\010'}, // cross
975 {'\000', '\024', '\010', '\024', '\000'}, // X
976 {'\034', '\042', '\042', '\042', '\034'}, // open circle
977 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
978 {'\076', '\042', '\042', '\042', '\076'}, // big open square
979 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
985 SGP::setDC (wxDC* pDC)
987 if (m_driver.isWX()) {
990 setTextPointSize (m_iTextPointSize);