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.22 2000/12/18 06:47:03 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()) {
\r
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
176 pen.SetColour(255,255,255);
\r
177 m_driver.idWX()->SetBrush (brushWhite);
\r
178 m_driver.idWX()->DrawRectangle (0, 0, m_iPhysicalXSize, m_iPhysicalYSize);
\r
179 m_driver.idWX()->SetBrush (wxNullBrush);
\r
186 // sgp2_window Set window in world coordinates
190 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
192 if (xmin >= xmax || ymin >= ymax) {
193 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
201 m_bRecalcTransform = true;
206 // sgp2_viewport Set viewport in NDC
209 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
211 if (xmin >= xmax || ymin >= ymax) {
212 sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
220 m_bRecalcTransform = true;
222 viewNDC[0] = xmin; // Array for clip_rect()
229 SGP::getViewport (double& xmin, double& ymin, double& xmax, double& ymax)
238 SGP::getWindow (double& xmin, double& ymin, double& xmax, double& ymax)
248 // frameViewport draw box around viewport
251 SGP::frameViewport (void)
253 stylusNDC (xv_min, yv_min, false);
254 stylusNDC (xv_max, yv_min, true);
255 stylusNDC (xv_max, yv_max, true);
256 stylusNDC (xv_min, yv_max, true);
257 stylusNDC (xv_min, yv_min, true);
261 SGP::setTextColor (int iFGcolor, int iBGcolor)
264 if (m_driver.isWX()) {
266 wxColor colour (s_aRGBColor[iFGcolor].getRed(), s_aRGBColor[iFGcolor].getGreen(), s_aRGBColor[iFGcolor].getBlue());
267 m_driver.idWX()->SetTextForeground (colour);
270 wxColor colour (s_aRGBColor[iBGcolor].getRed(), s_aRGBColor[iBGcolor].getGreen(), s_aRGBColor[iBGcolor].getBlue());
271 m_driver.idWX()->SetTextBackground (colour);
278 SGP::setColor (int icol)
280 if (icol >= 0 && icol < s_iRGBColorCount) {
282 if (m_driver.isG2()) {
283 int iInk = g2_ink (m_driver.idG2(), s_aRGBColor[icol].getRed() / 255., s_aRGBColor[icol].getGreen() / 255., s_aRGBColor[icol].getBlue() / 255.);
284 g2_pen (m_driver.idG2(), iInk);
288 if (m_driver.isWX()) {
289 wxColor colour (s_aRGBColor[icol].getRed(), s_aRGBColor[icol].getGreen(), s_aRGBColor[icol].getBlue());
290 m_pen.SetColour (colour);
291 m_driver.idWX()->SetPen (m_pen);
298 SGP::setPenWidth (int iWidth)
302 if (m_driver.isWX()) {
303 m_pen.SetWidth (iWidth);
304 m_driver.idWX()->SetPen (m_pen);
311 SGP::setRasterOp (int ro)
314 if (m_driver.isWX()) {
321 wxFxn = wxAND_INVERT;
324 wxFxn = wxAND_REVERSE;
354 wxFxn = wxOR_REVERSE;
360 wxFxn = wxSRC_INVERT;
367 m_driver.idWX()->SetLogicalFunction (wxFxn);
374 SGP::setMarker (int idMarke, int iColor)
378 //==============================================================
379 // set line style. Pass 16 bit repeating pattern
380 //==============================================================
382 SGP::setLineStyle (int style)
386 //==============================================================
388 //*==============================================================
391 SGP::lineAbs (double x, double y)
393 if (m_bRecalcTransform)
396 double x1 = m_dCurrentWorldX;
397 double y1 = m_dCurrentWorldY;
398 mc_to_ndc.transformPoint (&x1, &y1);
402 mc_to_ndc.transformPoint (&x2, &y2);
404 if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport
405 stylusNDC (x1, y1, false); // move to first point
406 stylusNDC (x2, y2, true); // draw to second point
409 m_dCurrentWorldX = x;
410 m_dCurrentWorldY = y;
414 SGP::moveAbs (double x, double y)
416 m_dCurrentWorldX = x;
417 m_dCurrentWorldY = y; /* moves are not clipped */
421 SGP::lineRel (double x, double y)
423 lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
427 SGP::moveRel (double x, double y)
429 moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
433 // Height is in master coordinates
435 SGP::setTextSize (double height)
437 height /= (yw_max - yw_min); // convert to NDC
440 g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
443 if (m_driver.isWX()) {
444 double dHeightPixels = height * m_iPhysicalYSize;
445 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
446 m_font.SetPointSize (nearest<int>(dHeightPoints));
447 m_driver.idWX()->SetFont (m_font);
453 SGP::setTextNDCSize (double height)
455 double dHeightPixels = height * m_iPhysicalYSize;
458 g2_set_font_size(m_driver.idG2(), nearest<int>(dHeightPixels));
461 if (m_driver.isWX()) {
462 double dHeightPoints = dHeightPixels * m_dPointsPerPixel;
463 m_font.SetPointSize (nearest<int>(dHeightPoints));
464 m_driver.idWX()->SetFont (m_font);
470 SGP::setTextPointSize (double height)
473 // if (m_driver.isG2())
474 // g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
477 if (m_driver.isWX()) {
478 m_font.SetPointSize (static_cast<int>(height+0.5));
479 m_driver.idWX()->SetFont (m_font);
485 SGP::getTextExtent (const char* szText, double* worldW, double* worldH)
488 if (m_driver.isWX()) {
489 wxString sText (szText);
490 wxCoord deviceW, deviceH;
491 m_driver.idWX()->GetTextExtent (sText, &deviceW, &deviceH);
492 *worldW = static_cast<double>(deviceW) / static_cast<double>(m_iPhysicalXSize);;
493 *worldH = static_cast<double>(deviceH) / static_cast<double>(m_iPhysicalYSize);
494 *worldW *= (xw_max - xw_min);
495 *worldH *= (yw_max - yw_min);
501 SGP::getCharHeight ()
503 double dHeight = (1. / 25.);
506 if (m_driver.isWX()) {
507 dHeight = m_driver.idWX()->GetCharHeight();
508 dHeight /= static_cast<double>(m_iPhysicalYSize);
\r
509 dHeight /= (yv_max - yv_min); // scale to viewport;
512 dHeight *= (yw_max - yw_min); // scale to world coordinates
519 double dWidth = (1. / 80.);
522 if (m_driver.isWX()) {
523 dWidth = m_driver.idWX()->GetCharWidth();
524 dWidth /= static_cast<double>(m_iPhysicalXSize);
\r
525 dWidth /= (xv_max - xv_min); // scale to viewport
528 dWidth *= (xw_max - xw_min); //scale to world coordinates
533 SGP::setTextAngle (double angle)
535 m_dTextAngle = convertRadiansToDegrees(angle);
539 SGP::polylineAbs (double x[], double y[], int n)
541 if (m_bRecalcTransform)
544 double x1 = x[0], y1 = y[0];
545 mc_to_ndc.transformPoint (&x1, &y1);
546 double x2 = x[1], y2 = y[1];
547 mc_to_ndc.transformPoint (&x2, &y2);
549 double xt = x2; // don't pass (x2,y2) to clip, we need them
550 double yt = y2; // as the beginning point of the next line
552 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
553 stylusNDC (x1, y1, false);
554 stylusNDC (xt, yt, true);
557 for (int i = 2; i < n; i++) {
558 x1 = x2; y1 = y2; // NDC endpoint of last line
559 x2 = x[i]; y2 = y[i];
560 mc_to_ndc.transformPoint (&x2, &y2);
563 if (clip_rect (x1, y1, xt, yt, viewNDC)) {
564 stylusNDC (x1, y1, false);
565 stylusNDC (xt, yt, true);
572 SGP::markerAbs (double x, double y)
574 if (m_bRecalcTransform)
579 mc_to_ndc.transformPoint (&xndc, &yndc);
580 markerNDC (xndc, yndc);
581 stylusNDC (xndc, yndc, false); // move to location
582 m_dCurrentWorldX = x;
583 m_dCurrentWorldY = y;
588 SGP::markerRel (double x, double y)
590 markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
595 SGP::pointAbs (double x, double y)
597 if (m_bRecalcTransform)
599 double xndc = x, yndc = y;
600 mc_to_ndc.transformPoint (&xndc, &yndc);
601 pointNDC (xndc, yndc);
602 stylusNDC (xndc, yndc, false); // move to location
603 m_dCurrentWorldX = x;
604 m_dCurrentWorldY = y;
609 SGP::pointRel (double x, double y)
611 pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
616 SGP::drawText (const std::string& rsMessage)
618 drawText (rsMessage.c_str());
622 SGP::drawText (const char *pszMessage)
624 if (m_bRecalcTransform)
627 double xndc = m_dCurrentWorldX;
628 double yndc = m_dCurrentWorldY;
629 mc_to_ndc.transformPoint (&xndc, &yndc);
631 stylusNDC (xndc, yndc, false); // move to location
634 if (m_driver.isG2()) {
635 g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
639 if (m_driver.isWX()) {
640 wxString str (pszMessage);
641 m_driver.idWX()->DrawRotatedText (str, m_iCurrentPhysicalX, m_iCurrentPhysicalY, m_dTextAngle);
648 // drawRect Draw box in graphics mode
651 // drawbox (xmin, ymin, xmax, ymax)
652 // double xmin, ymin Lower left corner of box
653 // double xmax, ymax Upper left corner of box
656 // This routine leaves the current position of graphic cursor at lower
657 // left corner of box.
660 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
662 moveAbs (xmin, ymin);
663 lineAbs (xmax, ymin);
664 lineAbs (xmax, ymax);
665 lineAbs (xmin, ymax);
666 lineAbs (xmin, ymin);
670 // sgp2_circle - draw circle of radius r at current center
673 SGP::drawCircle (const double r)
675 drawArc (r, 0.0, TWOPI);
678 //==============================================================
679 // draw arc around current center. angles in radius
680 //==============================================================
683 SGP::drawArc (const double r, double start, double stop)
691 double x = r * cos ((double) start);
692 double y = r * sin ((double) start);
693 moveRel (x, y); // move from center to start of arc
695 const double thetaIncrement = (5 * (TWOPI / 360));
696 double cosTheta = cos(thetaIncrement);
697 double sinTheta = sin(thetaIncrement);
699 double angle, xp, yp;
700 for (angle = start; angle < stop - thetaIncrement; angle += thetaIncrement) {
701 xp = cosTheta * x - sinTheta * y; // translate point by thetaIncrement
702 yp = sinTheta * x + cosTheta * y;
707 double c = cos (stop - angle);
708 double s = sin (stop - angle);
715 moveRel (-x, -y); // move back to center of circle
720 ///////////////////////////////////////////////////////////////////////
721 // Coordinate Transformations
722 ///////////////////////////////////////////////////////////////////////
726 SGP::transformNDCtoMC (double* x, double* y)
728 if (m_bRecalcTransform)
730 ndc_to_mc.transformPoint (x, y);
735 SGP::transformMCtoNDC (double* x, double* y)
737 if (m_bRecalcTransform)
739 mc_to_ndc.transformPoint (x, y);
744 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
746 if (m_bRecalcTransform)
750 mc_to_ndc.transformPoint (x, y);
755 // calc_transform Calculate transform matrices
758 SGP::calc_transform ()
760 double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
761 double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
763 wc_to_ndc.setIdentity();
764 wc_to_ndc.mtx[0][0] = scaleX;
765 wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
766 wc_to_ndc.mtx[1][1] = scaleY;
767 wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
769 mc_to_ndc = m_ctm * wc_to_ndc;
770 ndc_to_mc = mc_to_ndc.invert();
772 m_bRecalcTransform = false;
783 SGP::ctmSet (const TransformationMatrix2D& m)
791 SGP::preTranslate (double x, double y)
793 TransformationMatrix2D m;
795 m.setTranslate (x, y);
801 SGP::postTranslate (double x, double y)
803 TransformationMatrix2D m;
805 m.setTranslate (x, y);
811 SGP::preScale (double sx, double sy)
813 TransformationMatrix2D m;
821 SGP::postScale (double sx, double sy)
823 TransformationMatrix2D m;
832 SGP::preRotate (double theta)
834 TransformationMatrix2D m;
843 SGP::postRotate (double theta)
845 TransformationMatrix2D m;
853 SGP::preShear (double shrx, double shry)
855 TransformationMatrix2D m;
857 m.setShear (shrx, shry);
863 SGP::postShear (double shrx, double shry)
865 TransformationMatrix2D m;
867 m.setShear (shrx, shry);
872 ////////////////////////////////////////////////////////////////////////
874 ////////////////////////////////////////////////////////////////////////
876 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
877 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] =
879 {'\000', '\000', '\010', '\000', '\000'}, // small dot
880 {'\000', '\034', '\024', '\034', '\000'}, // empty square
881 {'\000', '\034', '\034', '\034', '\000'}, // filled square
882 {'\000', '\010', '\024', '\010', '\000'}, // empty diamond
883 {'\000', '\010', '\034', '\010', '\000'}, // filled diamond
884 {'\010', '\010', '\076', '\010', '\010'}, // cross
885 {'\000', '\024', '\010', '\024', '\000'}, // X
886 {'\034', '\042', '\042', '\042', '\034'}, // open circle
887 {'\034', '\076', '\076', '\076', '\034'}, // filled circle
888 {'\076', '\042', '\042', '\042', '\076'}, // big open square
889 {'\010', '\024', '\042', '\024', '\010'}, // big open diamond
895 SGP::setDC (wxDC* pDC)