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.14 2000/09/04 09:06:46 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 = 640, int ysize = 480)
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 = 640, int ysize = 480)
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 ()
79 // SGP::SGP Constructor for Simple Graphics Package
81 SGP::SGP (const SGPDriver& driver)
84 m_iPhysicalXSize = m_driver.getPhysicalXSize();
85 m_iPhysicalYSize = m_driver.getPhysicalYSize();
87 wc_to_ndc.setIdentity ();
88 mc_to_ndc.setIdentity();
89 ndc_to_mc.setIdentity();
94 m_pen.SetStyle(wxSOLID);
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 = 72;
102 m_font.SetPointSize (iTestPointSize);
103 m_driver.idWX()->SetFont(m_font);
104 double dTestCharHeight = m_driver.idWX()->GetCharHeight();
105 m_dPointsPerPixel = iTestPointSize / dTestCharHeight;
108 setWindow (0., 0., 1., 1.);
109 setViewport (0., 0., 1., 1.);
111 stylusNDC (0., 0., false);
114 setTextPointSize (12);
120 SGP::stylusNDC (double x, double y, bool beam)
122 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
123 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
125 yp = m_iPhysicalYSize - yp;
130 m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
134 g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
137 m_iCurrentPhysicalX = xp;
138 m_iCurrentPhysicalY = yp;
142 SGP::markerNDC (double x, double y)
147 SGP::pointNDC (double x, double y)
153 // clear Clear Window
160 g2_clear (m_driver.idG2());
164 m_driver.idWX()->Clear();
169 // sgp2_window Set window in world coordinates
173 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
175 if (xmin >= xmax || ymin >= ymax) {
176 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
184 m_bRecalcTransform = true;
189 // sgp2_viewport Set viewport in NDC
192 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
194 if (xmin >= xmax || ymin >= ymax) {
195 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
203 m_bRecalcTransform = true;
205 viewNDC[0] = xmin; // Array for clip_rect()
212 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
221 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
231 // frameViewport draw box around viewport
234 SGP::frameViewport (void)
236 stylusNDC (xv_min, yv_min, false);
237 stylusNDC (xv_max, yv_min, true);
238 stylusNDC (xv_max, yv_max, true);
239 stylusNDC (xv_min, yv_max, true);
240 stylusNDC (xv_min, yv_min, true);
244 SGP::setTextColor (int iFGcolor, int iBGcolor)
247 if (m_driver.isWX()) {
249 wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
250 m_driver.idWX()->SetTextForeground (colour);
253 wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
254 m_driver.idWX()->SetTextBackground (colour);
261 SGP::setColor (int icol)
263 if (icol >= 0 && icol < s_iRGBColorCount) {
265 if (m_driver.isG2()) {
266 int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
267 g2_pen (m_driver.idG2(), iInk);
271 if (m_driver.isWX()) {
272 wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
273 m_pen.SetColour (colour);
274 m_driver.idWX()->SetPen (m_pen);
281 SGP::setPenWidth (int iWidth)
285 if (m_driver.isWX()) {
286 m_pen.SetWidth (iWidth);
287 m_driver.idWX()->SetPen (m_pen);
294 SGP::setRasterOp (int ro)
297 if (m_driver.isWX()) {
304 wxFxn = wxAND_INVERT;
307 wxFxn = wxAND_REVERSE;
337 wxFxn = wxOR_REVERSE;
343 wxFxn = wxSRC_INVERT;
350 m_driver.idWX()->SetLogicalFunction (wxFxn);
357 SGP::setMarker (int idMarke, int iColor)
361 //==============================================================
362 // set line style. Pass 16 bit repeating pattern
363 //==============================================================
365 SGP::setLineStyle (int style)
369 //==============================================================
371 //*==============================================================
374 SGP::lineAbs (double x, double y)
376 if (m_bRecalcTransform)
379 double x1 = m_dCurrentWorldX;
380 double y1 = m_dCurrentWorldY;
381 mc_to_ndc.transformPoint (&x1, &y1);
385 mc_to_ndc.transformPoint (&x2, &y2);
387 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
388 stylusNDC (x1, y1, false); // move to first point
389 stylusNDC (x2, y2, true); // draw to second point
392 m_dCurrentWorldX = x;
393 m_dCurrentWorldY = y;
397 SGP::moveAbs (double x, double y)
399 m_dCurrentWorldX = x;
400 m_dCurrentWorldY = y; /* moves are not clipped */
404 SGP::lineRel (double x, double y)
406 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
410 SGP::moveRel (double x, double y)
412 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
416 // Height is in master coordinates
418 SGP::setTextSize (double height)
420 height /= (xw_max - xw_min);
423 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
426 if (m_driver.isWX()) {
427 double dHeightPixels = height * m_iPhysicalYSize;
428 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
429 m_font.SetPointSize (nearest<int>(dHeightPoints));
430 m_driver.idWX()->SetFont (m_font);
436 SGP::setTextNDCSize (double height)
438 double dHeightPixels = height * m_iPhysicalYSize;
441 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
444 if (m_driver.isWX()) {
445 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
446 m_font.SetPointSize (nearest<int>(dHeightPoints));
447 m_driver.idWX()->SetFont (m_font);
453 SGP::setTextPointSize (double height)
456 // if (m_driver.isG2())
457 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
460 if (m_driver.isWX()) {
461 m_font.SetPointSize (height);
462 m_driver.idWX()->SetFont (m_font);
468 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
471 if (m_driver.isWX()) {
472 wxString sText (szText);
473 wxCoord deviceW, deviceH;
474 m_driver.idWX()->GetTextExtent (sText, &deviceW, &deviceH);
475 *worldW = static_cast<double>(deviceW) / static_cast<double>(m_iPhysicalXSize);;
476 *worldH = static_cast<double>(deviceH) / static_cast<double>(m_iPhysicalYSize);
477 *worldW *= (xw_max - xw_min);
478 *worldH *= (yw_max - yw_min);
484 SGP::getCharHeight ()
486 double dHeight = (1. / 25.);
489 if (m_driver.isWX()) {
490 dHeight = m_driver.idWX()->GetCharHeight();
491 dHeight /= static_cast<double>(m_iPhysicalYSize);
494 dHeight *= (yw_max - yw_min);
501 double dWidth = (1. / 80.);
504 if (m_driver.isWX()) {
505 dWidth = m_driver.idWX()->GetCharWidth();
506 dWidth /= static_cast<double>(m_iPhysicalXSize);
509 dWidth *= (xw_max - xw_min);
514 SGP::setTextAngle (double angle)
516 m_dTextAngle = convertRadiansToDegrees(angle);
520 SGP::polylineAbs (double x[], double y[], int n)
522 if (m_bRecalcTransform)
525 double x1 = x[0], y1 = y[0];
526 mc_to_ndc.transformPoint (&x1, &y1);
527 double x2 = x[1], y2 = y[1];
528 mc_to_ndc.transformPoint (&x2, &y2);
530 double xt = x2; // don't pass (x2,y2) to clip, we need them
531 double yt = y2; // as the beginning point of the next line
533 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
534 stylusNDC (x1, y1, false);
535 stylusNDC (xt, yt, true);
538 for (int i = 2; i < n; i++) {
539 x1 = x2; y1 = y2; // NDC endpoint of last line
540 x2 = x[i]; y2 = y[i];
541 mc_to_ndc.transformPoint (&x2, &y2);
544 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
545 stylusNDC (x1, y1, false);
546 stylusNDC (xt, yt, true);
553 SGP::markerAbs (double x, double y)
555 if (m_bRecalcTransform)
560 mc_to_ndc.transformPoint (&xndc, &yndc);
561 markerNDC (xndc, yndc);
562 stylusNDC (xndc, yndc, false); // move to location
563 m_dCurrentWorldX = x;
564 m_dCurrentWorldY = y;
569 SGP::markerRel (double x, double y)
571 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
576 SGP::pointAbs (double x, double y)
578 if (m_bRecalcTransform)
580 double xndc = x, yndc = y;
581 mc_to_ndc.transformPoint (&xndc, &yndc);
582 pointNDC (xndc, yndc);
583 stylusNDC (xndc, yndc, false); // move to location
584 m_dCurrentWorldX = x;
585 m_dCurrentWorldY = y;
590 SGP::pointRel (double x, double y)
592 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
597 SGP::drawText (const string& rsMessage)
599 drawText (rsMessage.c_str());
603 SGP::drawText (const char *pszMessage)
605 if (m_bRecalcTransform)
608 double xndc = m_dCurrentWorldX;
609 double yndc = m_dCurrentWorldY;
610 mc_to_ndc.transformPoint (&xndc, &yndc);
612 stylusNDC (xndc, yndc, false); // move to location
615 if (m_driver.isG2()) {
616 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
620 if (m_driver.isWX()) {
621 wxString str (pszMessage);
622 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
629 // drawRect Draw box in graphics mode
632 // drawbox (xmin, ymin, xmax, ymax)
633 // double xmin, ymin Lower left corner of box
634 // double xmax, ymax Upper left corner of box
637 // This routine leaves the current position of graphic cursor at lower
638 // left corner of box.
641 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
643 moveAbs (xmin, ymin);
644 lineAbs (xmax, ymin);
645 lineAbs (xmax, ymax);
646 lineAbs (xmin, ymax);
647 lineAbs (xmin, ymin);
651 // sgp2_circle - draw circle of radius r at current center
654 SGP::drawCircle (const double r)
656 drawArc (r, 0.0, TWOPI);
659 //==============================================================
660 // draw arc around current center. angles in radius
661 //==============================================================
664 SGP::drawArc (const double r, double start, double stop)
672 double x = r * cos ((double) start);
673 double y = r * sin ((double) start);
674 moveRel (x, y); // move from center to start of arc
676 const double thetaIncrement = (5 * (TWOPI / 360));
677 double cosTheta = cos(thetaIncrement);
678 double sinTheta = sin(thetaIncrement);
680 double angle, xp, yp;
681 for (angle = start; angle < stop - thetaIncrement; angle += thetaIncrement) {
682 xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
683 yp = sinTheta * x + cosTheta * y;
688 double c = cos (stop - angle);
689 double s = sin (stop - angle);
696 moveRel (-x, -y); // move back to center of circle
701 ///////////////////////////////////////////////////////////////////////
702 // Coordinate Transformations
703 ///////////////////////////////////////////////////////////////////////
707 SGP::transformNDCtoMC (double* x, double* y)
709 if (m_bRecalcTransform)
711 ndc_to_mc.transformPoint (x, y);
716 SGP::transformMCtoNDC (double* x, double* y)
718 if (m_bRecalcTransform)
720 mc_to_ndc.transformPoint (x, y);
725 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
727 if (m_bRecalcTransform)
731 mc_to_ndc.transformPoint (x, y);
736 // calc_transform Calculate transform matrices
739 SGP::calc_transform ()
741 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
742 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
744 wc_to_ndc.setIdentity();
745 wc_to_ndc.mtx[0][0] = scaleX;
746 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
747 wc_to_ndc.mtx[1][1] = scaleY;
748 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
750 mc_to_ndc = m_ctm * wc_to_ndc;
751 ndc_to_mc = mc_to_ndc.invert();
753 m_bRecalcTransform = false;
764 SGP::ctmSet (const TransformationMatrix2D& m)
772 SGP::preTranslate (double x, double y)
774 TransformationMatrix2D m;
776 m.setTranslate (x, y);
782 SGP::postTranslate (double x, double y)
784 TransformationMatrix2D m;
786 m.setTranslate (x, y);
792 SGP::preScale (double sx, double sy)
794 TransformationMatrix2D m;
802 SGP::postScale (double sx, double sy)
804 TransformationMatrix2D m;
813 SGP::preRotate (double theta)
815 TransformationMatrix2D m;
824 SGP::postRotate (double theta)
826 TransformationMatrix2D m;
834 SGP::preShear (double shrx, double shry)
836 TransformationMatrix2D m;
838 m.setShear (shrx, shry);
844 SGP::postShear (double shrx, double shry)
846 TransformationMatrix2D m;
848 m.setShear (shrx, shry);
853 ////////////////////////////////////////////////////////////////////////
855 ////////////////////////////////////////////////////////////////////////
857 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
858 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
860 {'\000', '\000', '\010', '\000', '\000'}, // small dot
861 {'\000', '\034', '\024', '\034', '\000'}, // empty square
862 {'\000', '\034', '\034', '\034', '\000'}, // filled square
863 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
864 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
865 {'\010', '\010', '\076', '\010', '\010'}, // cross
866 {'\000', '\024', '\010', '\024', '\000'}, // X
867 {'\034', '\042', '\042', '\042', '\034'}, // open circle
868 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
869 {'\076', '\042', '\042', '\042', '\076'}, // big open square
870 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
876 SGP::setDC (wxDC* pDC)