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
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 SGP_RGBColor SGP::s_aRGBColor[] =
34 SGP_RGBColor (0, 0, 0),
35 SGP_RGBColor (0, 0, 128),
36 SGP_RGBColor (0, 128, 0),
37 SGP_RGBColor (0, 128, 128),
38 SGP_RGBColor (128, 0, 0),
39 SGP_RGBColor (128, 0, 128),
40 SGP_RGBColor (128, 128, 0),
41 SGP_RGBColor (80, 80, 80),
42 SGP_RGBColor (160, 160, 160),
43 SGP_RGBColor (0, 0, 255),
44 SGP_RGBColor (0, 255, 0),
45 SGP_RGBColor (0, 255, 255),
46 SGP_RGBColor (255, 0, 0),
47 SGP_RGBColor (255, 0, 255),
48 SGP_RGBColor (255, 255, 0),
49 SGP_RGBColor (255, 255, 255),
52 int SGP::s_iRGBColorCount = sizeof(s_aRGBColor) / sizeof(class SGP_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);
107 setMarker (MARKER_POINT);
111 #ifdef HAVE_WXWINDOWS
113 SGP::initFromDC (wxDC* pDC)
117 if (m_driver.isWX()) {
118 static const double dScreenDPI = 82;
119 static const double dPointsPerInch = 72.;
120 m_dPointsPerPixel = dPointsPerInch / dScreenDPI;
121 const int iTestPointSize = 12;
122 m_pFont = new wxFont (wxROMAN, wxNORMAL, wxNORMAL, wxNORMAL);
123 m_pFont->SetPointSize (iTestPointSize);
124 m_pFont->SetWeight (wxNORMAL);
125 m_pFont->SetStyle (wxNORMAL);
126 m_pFont->SetFamily (wxROMAN);
128 m_pFont->SetFaceName(wxString("times new roman"));
130 m_driver.idWX()->SetFont (*m_pFont);
131 double dTestCharHeight = m_driver.idWX()->GetCharHeight();
132 m_dPointsPerPixel = iTestPointSize / dTestCharHeight;
133 m_driver.idWX()->SetBackground (*wxWHITE_BRUSH);
142 if (m_driver.isWX()) {
143 // m_driver.idWX()->SetFont (wxNullFont);
150 SGP::stylusNDC (double x, double y, bool beam)
152 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
153 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
155 yp = m_iPhysicalYSize - yp;
160 m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
164 g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
167 m_iCurrentPhysicalX = xp;
168 m_iCurrentPhysicalY = yp;
172 SGP::markerNDC (double x, double y)
174 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
175 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
177 yp = m_iPhysicalYSize - yp;
180 if (m_driver.isWX()) {
181 m_driver.idWX()->DrawPoint (xp, yp);
182 m_driver.idWX()->DrawPoint (xp-1, yp-1);
183 m_driver.idWX()->DrawPoint (xp+1, yp+1);
184 m_driver.idWX()->DrawPoint (xp+1, yp-1);
185 m_driver.idWX()->DrawPoint (xp-1, yp+1);
188 m_iCurrentPhysicalX = xp;
189 m_iCurrentPhysicalY = yp;
193 SGP::pointNDC (double x, double y)
195 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
196 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
198 yp = m_iPhysicalYSize - yp;
202 m_driver.idWX()->DrawPoint (xp, yp);
204 m_iCurrentPhysicalX = xp;
205 m_iCurrentPhysicalY = yp;
210 // clear Clear Window
217 g2_clear (m_driver.idG2());
220 if (m_driver.isWX()) {
222 brushWhite.SetColour(255,255,255);
223 m_driver.idWX()->SetBackground(brushWhite);
224 m_driver.idWX()->Clear();
225 m_driver.idWX()->SetBackground(wxNullBrush);
228 pen.SetColour(255,255,255);
229 m_driver.idWX()->SetBrush (brushWhite);
230 m_driver.idWX()->DrawRectangle (0, 0, m_iPhysicalXSize, m_iPhysicalYSize);
231 m_driver.idWX()->SetBrush (wxNullBrush);
238 // sgp2_window Set window in world coordinates
242 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
244 if (xmin >= xmax || ymin >= ymax) {
245 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
253 m_bRecalcTransform = true;
258 // sgp2_viewport Set viewport in NDC
261 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
263 if (xmin >= xmax || ymin >= ymax) {
264 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
272 m_bRecalcTransform = true;
274 viewNDC[0] = xmin; // Array for clip_rect()
281 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
290 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
300 // frameViewport draw box around viewport
303 SGP::frameViewport (void)
305 stylusNDC (xv_min, yv_min, false);
306 stylusNDC (xv_max, yv_min, true);
307 stylusNDC (xv_max, yv_max, true);
308 stylusNDC (xv_min, yv_max, true);
309 stylusNDC (xv_min, yv_min, true);
313 SGP::setTextColor (int iFGcolor, int iBGcolor)
316 if (m_driver.isWX()) {
318 wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
319 m_driver.idWX()->SetTextForeground (colour);
322 wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
323 m_driver.idWX()->SetTextBackground (colour);
330 SGP::setColor (int icol)
332 if (icol >= 0 && icol < s_iRGBColorCount) {
334 if (m_driver.isG2()) {
335 int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
336 g2_pen (m_driver.idG2(), iInk);
340 if (m_driver.isWX()) {
341 wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
342 m_pen.SetColour (colour);
343 m_driver.idWX()->SetPen (m_pen);
350 SGP::setPenWidth (int iWidth)
354 if (m_driver.isWX()) {
355 m_pen.SetWidth (iWidth);
356 m_driver.idWX()->SetPen (m_pen);
363 SGP::setRasterOp (int ro)
366 if (m_driver.isWX()) {
373 wxFxn = wxAND_INVERT;
376 wxFxn = wxAND_REVERSE;
406 wxFxn = wxOR_REVERSE;
412 wxFxn = wxSRC_INVERT;
419 m_driver.idWX()->SetLogicalFunction (wxFxn);
426 SGP::setMarker (int idMarker)
428 m_iMarker = idMarker;
431 //==============================================================
432 // set line style. Pass 16 bit repeating pattern
433 //==============================================================
435 SGP::setLineStyle (int style)
437 m_iLinestyle = style;
440 if (m_driver.isWX()) {
441 switch (m_iLinestyle) {
443 m_pen.SetStyle (wxSOLID);
446 m_pen.SetStyle (wxLONG_DASH);
449 m_pen.SetStyle (wxSHORT_DASH);
452 m_pen.SetStyle (wxDOT_DASH);
455 m_pen.SetStyle (wxCROSS_HATCH);
458 m_pen.SetStyle (wxDOT);
461 m_pen.SetStyle (wxSOLID);
464 m_driver.idWX()->SetPen (m_pen);
469 //==============================================================
471 //*==============================================================
474 SGP::lineAbs (double x, double y)
476 if (m_bRecalcTransform)
479 double x1 = m_dCurrentWorldX;
480 double y1 = m_dCurrentWorldY;
481 mc_to_ndc.transformPoint (&x1, &y1);
485 mc_to_ndc.transformPoint (&x2, &y2);
487 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
488 stylusNDC (x1, y1, false); // move to first point
489 stylusNDC (x2, y2, true); // draw to second point
492 m_dCurrentWorldX = x;
493 m_dCurrentWorldY = y;
497 SGP::moveAbs (double x, double y)
499 m_dCurrentWorldX = x;
500 m_dCurrentWorldY = y; /* moves are not clipped */
504 SGP::lineRel (double x, double y)
506 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
510 SGP::moveRel (double x, double y)
512 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
516 // Height is in master coordinates
518 SGP::setTextSize (double height)
520 height /= (yw_max - yw_min); // convert to NDC
523 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
526 if (m_driver.isWX()) {
527 double dHeightPixels = height * m_iPhysicalYSize;
528 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
529 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
530 m_driver.idWX()->SetFont (*m_pFont);
536 SGP::setTextNDCSize (double height)
538 double dHeightPixels = height * m_iPhysicalYSize;
541 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
544 if (m_driver.isWX()) {
545 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
546 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
547 m_driver.idWX()->SetFont (*m_pFont);
553 SGP::setTextPointSize (double height)
556 // if (m_driver.isG2())
557 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
560 if (m_driver.isWX()) {
561 m_iTextPointSize = static_cast<int>(height+0.5);
562 m_pFont->SetPointSize (m_iTextPointSize);
563 m_driver.idWX()->SetFont (*m_pFont);
569 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
572 if (m_driver.isWX()) {
573 wxCoord deviceW, deviceH;
574 wxString str (wxConvCurrent->cMB2WC(szText));
575 m_driver.idWX()->GetTextExtent (str, &deviceW, &deviceH);
576 if (m_dTextAngle == 90 || m_dTextAngle == -90) {
577 wxCoord temp = deviceW;
581 *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
582 *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
588 SGP::getCharHeight ()
590 double dHeight = (1. / 25.);
593 if (m_driver.isWX()) {
594 dHeight = m_driver.idWX()->GetCharHeight();
595 dHeight /= static_cast<double>(m_iPhysicalYSize);
596 dHeight /= (yv_max - yv_min); // scale to viewport;
599 dHeight *= (yw_max - yw_min); // scale to world coordinates
606 double dWidth = (1. / 80.);
609 if (m_driver.isWX()) {
610 dWidth = m_driver.idWX()->GetCharWidth();
611 dWidth /= static_cast<double>(m_iPhysicalXSize);
612 dWidth /= (xv_max - xv_min); // scale to viewport
615 dWidth *= (xw_max - xw_min); //scale to world coordinates
620 SGP::setTextAngle (double angle)
622 m_dTextAngle = convertRadiansToDegrees(angle);
626 SGP::polylineAbs (double x[], double y[], int n)
628 if (m_bRecalcTransform)
631 double x1 = x[0], y1 = y[0];
632 mc_to_ndc.transformPoint (&x1, &y1);
633 double x2 = x[1], y2 = y[1];
634 mc_to_ndc.transformPoint (&x2, &y2);
636 double xt = x2; // don't pass (x2,y2) to clip, we need them
637 double yt = y2; // as the beginning point of the next line
639 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
640 stylusNDC (x1, y1, false);
641 stylusNDC (xt, yt, true);
644 for (int i = 2; i < n; i++) {
645 x1 = x2; y1 = y2; // NDC endpoint of last line
646 x2 = x[i]; y2 = y[i];
647 mc_to_ndc.transformPoint (&x2, &y2);
650 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
651 stylusNDC (x1, y1, false);
652 stylusNDC (xt, yt, true);
659 SGP::markerAbs (double x, double y)
661 if (m_bRecalcTransform)
666 mc_to_ndc.transformPoint (&xndc, &yndc);
667 markerNDC (xndc, yndc);
668 m_dCurrentWorldX = x;
669 m_dCurrentWorldY = y;
674 SGP::markerRel (double x, double y)
676 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
681 SGP::pointAbs (double x, double y)
683 if (m_bRecalcTransform)
685 double xndc = x, yndc = y;
686 mc_to_ndc.transformPoint (&xndc, &yndc);
687 pointNDC (xndc, yndc);
688 m_dCurrentWorldX = x;
689 m_dCurrentWorldY = y;
694 SGP::pointRel (double x, double y)
696 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
701 SGP::drawText (const std::string& rsMessage)
703 drawText (rsMessage.c_str());
707 SGP::drawText (const char *pszMessage)
709 if (m_bRecalcTransform)
712 double xndc = m_dCurrentWorldX;
713 double yndc = m_dCurrentWorldY;
714 mc_to_ndc.transformPoint (&xndc, &yndc);
716 stylusNDC (xndc, yndc, false); // move to location
719 if (m_driver.isG2()) {
720 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
724 if (m_driver.isWX()) {
725 wxString str(wxConvCurrent->cMB2WC(pszMessage));
726 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
733 // drawRect Draw box in graphics mode
736 // drawbox (xmin, ymin, xmax, ymax)
737 // double xmin, ymin Lower left corner of box
738 // double xmax, ymax Upper left corner of box
741 // This routine leaves the current position of graphic cursor at lower
742 // left corner of box.
745 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
747 moveAbs (xmin, ymin);
748 lineAbs (xmax, ymin);
749 lineAbs (xmax, ymax);
750 lineAbs (xmin, ymax);
751 lineAbs (xmin, ymin);
755 // sgp2_circle - draw circle of radius r at current center
758 SGP::drawCircle (const double r)
760 drawArc (r, 0.0, TWOPI);
763 //==============================================================
764 // draw arc around current center. angles in radius
765 //==============================================================
768 SGP::drawArc (const double r, double start, double stop)
776 double xCent = m_dCurrentWorldX;
777 double yCent = m_dCurrentWorldY;
779 double x = r * cos (start);
780 double y = r * sin (start);
781 moveAbs (xCent + x, yCent + y); // move from center to start of arc
783 const double thetaIncrement = (5 * (TWOPI / 360)); // 5 degree increments
784 double cosTheta = cos (thetaIncrement);
785 double sinTheta = sin (thetaIncrement);
788 for (angle = start; angle < stop; angle += thetaIncrement) {
789 double xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
790 double yp = sinTheta * x + cosTheta * y;
791 lineAbs (xCent + xp, yCent + yp);
795 double c = cos (stop - angle);
796 double s = sin (stop - angle);
797 double xp = c * x - s * y;
798 double yp = s * x + c * y;
799 lineAbs (xCent + xp, yCent + yp);
801 moveAbs (xCent, yCent); // move back to center of circle
806 ///////////////////////////////////////////////////////////////////////
807 // Coordinate Transformations
808 ///////////////////////////////////////////////////////////////////////
812 SGP::transformNDCtoMC (double* x, double* y)
814 if (m_bRecalcTransform)
816 ndc_to_mc.transformPoint (x, y);
821 SGP::transformMCtoNDC (double* x, double* y)
823 if (m_bRecalcTransform)
825 mc_to_ndc.transformPoint (x, y);
830 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
832 if (m_bRecalcTransform)
836 mc_to_ndc.transformPoint (x, y);
841 // calc_transform Calculate transform matrices
844 SGP::calc_transform ()
846 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
847 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
849 wc_to_ndc.setIdentity();
850 wc_to_ndc.mtx[0][0] = scaleX;
851 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
852 wc_to_ndc.mtx[1][1] = scaleY;
853 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
855 mc_to_ndc = m_ctm * wc_to_ndc;
856 ndc_to_mc = mc_to_ndc.invert();
858 m_bRecalcTransform = false;
869 SGP::ctmSet (const TransformationMatrix2D& m)
877 SGP::preTranslate (double x, double y)
879 TransformationMatrix2D m;
881 m.setTranslate (x, y);
887 SGP::postTranslate (double x, double y)
889 TransformationMatrix2D m;
891 m.setTranslate (x, y);
897 SGP::preScale (double sx, double sy)
899 TransformationMatrix2D m;
907 SGP::postScale (double sx, double sy)
909 TransformationMatrix2D m;
918 SGP::preRotate (double theta)
920 TransformationMatrix2D m;
929 SGP::postRotate (double theta)
931 TransformationMatrix2D m;
939 SGP::preShear (double shrx, double shry)
941 TransformationMatrix2D m;
943 m.setShear (shrx, shry);
949 SGP::postShear (double shrx, double shry)
951 TransformationMatrix2D m;
953 m.setShear (shrx, shry);
958 ////////////////////////////////////////////////////////////////////////
960 ////////////////////////////////////////////////////////////////////////
962 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
963 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
965 {'\000', '\000', '\010', '\000', '\000'}, // small dot
966 {'\000', '\034', '\024', '\034', '\000'}, // empty square
967 {'\000', '\034', '\034', '\034', '\000'}, // filled square
968 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
969 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
970 {'\010', '\010', '\076', '\010', '\010'}, // cross
971 {'\000', '\024', '\010', '\024', '\000'}, // X
972 {'\034', '\042', '\042', '\042', '\034'}, // open circle
973 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
974 {'\076', '\042', '\042', '\042', '\076'}, // big open square
975 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
981 SGP::setDC (wxDC* pDC)
983 if (m_driver.isWX()) {
986 setTextPointSize (m_iTextPointSize);