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.16 2000/10/11 08:16:08 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 ()
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 = 72;
103 m_font.SetPointSize (iTestPointSize);
104 m_driver.idWX()->SetFont(m_font);
105 double dTestCharHeight = m_driver.idWX()->GetCharHeight();
106 m_dPointsPerPixel = iTestPointSize / dTestCharHeight;
110 setWindow (0., 0., 1., 1.);
111 setViewport (0., 0., 1., 1.);
113 stylusNDC (0., 0., false);
116 setTextPointSize (12);
122 SGP::stylusNDC (double x, double y, bool beam)
124 int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
125 int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
127 yp = m_iPhysicalYSize - yp;
132 m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
136 g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
139 m_iCurrentPhysicalX = xp;
140 m_iCurrentPhysicalY = yp;
144 SGP::markerNDC (double x, double y)
149 SGP::pointNDC (double x, double y)
155 // clear Clear Window
162 g2_clear (m_driver.idG2());
166 m_driver.idWX()->Clear();
171 // sgp2_window Set window in world coordinates
175 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
177 if (xmin >= xmax || ymin >= ymax) {
178 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
186 m_bRecalcTransform = true;
191 // sgp2_viewport Set viewport in NDC
194 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
196 if (xmin >= xmax || ymin >= ymax) {
197 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
205 m_bRecalcTransform = true;
207 viewNDC[0] = xmin; // Array for clip_rect()
214 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
223 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
233 // frameViewport draw box around viewport
236 SGP::frameViewport (void)
238 stylusNDC (xv_min, yv_min, false);
239 stylusNDC (xv_max, yv_min, true);
240 stylusNDC (xv_max, yv_max, true);
241 stylusNDC (xv_min, yv_max, true);
242 stylusNDC (xv_min, yv_min, true);
246 SGP::setTextColor (int iFGcolor, int iBGcolor)
249 if (m_driver.isWX()) {
251 wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
252 m_driver.idWX()->SetTextForeground (colour);
255 wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
256 m_driver.idWX()->SetTextBackground (colour);
263 SGP::setColor (int icol)
265 if (icol >= 0 && icol < s_iRGBColorCount) {
267 if (m_driver.isG2()) {
268 int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
269 g2_pen (m_driver.idG2(), iInk);
273 if (m_driver.isWX()) {
274 wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
275 m_pen.SetColour (colour);
276 m_driver.idWX()->SetPen (m_pen);
283 SGP::setPenWidth (int iWidth)
287 if (m_driver.isWX()) {
288 m_pen.SetWidth (iWidth);
289 m_driver.idWX()->SetPen (m_pen);
296 SGP::setRasterOp (int ro)
299 if (m_driver.isWX()) {
306 wxFxn = wxAND_INVERT;
309 wxFxn = wxAND_REVERSE;
339 wxFxn = wxOR_REVERSE;
345 wxFxn = wxSRC_INVERT;
352 m_driver.idWX()->SetLogicalFunction (wxFxn);
359 SGP::setMarker (int idMarke, int iColor)
363 //==============================================================
364 // set line style. Pass 16 bit repeating pattern
365 //==============================================================
367 SGP::setLineStyle (int style)
371 //==============================================================
373 //*==============================================================
376 SGP::lineAbs (double x, double y)
378 if (m_bRecalcTransform)
381 double x1 = m_dCurrentWorldX;
382 double y1 = m_dCurrentWorldY;
383 mc_to_ndc.transformPoint (&x1, &y1);
387 mc_to_ndc.transformPoint (&x2, &y2);
389 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
390 stylusNDC (x1, y1, false); // move to first point
391 stylusNDC (x2, y2, true); // draw to second point
394 m_dCurrentWorldX = x;
395 m_dCurrentWorldY = y;
399 SGP::moveAbs (double x, double y)
401 m_dCurrentWorldX = x;
402 m_dCurrentWorldY = y; /* moves are not clipped */
406 SGP::lineRel (double x, double y)
408 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
412 SGP::moveRel (double x, double y)
414 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
418 // Height is in master coordinates
420 SGP::setTextSize (double height)
422 height /= (xw_max - xw_min);
425 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
428 if (m_driver.isWX()) {
429 double dHeightPixels = height * m_iPhysicalYSize;
430 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
431 m_font.SetPointSize (nearest<int>(dHeightPoints));
432 m_driver.idWX()->SetFont (m_font);
438 SGP::setTextNDCSize (double height)
440 double dHeightPixels = height * m_iPhysicalYSize;
443 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
446 if (m_driver.isWX()) {
447 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
448 m_font.SetPointSize (nearest<int>(dHeightPoints));
449 m_driver.idWX()->SetFont (m_font);
455 SGP::setTextPointSize (double height)
458 // if (m_driver.isG2())
459 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
462 if (m_driver.isWX()) {
463 m_font.SetPointSize (static_cast<int>(height+0.5));
464 m_driver.idWX()->SetFont (m_font);
470 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
473 if (m_driver.isWX()) {
474 wxString sText (szText);
475 wxCoord deviceW, deviceH;
476 m_driver.idWX()->GetTextExtent (sText, &deviceW, &deviceH);
477 *worldW = static_cast<double>(deviceW) / static_cast<double>(m_iPhysicalXSize);;
478 *worldH = static_cast<double>(deviceH) / static_cast<double>(m_iPhysicalYSize);
479 *worldW *= (xw_max - xw_min);
480 *worldH *= (yw_max - yw_min);
486 SGP::getCharHeight ()
488 double dHeight = (1. / 25.);
491 if (m_driver.isWX()) {
492 dHeight = m_driver.idWX()->GetCharHeight();
493 dHeight /= static_cast<double>(m_iPhysicalYSize);
496 dHeight *= (yw_max - yw_min);
503 double dWidth = (1. / 80.);
506 if (m_driver.isWX()) {
507 dWidth = m_driver.idWX()->GetCharWidth();
508 dWidth /= static_cast<double>(m_iPhysicalXSize);
511 dWidth *= (xw_max - xw_min);
516 SGP::setTextAngle (double angle)
518 m_dTextAngle = convertRadiansToDegrees(angle);
522 SGP::polylineAbs (double x[], double y[], int n)
524 if (m_bRecalcTransform)
527 double x1 = x[0], y1 = y[0];
528 mc_to_ndc.transformPoint (&x1, &y1);
529 double x2 = x[1], y2 = y[1];
530 mc_to_ndc.transformPoint (&x2, &y2);
532 double xt = x2; // don't pass (x2,y2) to clip, we need them
533 double yt = y2; // as the beginning point of the next line
535 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
536 stylusNDC (x1, y1, false);
537 stylusNDC (xt, yt, true);
540 for (int i = 2; i < n; i++) {
541 x1 = x2; y1 = y2; // NDC endpoint of last line
542 x2 = x[i]; y2 = y[i];
543 mc_to_ndc.transformPoint (&x2, &y2);
546 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
547 stylusNDC (x1, y1, false);
548 stylusNDC (xt, yt, true);
555 SGP::markerAbs (double x, double y)
557 if (m_bRecalcTransform)
562 mc_to_ndc.transformPoint (&xndc, &yndc);
563 markerNDC (xndc, yndc);
564 stylusNDC (xndc, yndc, false); // move to location
565 m_dCurrentWorldX = x;
566 m_dCurrentWorldY = y;
571 SGP::markerRel (double x, double y)
573 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
578 SGP::pointAbs (double x, double y)
580 if (m_bRecalcTransform)
582 double xndc = x, yndc = y;
583 mc_to_ndc.transformPoint (&xndc, &yndc);
584 pointNDC (xndc, yndc);
585 stylusNDC (xndc, yndc, false); // move to location
586 m_dCurrentWorldX = x;
587 m_dCurrentWorldY = y;
592 SGP::pointRel (double x, double y)
594 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
599 SGP::drawText (const string& rsMessage)
601 drawText (rsMessage.c_str());
605 SGP::drawText (const char *pszMessage)
607 if (m_bRecalcTransform)
610 double xndc = m_dCurrentWorldX;
611 double yndc = m_dCurrentWorldY;
612 mc_to_ndc.transformPoint (&xndc, &yndc);
614 stylusNDC (xndc, yndc, false); // move to location
617 if (m_driver.isG2()) {
618 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
622 if (m_driver.isWX()) {
623 wxString str (pszMessage);
624 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
631 // drawRect Draw box in graphics mode
634 // drawbox (xmin, ymin, xmax, ymax)
635 // double xmin, ymin Lower left corner of box
636 // double xmax, ymax Upper left corner of box
639 // This routine leaves the current position of graphic cursor at lower
640 // left corner of box.
643 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
645 moveAbs (xmin, ymin);
646 lineAbs (xmax, ymin);
647 lineAbs (xmax, ymax);
648 lineAbs (xmin, ymax);
649 lineAbs (xmin, ymin);
653 // sgp2_circle - draw circle of radius r at current center
656 SGP::drawCircle (const double r)
658 drawArc (r, 0.0, TWOPI);
661 //==============================================================
662 // draw arc around current center. angles in radius
663 //==============================================================
666 SGP::drawArc (const double r, double start, double stop)
674 double x = r * cos ((double) start);
675 double y = r * sin ((double) start);
676 moveRel (x, y); // move from center to start of arc
678 const double thetaIncrement = (5 * (TWOPI / 360));
679 double cosTheta = cos(thetaIncrement);
680 double sinTheta = sin(thetaIncrement);
682 double angle, xp, yp;
683 for (angle = start; angle < stop - thetaIncrement; angle += thetaIncrement) {
684 xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
685 yp = sinTheta * x + cosTheta * y;
690 double c = cos (stop - angle);
691 double s = sin (stop - angle);
698 moveRel (-x, -y); // move back to center of circle
703 ///////////////////////////////////////////////////////////////////////
704 // Coordinate Transformations
705 ///////////////////////////////////////////////////////////////////////
709 SGP::transformNDCtoMC (double* x, double* y)
711 if (m_bRecalcTransform)
713 ndc_to_mc.transformPoint (x, y);
718 SGP::transformMCtoNDC (double* x, double* y)
720 if (m_bRecalcTransform)
722 mc_to_ndc.transformPoint (x, y);
727 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
729 if (m_bRecalcTransform)
733 mc_to_ndc.transformPoint (x, y);
738 // calc_transform Calculate transform matrices
741 SGP::calc_transform ()
743 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
744 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
746 wc_to_ndc.setIdentity();
747 wc_to_ndc.mtx[0][0] = scaleX;
748 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
749 wc_to_ndc.mtx[1][1] = scaleY;
750 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
752 mc_to_ndc = m_ctm * wc_to_ndc;
753 ndc_to_mc = mc_to_ndc.invert();
755 m_bRecalcTransform = false;
766 SGP::ctmSet (const TransformationMatrix2D& m)
774 SGP::preTranslate (double x, double y)
776 TransformationMatrix2D m;
778 m.setTranslate (x, y);
784 SGP::postTranslate (double x, double y)
786 TransformationMatrix2D m;
788 m.setTranslate (x, y);
794 SGP::preScale (double sx, double sy)
796 TransformationMatrix2D m;
804 SGP::postScale (double sx, double sy)
806 TransformationMatrix2D m;
815 SGP::preRotate (double theta)
817 TransformationMatrix2D m;
826 SGP::postRotate (double theta)
828 TransformationMatrix2D m;
836 SGP::preShear (double shrx, double shry)
838 TransformationMatrix2D m;
840 m.setShear (shrx, shry);
846 SGP::postShear (double shrx, double shry)
848 TransformationMatrix2D m;
850 m.setShear (shrx, shry);
855 ////////////////////////////////////////////////////////////////////////
857 ////////////////////////////////////////////////////////////////////////
859 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
860 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
862 {'\000', '\000', '\010', '\000', '\000'}, // small dot
863 {'\000', '\034', '\024', '\034', '\000'}, // empty square
864 {'\000', '\034', '\034', '\034', '\000'}, // filled square
865 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
866 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
867 {'\010', '\010', '\076', '\010', '\010'}, // cross
868 {'\000', '\024', '\010', '\024', '\000'}, // X
869 {'\034', '\042', '\042', '\042', '\034'}, // open circle
870 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
871 {'\076', '\042', '\042', '\042', '\076'}, // big open square
872 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
878 SGP::setDC (wxDC* pDC)