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
10 ** $Id: sgp.cpp,v 1.32 2001/03/11 18:52:03 kevin Exp $
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 RGBColor SGP::s_aRGBColor[] =
37 RGBColor (0, 128, 128),
39 RGBColor (128, 0, 128),
40 RGBColor (128, 128, 0),
41 RGBColor (80, 80, 80),
42 RGBColor (160, 160, 160),
45 RGBColor (0, 255, 255),
47 RGBColor (255, 0, 255),
48 RGBColor (255, 255, 0),
49 RGBColor (255, 255, 255),
52 int SGP::s_iRGBColorCount = sizeof(s_aRGBColor) / sizeof(class 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 wxString sText (szText);
574 wxCoord deviceW, deviceH;
575 m_driver.idWX()->GetTextExtent (sText, &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 (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);