1 /*****************************************************************************
4 ** Name: sgp.cpp Simple Graphics Package
5 ** Programmer: Kevin Rosenberg
7 ** This is part of the CTSim program
8 ** Copyright (C) 1983-2000 Kevin Rosenberg
10 ** $Id: sgp.cpp,v 1.23 2000/12/25 21:54:26 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();
97 if (m_driver.isWX()) {
98 static const double dScreenDPI = 82;
99 static const double dPointsPerInch = 72.;
100 m_dPointsPerPixel = dPointsPerInch / dScreenDPI;
101 const int iTestPointSize = 12;
\r
102 m_pFont = new wxFont (wxROMAN, wxNORMAL, wxNORMAL, wxNORMAL);
103 m_pFont->SetPointSize (iTestPointSize);
\r
104 m_pFont->SetWeight (wxNORMAL);
\r
105 m_pFont->SetStyle (wxNORMAL);
106 m_pFont->SetFamily (wxROMAN);
\r
108 m_pFont->SetFaceName(wxString("times new roman"));
\r
110 m_driver.idWX()->SetFont (*m_pFont);
111 double dTestCharHeight = m_driver.idWX()->GetCharHeight();
112 m_dPointsPerPixel = iTestPointSize / dTestCharHeight;
\r
113 m_driver.idWX()->SetBackground (*wxWHITE_BRUSH);
117 setWindow (0., 0., 1., 1.);
118 setViewport (0., 0., 1., 1.);
120 stylusNDC (0., 0., false);
123 setTextPointSize (12);
124 setColor (C_BLACK);
\r
125 setLineStyle (LS_SOLID);
131 if (m_driver.isWX()) {
\r
132 m_driver.idWX()->SetFont (wxNullFont);
\r
139 SGP::stylusNDC (double x, double y, bool beam)
141 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
142 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
144 yp = m_iPhysicalYSize - yp;
149 m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
153 g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
156 m_iCurrentPhysicalX = xp;
157 m_iCurrentPhysicalY = yp;
161 SGP::markerNDC (double x, double y)
166 SGP::pointNDC (double x, double y)
172 // clear Clear Window
179 g2_clear (m_driver.idG2());
182 if (m_driver.isWX()) {
\r
183 wxBrush brushWhite;
\r
184 brushWhite.SetColour(255,255,255);
\r
185 m_driver.idWX()->SetBackground(brushWhite);
\r
186 m_driver.idWX()->Clear();
\r
187 m_driver.idWX()->SetBackground(wxNullBrush);
\r
190 pen.SetColour(255,255,255);
\r
191 m_driver.idWX()->SetBrush (brushWhite);
\r
192 m_driver.idWX()->DrawRectangle (0, 0, m_iPhysicalXSize, m_iPhysicalYSize);
\r
193 m_driver.idWX()->SetBrush (wxNullBrush);
\r
200 // sgp2_window Set window in world coordinates
204 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
206 if (xmin >= xmax || ymin >= ymax) {
207 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
215 m_bRecalcTransform = true;
220 // sgp2_viewport Set viewport in NDC
223 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
225 if (xmin >= xmax || ymin >= ymax) {
226 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
234 m_bRecalcTransform = true;
236 viewNDC[0] = xmin; // Array for clip_rect()
243 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
252 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
262 // frameViewport draw box around viewport
265 SGP::frameViewport (void)
267 stylusNDC (xv_min, yv_min, false);
268 stylusNDC (xv_max, yv_min, true);
269 stylusNDC (xv_max, yv_max, true);
270 stylusNDC (xv_min, yv_max, true);
271 stylusNDC (xv_min, yv_min, true);
275 SGP::setTextColor (int iFGcolor, int iBGcolor)
278 if (m_driver.isWX()) {
280 wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
281 m_driver.idWX()->SetTextForeground (colour);
284 wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
285 m_driver.idWX()->SetTextBackground (colour);
292 SGP::setColor (int icol)
294 if (icol >= 0 && icol < s_iRGBColorCount) {
296 if (m_driver.isG2()) {
297 int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
298 g2_pen (m_driver.idG2(), iInk);
302 if (m_driver.isWX()) {
303 wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
304 m_pen.SetColour (colour);
305 m_driver.idWX()->SetPen (m_pen);
312 SGP::setPenWidth (int iWidth)
316 if (m_driver.isWX()) {
317 m_pen.SetWidth (iWidth);
318 m_driver.idWX()->SetPen (m_pen);
325 SGP::setRasterOp (int ro)
328 if (m_driver.isWX()) {
335 wxFxn = wxAND_INVERT;
338 wxFxn = wxAND_REVERSE;
368 wxFxn = wxOR_REVERSE;
374 wxFxn = wxSRC_INVERT;
381 m_driver.idWX()->SetLogicalFunction (wxFxn);
388 SGP::setMarker (int idMarke, int iColor)
392 //==============================================================
393 // set line style. Pass 16 bit repeating pattern
394 //==============================================================
396 SGP::setLineStyle (int style)
398 m_iLinestyle = style;
\r
401 if (m_driver.isWX()) {
\r
402 switch (m_iLinestyle) {
\r
404 m_pen.SetStyle (wxSOLID);
\r
407 m_pen.SetStyle (wxLONG_DASH);
\r
410 m_pen.SetStyle (wxSHORT_DASH);
\r
413 m_pen.SetStyle (wxDOT_DASH);
\r
416 m_pen.SetStyle (wxCROSS_HATCH);
\r
419 m_pen.SetStyle (wxDOT);
\r
422 m_pen.SetStyle (wxSOLID);
\r
425 m_driver.idWX()->SetPen (m_pen);
\r
430 //==============================================================
432 //*==============================================================
435 SGP::lineAbs (double x, double y)
437 if (m_bRecalcTransform)
440 double x1 = m_dCurrentWorldX;
441 double y1 = m_dCurrentWorldY;
442 mc_to_ndc.transformPoint (&x1, &y1);
446 mc_to_ndc.transformPoint (&x2, &y2);
448 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
449 stylusNDC (x1, y1, false); // move to first point
450 stylusNDC (x2, y2, true); // draw to second point
453 m_dCurrentWorldX = x;
454 m_dCurrentWorldY = y;
458 SGP::moveAbs (double x, double y)
460 m_dCurrentWorldX = x;
461 m_dCurrentWorldY = y; /* moves are not clipped */
465 SGP::lineRel (double x, double y)
467 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
471 SGP::moveRel (double x, double y)
473 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
477 // Height is in master coordinates
479 SGP::setTextSize (double height)
481 height /= (yw_max - yw_min); // convert to NDC
484 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
487 if (m_driver.isWX()) {
488 double dHeightPixels = height * m_iPhysicalYSize;
489 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
490 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
491 m_driver.idWX()->SetFont (*m_pFont);
497 SGP::setTextNDCSize (double height)
499 double dHeightPixels = height * m_iPhysicalYSize;
502 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
505 if (m_driver.isWX()) {
506 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
507 m_pFont->SetPointSize (nearest<int>(dHeightPoints));
508 m_driver.idWX()->SetFont (*m_pFont);
514 SGP::setTextPointSize (double height)
517 // if (m_driver.isG2())
518 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
521 if (m_driver.isWX()) {
522 m_pFont->SetPointSize (static_cast<int>(height+0.5));
523 m_driver.idWX()->SetFont (*m_pFont);
529 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
532 if (m_driver.isWX()) {
533 wxString sText (szText);
534 wxCoord deviceW, deviceH;
535 m_driver.idWX()->GetTextExtent (sText, &deviceW, &deviceH);
536 *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
537 *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
543 SGP::getCharHeight ()
545 double dHeight = (1. / 25.);
548 if (m_driver.isWX()) {
549 dHeight = m_driver.idWX()->GetCharHeight();
550 dHeight /= static_cast<double>(m_iPhysicalYSize);
\r
551 dHeight /= (yv_max - yv_min); // scale to viewport;
554 dHeight *= (yw_max - yw_min); // scale to world coordinates
561 double dWidth = (1. / 80.);
564 if (m_driver.isWX()) {
565 dWidth = m_driver.idWX()->GetCharWidth();
566 dWidth /= static_cast<double>(m_iPhysicalXSize);
\r
567 dWidth /= (xv_max - xv_min); // scale to viewport
570 dWidth *= (xw_max - xw_min); //scale to world coordinates
575 SGP::setTextAngle (double angle)
577 m_dTextAngle = convertRadiansToDegrees(angle);
581 SGP::polylineAbs (double x[], double y[], int n)
583 if (m_bRecalcTransform)
586 double x1 = x[0], y1 = y[0];
587 mc_to_ndc.transformPoint (&x1, &y1);
588 double x2 = x[1], y2 = y[1];
589 mc_to_ndc.transformPoint (&x2, &y2);
591 double xt = x2; // don't pass (x2,y2) to clip, we need them
592 double yt = y2; // as the beginning point of the next line
594 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
595 stylusNDC (x1, y1, false);
596 stylusNDC (xt, yt, true);
599 for (int i = 2; i < n; i++) {
600 x1 = x2; y1 = y2; // NDC endpoint of last line
601 x2 = x[i]; y2 = y[i];
602 mc_to_ndc.transformPoint (&x2, &y2);
605 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
606 stylusNDC (x1, y1, false);
607 stylusNDC (xt, yt, true);
614 SGP::markerAbs (double x, double y)
616 if (m_bRecalcTransform)
621 mc_to_ndc.transformPoint (&xndc, &yndc);
622 markerNDC (xndc, yndc);
623 stylusNDC (xndc, yndc, false); // move to location
624 m_dCurrentWorldX = x;
625 m_dCurrentWorldY = y;
630 SGP::markerRel (double x, double y)
632 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
637 SGP::pointAbs (double x, double y)
639 if (m_bRecalcTransform)
641 double xndc = x, yndc = y;
642 mc_to_ndc.transformPoint (&xndc, &yndc);
643 pointNDC (xndc, yndc);
644 stylusNDC (xndc, yndc, false); // move to location
645 m_dCurrentWorldX = x;
646 m_dCurrentWorldY = y;
651 SGP::pointRel (double x, double y)
653 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
658 SGP::drawText (const std::string& rsMessage)
660 drawText (rsMessage.c_str());
664 SGP::drawText (const char *pszMessage)
666 if (m_bRecalcTransform)
669 double xndc = m_dCurrentWorldX;
670 double yndc = m_dCurrentWorldY;
671 mc_to_ndc.transformPoint (&xndc, &yndc);
673 stylusNDC (xndc, yndc, false); // move to location
676 if (m_driver.isG2()) {
677 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
681 if (m_driver.isWX()) {
682 wxString str (pszMessage);
683 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
690 // drawRect Draw box in graphics mode
693 // drawbox (xmin, ymin, xmax, ymax)
694 // double xmin, ymin Lower left corner of box
695 // double xmax, ymax Upper left corner of box
698 // This routine leaves the current position of graphic cursor at lower
699 // left corner of box.
702 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
704 moveAbs (xmin, ymin);
705 lineAbs (xmax, ymin);
706 lineAbs (xmax, ymax);
707 lineAbs (xmin, ymax);
708 lineAbs (xmin, ymin);
712 // sgp2_circle - draw circle of radius r at current center
715 SGP::drawCircle (const double r)
717 drawArc (r, 0.0, TWOPI);
720 //==============================================================
721 // draw arc around current center. angles in radius
722 //==============================================================
725 SGP::drawArc (const double r, double start, double stop)
733 double x = r * cos ((double) start);
734 double y = r * sin ((double) start);
735 moveRel (x, y); // move from center to start of arc
737 const double thetaIncrement = (5 * (TWOPI / 360));
738 double cosTheta = cos(thetaIncrement);
739 double sinTheta = sin(thetaIncrement);
741 double angle, xp, yp;
742 for (angle = start; angle < stop - thetaIncrement; angle += thetaIncrement) {
743 xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
744 yp = sinTheta * x + cosTheta * y;
749 double c = cos (stop - angle);
750 double s = sin (stop - angle);
757 moveRel (-x, -y); // move back to center of circle
762 ///////////////////////////////////////////////////////////////////////
763 // Coordinate Transformations
764 ///////////////////////////////////////////////////////////////////////
768 SGP::transformNDCtoMC (double* x, double* y)
770 if (m_bRecalcTransform)
772 ndc_to_mc.transformPoint (x, y);
777 SGP::transformMCtoNDC (double* x, double* y)
779 if (m_bRecalcTransform)
781 mc_to_ndc.transformPoint (x, y);
786 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
788 if (m_bRecalcTransform)
792 mc_to_ndc.transformPoint (x, y);
797 // calc_transform Calculate transform matrices
800 SGP::calc_transform ()
802 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
803 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
805 wc_to_ndc.setIdentity();
806 wc_to_ndc.mtx[0][0] = scaleX;
807 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
808 wc_to_ndc.mtx[1][1] = scaleY;
809 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
811 mc_to_ndc = m_ctm * wc_to_ndc;
812 ndc_to_mc = mc_to_ndc.invert();
814 m_bRecalcTransform = false;
825 SGP::ctmSet (const TransformationMatrix2D& m)
833 SGP::preTranslate (double x, double y)
835 TransformationMatrix2D m;
837 m.setTranslate (x, y);
843 SGP::postTranslate (double x, double y)
845 TransformationMatrix2D m;
847 m.setTranslate (x, y);
853 SGP::preScale (double sx, double sy)
855 TransformationMatrix2D m;
863 SGP::postScale (double sx, double sy)
865 TransformationMatrix2D m;
874 SGP::preRotate (double theta)
876 TransformationMatrix2D m;
885 SGP::postRotate (double theta)
887 TransformationMatrix2D m;
895 SGP::preShear (double shrx, double shry)
897 TransformationMatrix2D m;
899 m.setShear (shrx, shry);
905 SGP::postShear (double shrx, double shry)
907 TransformationMatrix2D m;
909 m.setShear (shrx, shry);
914 ////////////////////////////////////////////////////////////////////////
916 ////////////////////////////////////////////////////////////////////////
918 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
919 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
921 {'\000', '\000', '\010', '\000', '\000'}, // small dot
922 {'\000', '\034', '\024', '\034', '\000'}, // empty square
923 {'\000', '\034', '\034', '\034', '\000'}, // filled square
924 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
925 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
926 {'\010', '\010', '\076', '\010', '\010'}, // cross
927 {'\000', '\024', '\010', '\024', '\000'}, // X
928 {'\034', '\042', '\042', '\042', '\034'}, // open circle
929 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
930 {'\076', '\042', '\042', '\042', '\076'}, // big open square
931 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
937 SGP::setDC (wxDC* pDC)