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.20 2000/12/18 05:40:30 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();
96 m_pen.SetStyle(wxSOLID);
98 if (m_driver.isWX()) {
99 static const double dScreenDPI = 82;
100 static const double dPointsPerInch = 72.;
101 m_dPointsPerPixel = dPointsPerInch / dScreenDPI;
102 const int iTestPointSize = 12;
103 m_font.SetPointSize (iTestPointSize);
104 m_driver.idWX()->SetFont(m_font);
105 double dTestCharHeight = m_driver.idWX()->GetCharHeight();
106 m_dPointsPerPixel = iTestPointSize / dTestCharHeight;
\r
107 // wxWHITE_BRUSH->SetColour (255, 255, 255);
\r
108 // wxWHITE_BRUSH->SetStyle (wxSOLID);
\r
109 m_driver.idWX()->SetBackground (*wxWHITE_BRUSH);
113 setWindow (0., 0., 1., 1.);
114 setViewport (0., 0., 1., 1.);
116 stylusNDC (0., 0., false);
119 setTextPointSize (12);
125 SGP::stylusNDC (double x, double y, bool beam)
127 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
128 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
130 yp = m_iPhysicalYSize - yp;
135 m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
139 g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
142 m_iCurrentPhysicalX = xp;
143 m_iCurrentPhysicalY = yp;
147 SGP::markerNDC (double x, double y)
152 SGP::pointNDC (double x, double y)
158 // clear Clear Window
165 g2_clear (m_driver.idG2());
168 if (m_driver.isWX()) {
169 wxBrush brushWhite;
\r
170 brushWhite.SetColour(255,255,255);
\r
171 m_driver.idWX()->SetBackground(brushWhite);
\r
172 m_driver.idWX()->Clear();
\r
173 m_driver.idWX()->SetBackground(wxNullBrush);
\r
179 // sgp2_window Set window in world coordinates
183 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
185 if (xmin >= xmax || ymin >= ymax) {
186 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
194 m_bRecalcTransform = true;
199 // sgp2_viewport Set viewport in NDC
202 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
204 if (xmin >= xmax || ymin >= ymax) {
205 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
213 m_bRecalcTransform = true;
215 viewNDC[0] = xmin; // Array for clip_rect()
222 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
231 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
241 // frameViewport draw box around viewport
244 SGP::frameViewport (void)
246 stylusNDC (xv_min, yv_min, false);
247 stylusNDC (xv_max, yv_min, true);
248 stylusNDC (xv_max, yv_max, true);
249 stylusNDC (xv_min, yv_max, true);
250 stylusNDC (xv_min, yv_min, true);
254 SGP::setTextColor (int iFGcolor, int iBGcolor)
257 if (m_driver.isWX()) {
259 wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
260 m_driver.idWX()->SetTextForeground (colour);
263 wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
264 m_driver.idWX()->SetTextBackground (colour);
271 SGP::setColor (int icol)
273 if (icol >= 0 && icol < s_iRGBColorCount) {
275 if (m_driver.isG2()) {
276 int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
277 g2_pen (m_driver.idG2(), iInk);
281 if (m_driver.isWX()) {
282 wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
283 m_pen.SetColour (colour);
284 m_driver.idWX()->SetPen (m_pen);
291 SGP::setPenWidth (int iWidth)
295 if (m_driver.isWX()) {
296 m_pen.SetWidth (iWidth);
297 m_driver.idWX()->SetPen (m_pen);
304 SGP::setRasterOp (int ro)
307 if (m_driver.isWX()) {
314 wxFxn = wxAND_INVERT;
317 wxFxn = wxAND_REVERSE;
347 wxFxn = wxOR_REVERSE;
353 wxFxn = wxSRC_INVERT;
360 m_driver.idWX()->SetLogicalFunction (wxFxn);
367 SGP::setMarker (int idMarke, int iColor)
371 //==============================================================
372 // set line style. Pass 16 bit repeating pattern
373 //==============================================================
375 SGP::setLineStyle (int style)
379 //==============================================================
381 //*==============================================================
384 SGP::lineAbs (double x, double y)
386 if (m_bRecalcTransform)
389 double x1 = m_dCurrentWorldX;
390 double y1 = m_dCurrentWorldY;
391 mc_to_ndc.transformPoint (&x1, &y1);
395 mc_to_ndc.transformPoint (&x2, &y2);
397 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
398 stylusNDC (x1, y1, false); // move to first point
399 stylusNDC (x2, y2, true); // draw to second point
402 m_dCurrentWorldX = x;
403 m_dCurrentWorldY = y;
407 SGP::moveAbs (double x, double y)
409 m_dCurrentWorldX = x;
410 m_dCurrentWorldY = y; /* moves are not clipped */
414 SGP::lineRel (double x, double y)
416 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
420 SGP::moveRel (double x, double y)
422 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
426 // Height is in master coordinates
428 SGP::setTextSize (double height)
430 height /= (yw_max - yw_min); // convert to NDC
433 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
436 if (m_driver.isWX()) {
437 double dHeightPixels = height * m_iPhysicalYSize;
438 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
439 m_font.SetPointSize (nearest<int>(dHeightPoints));
440 m_driver.idWX()->SetFont (m_font);
446 SGP::setTextNDCSize (double height)
448 double dHeightPixels = height * m_iPhysicalYSize;
451 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
454 if (m_driver.isWX()) {
455 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
456 m_font.SetPointSize (nearest<int>(dHeightPoints));
457 m_driver.idWX()->SetFont (m_font);
463 SGP::setTextPointSize (double height)
466 // if (m_driver.isG2())
467 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
470 if (m_driver.isWX()) {
471 m_font.SetPointSize (static_cast<int>(height+0.5));
472 m_driver.idWX()->SetFont (m_font);
478 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
481 if (m_driver.isWX()) {
482 wxString sText (szText);
483 wxCoord deviceW, deviceH;
484 m_driver.idWX()->GetTextExtent (sText, &deviceW, &deviceH);
485 *worldW = static_cast<double>(deviceW) / static_cast<double>(m_iPhysicalXSize);;
486 *worldH = static_cast<double>(deviceH) / static_cast<double>(m_iPhysicalYSize);
487 *worldW *= (xw_max - xw_min);
488 *worldH *= (yw_max - yw_min);
494 SGP::getCharHeight ()
496 double dHeight = (1. / 25.);
499 if (m_driver.isWX()) {
500 dHeight = m_driver.idWX()->GetCharHeight();
501 dHeight /= static_cast<double>(m_iPhysicalYSize);
504 dHeight *= (yw_max - yw_min);
511 double dWidth = (1. / 80.);
514 if (m_driver.isWX()) {
515 dWidth = m_driver.idWX()->GetCharWidth();
516 dWidth /= static_cast<double>(m_iPhysicalXSize);
519 dWidth *= (xw_max - xw_min);
524 SGP::setTextAngle (double angle)
526 m_dTextAngle = convertRadiansToDegrees(angle);
530 SGP::polylineAbs (double x[], double y[], int n)
532 if (m_bRecalcTransform)
535 double x1 = x[0], y1 = y[0];
536 mc_to_ndc.transformPoint (&x1, &y1);
537 double x2 = x[1], y2 = y[1];
538 mc_to_ndc.transformPoint (&x2, &y2);
540 double xt = x2; // don't pass (x2,y2) to clip, we need them
541 double yt = y2; // as the beginning point of the next line
543 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
544 stylusNDC (x1, y1, false);
545 stylusNDC (xt, yt, true);
548 for (int i = 2; i < n; i++) {
549 x1 = x2; y1 = y2; // NDC endpoint of last line
550 x2 = x[i]; y2 = y[i];
551 mc_to_ndc.transformPoint (&x2, &y2);
554 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
555 stylusNDC (x1, y1, false);
556 stylusNDC (xt, yt, true);
563 SGP::markerAbs (double x, double y)
565 if (m_bRecalcTransform)
570 mc_to_ndc.transformPoint (&xndc, &yndc);
571 markerNDC (xndc, yndc);
572 stylusNDC (xndc, yndc, false); // move to location
573 m_dCurrentWorldX = x;
574 m_dCurrentWorldY = y;
579 SGP::markerRel (double x, double y)
581 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
586 SGP::pointAbs (double x, double y)
588 if (m_bRecalcTransform)
590 double xndc = x, yndc = y;
591 mc_to_ndc.transformPoint (&xndc, &yndc);
592 pointNDC (xndc, yndc);
593 stylusNDC (xndc, yndc, false); // move to location
594 m_dCurrentWorldX = x;
595 m_dCurrentWorldY = y;
600 SGP::pointRel (double x, double y)
602 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
607 SGP::drawText (const std::string& rsMessage)
609 drawText (rsMessage.c_str());
613 SGP::drawText (const char *pszMessage)
615 if (m_bRecalcTransform)
618 double xndc = m_dCurrentWorldX;
619 double yndc = m_dCurrentWorldY;
620 mc_to_ndc.transformPoint (&xndc, &yndc);
622 stylusNDC (xndc, yndc, false); // move to location
625 if (m_driver.isG2()) {
626 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
630 if (m_driver.isWX()) {
631 wxString str (pszMessage);
632 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
639 // drawRect Draw box in graphics mode
642 // drawbox (xmin, ymin, xmax, ymax)
643 // double xmin, ymin Lower left corner of box
644 // double xmax, ymax Upper left corner of box
647 // This routine leaves the current position of graphic cursor at lower
648 // left corner of box.
651 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
653 moveAbs (xmin, ymin);
654 lineAbs (xmax, ymin);
655 lineAbs (xmax, ymax);
656 lineAbs (xmin, ymax);
657 lineAbs (xmin, ymin);
661 // sgp2_circle - draw circle of radius r at current center
664 SGP::drawCircle (const double r)
666 drawArc (r, 0.0, TWOPI);
669 //==============================================================
670 // draw arc around current center. angles in radius
671 //==============================================================
674 SGP::drawArc (const double r, double start, double stop)
682 double x = r * cos ((double) start);
683 double y = r * sin ((double) start);
684 moveRel (x, y); // move from center to start of arc
686 const double thetaIncrement = (5 * (TWOPI / 360));
687 double cosTheta = cos(thetaIncrement);
688 double sinTheta = sin(thetaIncrement);
690 double angle, xp, yp;
691 for (angle = start; angle < stop - thetaIncrement; angle += thetaIncrement) {
692 xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
693 yp = sinTheta * x + cosTheta * y;
698 double c = cos (stop - angle);
699 double s = sin (stop - angle);
706 moveRel (-x, -y); // move back to center of circle
711 ///////////////////////////////////////////////////////////////////////
712 // Coordinate Transformations
713 ///////////////////////////////////////////////////////////////////////
717 SGP::transformNDCtoMC (double* x, double* y)
719 if (m_bRecalcTransform)
721 ndc_to_mc.transformPoint (x, y);
726 SGP::transformMCtoNDC (double* x, double* y)
728 if (m_bRecalcTransform)
730 mc_to_ndc.transformPoint (x, y);
735 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
737 if (m_bRecalcTransform)
741 mc_to_ndc.transformPoint (x, y);
746 // calc_transform Calculate transform matrices
749 SGP::calc_transform ()
751 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
752 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
754 wc_to_ndc.setIdentity();
755 wc_to_ndc.mtx[0][0] = scaleX;
756 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
757 wc_to_ndc.mtx[1][1] = scaleY;
758 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
760 mc_to_ndc = m_ctm * wc_to_ndc;
761 ndc_to_mc = mc_to_ndc.invert();
763 m_bRecalcTransform = false;
774 SGP::ctmSet (const TransformationMatrix2D& m)
782 SGP::preTranslate (double x, double y)
784 TransformationMatrix2D m;
786 m.setTranslate (x, y);
792 SGP::postTranslate (double x, double y)
794 TransformationMatrix2D m;
796 m.setTranslate (x, y);
802 SGP::preScale (double sx, double sy)
804 TransformationMatrix2D m;
812 SGP::postScale (double sx, double sy)
814 TransformationMatrix2D m;
823 SGP::preRotate (double theta)
825 TransformationMatrix2D m;
834 SGP::postRotate (double theta)
836 TransformationMatrix2D m;
844 SGP::preShear (double shrx, double shry)
846 TransformationMatrix2D m;
848 m.setShear (shrx, shry);
854 SGP::postShear (double shrx, double shry)
856 TransformationMatrix2D m;
858 m.setShear (shrx, shry);
863 ////////////////////////////////////////////////////////////////////////
865 ////////////////////////////////////////////////////////////////////////
867 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
868 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
870 {'\000', '\000', '\010', '\000', '\000'}, // small dot
871 {'\000', '\034', '\024', '\034', '\000'}, // empty square
872 {'\000', '\034', '\034', '\034', '\000'}, // filled square
873 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
874 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
875 {'\010', '\010', '\076', '\010', '\010'}, // cross
876 {'\000', '\024', '\010', '\024', '\000'}, // X
877 {'\034', '\042', '\042', '\042', '\034'}, // open circle
878 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
879 {'\076', '\042', '\042', '\042', '\076'}, // big open square
880 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
886 SGP::setDC (wxDC* pDC)