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.24 2000/12/26 21:13:15 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);
\r
536 if (m_dTextAngle == 90 || m_dTextAngle == -90) {
\r
537 wxCoord temp = deviceW;
\r
541 *worldW = (xw_max - xw_min) * deviceW / static_cast<double>(m_iPhysicalXSize);;
542 *worldH = (yw_max - yw_min) * deviceH / static_cast<double>(m_iPhysicalYSize);
548 SGP::getCharHeight ()
550 double dHeight = (1. / 25.);
553 if (m_driver.isWX()) {
554 dHeight = m_driver.idWX()->GetCharHeight();
555 dHeight /= static_cast<double>(m_iPhysicalYSize);
\r
556 dHeight /= (yv_max - yv_min); // scale to viewport;
559 dHeight *= (yw_max - yw_min); // scale to world coordinates
566 double dWidth = (1. / 80.);
569 if (m_driver.isWX()) {
570 dWidth = m_driver.idWX()->GetCharWidth();
571 dWidth /= static_cast<double>(m_iPhysicalXSize);
\r
572 dWidth /= (xv_max - xv_min); // scale to viewport
575 dWidth *= (xw_max - xw_min); //scale to world coordinates
580 SGP::setTextAngle (double angle)
582 m_dTextAngle = convertRadiansToDegrees(angle);
586 SGP::polylineAbs (double x[], double y[], int n)
588 if (m_bRecalcTransform)
591 double x1 = x[0], y1 = y[0];
592 mc_to_ndc.transformPoint (&x1, &y1);
593 double x2 = x[1], y2 = y[1];
594 mc_to_ndc.transformPoint (&x2, &y2);
596 double xt = x2; // don't pass (x2,y2) to clip, we need them
597 double yt = y2; // as the beginning point of the next line
599 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
600 stylusNDC (x1, y1, false);
601 stylusNDC (xt, yt, true);
604 for (int i = 2; i < n; i++) {
605 x1 = x2; y1 = y2; // NDC endpoint of last line
606 x2 = x[i]; y2 = y[i];
607 mc_to_ndc.transformPoint (&x2, &y2);
610 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
611 stylusNDC (x1, y1, false);
612 stylusNDC (xt, yt, true);
619 SGP::markerAbs (double x, double y)
621 if (m_bRecalcTransform)
626 mc_to_ndc.transformPoint (&xndc, &yndc);
627 markerNDC (xndc, yndc);
628 stylusNDC (xndc, yndc, false); // move to location
629 m_dCurrentWorldX = x;
630 m_dCurrentWorldY = y;
635 SGP::markerRel (double x, double y)
637 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
642 SGP::pointAbs (double x, double y)
644 if (m_bRecalcTransform)
646 double xndc = x, yndc = y;
647 mc_to_ndc.transformPoint (&xndc, &yndc);
648 pointNDC (xndc, yndc);
649 stylusNDC (xndc, yndc, false); // move to location
650 m_dCurrentWorldX = x;
651 m_dCurrentWorldY = y;
656 SGP::pointRel (double x, double y)
658 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
663 SGP::drawText (const std::string& rsMessage)
665 drawText (rsMessage.c_str());
669 SGP::drawText (const char *pszMessage)
671 if (m_bRecalcTransform)
674 double xndc = m_dCurrentWorldX;
675 double yndc = m_dCurrentWorldY;
676 mc_to_ndc.transformPoint (&xndc, &yndc);
678 stylusNDC (xndc, yndc, false); // move to location
681 if (m_driver.isG2()) {
682 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
686 if (m_driver.isWX()) {
687 wxString str (pszMessage);
688 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
695 // drawRect Draw box in graphics mode
698 // drawbox (xmin, ymin, xmax, ymax)
699 // double xmin, ymin Lower left corner of box
700 // double xmax, ymax Upper left corner of box
703 // This routine leaves the current position of graphic cursor at lower
704 // left corner of box.
707 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
709 moveAbs (xmin, ymin);
710 lineAbs (xmax, ymin);
711 lineAbs (xmax, ymax);
712 lineAbs (xmin, ymax);
713 lineAbs (xmin, ymin);
717 // sgp2_circle - draw circle of radius r at current center
720 SGP::drawCircle (const double r)
722 drawArc (r, 0.0, TWOPI);
725 //==============================================================
726 // draw arc around current center. angles in radius
727 //==============================================================
730 SGP::drawArc (const double r, double start, double stop)
738 double x = r * cos ((double) start);
739 double y = r * sin ((double) start);
740 moveRel (x, y); // move from center to start of arc
742 const double thetaIncrement = (5 * (TWOPI / 360));
743 double cosTheta = cos(thetaIncrement);
744 double sinTheta = sin(thetaIncrement);
746 double angle, xp, yp;
747 for (angle = start; angle < stop - thetaIncrement; angle += thetaIncrement) {
748 xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
749 yp = sinTheta * x + cosTheta * y;
754 double c = cos (stop - angle);
755 double s = sin (stop - angle);
762 moveRel (-x, -y); // move back to center of circle
767 ///////////////////////////////////////////////////////////////////////
768 // Coordinate Transformations
769 ///////////////////////////////////////////////////////////////////////
773 SGP::transformNDCtoMC (double* x, double* y)
775 if (m_bRecalcTransform)
777 ndc_to_mc.transformPoint (x, y);
782 SGP::transformMCtoNDC (double* x, double* y)
784 if (m_bRecalcTransform)
786 mc_to_ndc.transformPoint (x, y);
791 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
793 if (m_bRecalcTransform)
797 mc_to_ndc.transformPoint (x, y);
802 // calc_transform Calculate transform matrices
805 SGP::calc_transform ()
807 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
808 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
810 wc_to_ndc.setIdentity();
811 wc_to_ndc.mtx[0][0] = scaleX;
812 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
813 wc_to_ndc.mtx[1][1] = scaleY;
814 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
816 mc_to_ndc = m_ctm * wc_to_ndc;
817 ndc_to_mc = mc_to_ndc.invert();
819 m_bRecalcTransform = false;
830 SGP::ctmSet (const TransformationMatrix2D& m)
838 SGP::preTranslate (double x, double y)
840 TransformationMatrix2D m;
842 m.setTranslate (x, y);
848 SGP::postTranslate (double x, double y)
850 TransformationMatrix2D m;
852 m.setTranslate (x, y);
858 SGP::preScale (double sx, double sy)
860 TransformationMatrix2D m;
868 SGP::postScale (double sx, double sy)
870 TransformationMatrix2D m;
879 SGP::preRotate (double theta)
881 TransformationMatrix2D m;
890 SGP::postRotate (double theta)
892 TransformationMatrix2D m;
900 SGP::preShear (double shrx, double shry)
902 TransformationMatrix2D m;
904 m.setShear (shrx, shry);
910 SGP::postShear (double shrx, double shry)
912 TransformationMatrix2D m;
914 m.setShear (shrx, shry);
919 ////////////////////////////////////////////////////////////////////////
921 ////////////////////////////////////////////////////////////////////////
923 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
924 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
926 {'\000', '\000', '\010', '\000', '\000'}, // small dot
927 {'\000', '\034', '\024', '\034', '\000'}, // empty square
928 {'\000', '\034', '\034', '\034', '\000'}, // filled square
929 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
930 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
931 {'\010', '\010', '\076', '\010', '\010'}, // cross
932 {'\000', '\024', '\010', '\024', '\000'}, // X
933 {'\034', '\042', '\042', '\042', '\034'}, // open circle
934 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
935 {'\076', '\042', '\042', '\042', '\076'}, // big open square
936 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
942 SGP::setDC (wxDC* pDC)