4d70f8cfe965f265f99f06d723542bd7d8603883
[ctsim.git] / libctgraphics / sgp.cpp
1 /*****************************************************************************
2 ** FILE IDENTIFICATION
3 **
4 **      Name:       sgp.cpp             Simple Graphics Package
5 **      Programmer: Kevin Rosenberg
6 **
7 **  This is part of the CTSim program
8 **  Copyright (C) 1983-2000 Kevin Rosenberg
9 **
10 **  $Id: sgp.cpp,v 1.6 2000/07/28 10:51:31 kevin Exp $
11 **
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.
15 **
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.
20 **
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 ******************************************************************************/
25
26 #include <stdio.h>
27 #include <math.h>
28 #include "ctsupport.h"
29 #include "sgp.h"
30
31
32
33 #ifdef HAVE_WXWINDOWS
34 SGPDriver::SGPDriver (wxDC* pDC, const char* szWinTitle = "", int xsize = 640, int ysize = 480)
35   : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_sWindowTitle(szWinTitle), m_idDriver(0)
36 {
37   m_pDC = pDC;
38   m_idDriver |= SGPDRIVER_WXWINDOWS;
39 }
40 #endif
41
42 SGPDriver::SGPDriver (const char* szWinTitle = "", int xsize = 640, int ysize = 480)
43   : m_iPhysicalXSize(xsize), m_iPhysicalYSize(ysize), m_sWindowTitle(szWinTitle), m_idDriver(0)
44 {
45 #ifdef HAVE_G2_H
46   m_idG2 = g2_open_X11X (m_iPhysicalXSize, m_iPhysicalYSize, 10, 10, const_cast<char*>(szWinTitle), const_cast<char*>(szWinTitle), NULL, -1, -1);
47   m_idDriver |= SGPDRIVER_G2;
48 #endif
49 }
50
51 SGPDriver::~SGPDriver ()
52 {
53   if (isG2()) 
54     g2_close (m_idG2);
55 }
56
57
58 // NAME
59 //   SGP::SGP        Constructor for Simple Graphics Package
60
61 SGP::SGP (const SGPDriver& driver)
62     : m_driver (driver)
63 {
64   m_iPhysicalXSize = m_driver.getPhysicalXSize();
65   m_iPhysicalYSize = m_driver.getPhysicalYSize();
66
67   wc_to_ndc.setIdentity ();
68   mc_to_ndc.setIdentity();
69   ndc_to_mc.setIdentity();
70   m_ctm.setIdentity();
71
72   setWindow (0., 0., 1., 1.);
73   setViewport (0., 0., 1., 1.);
74   moveAbs (0., 0.);
75   m_iCurrentPhysicalX = 0;
76   m_iCurrentPhysicalY = 0;
77  
78   setTextAngle (0.);
79   setTextSize (1. / 25.);
80 }
81
82
83 void
84 SGP::stylusNDC (double x, double y, bool beam)
85 {
86   int xp = static_cast<int>(x * (m_iPhysicalXSize - 1) + 0.5);
87   int yp = static_cast<int>(y * (m_iPhysicalYSize - 1) + 0.5);
88   if (m_driver.isWX())
89     yp = m_iPhysicalYSize - yp;
90
91   if (beam) {
92       if (m_driver.isWX())
93           m_driver.idWX()->DrawLine (m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
94       if (m_driver.isG2())
95           g2_line (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, xp, yp);
96   }
97   m_iCurrentPhysicalX = xp;
98   m_iCurrentPhysicalY = yp;
99 }
100
101 void
102 SGP::markerNDC (double x, double y)
103 {
104 }
105
106 void
107 SGP::pointNDC (double x, double y)
108 {
109 }
110
111
112 // NAME
113 //    clear     Clear Window
114
115 void 
116 SGP::eraseWindow ()
117 {
118   if (m_driver.isG2())
119     g2_clear (m_driver.idG2());
120   if (m_driver.isWX())
121     m_driver.idWX()->Clear();
122 }
123
124 // NAME
125 //      sgp2_window             Set window in world coordinates
126  
127
128 void
129 SGP::setWindow (double xmin, double ymin, double xmax, double ymax)
130 {
131   if (xmin >= xmax || ymin >= ymax) {
132     sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_window]");
133     return;
134   }
135   
136   xw_min = xmin;
137   yw_min = ymin;
138   xw_max = xmax;
139   yw_max = ymax;
140   m_bRecalcTransform = true;
141 }
142
143
144 // NAME
145 //      sgp2_viewport                   Set viewport in NDC
146
147 void
148 SGP::setViewport (double xmin, double ymin, double xmax, double ymax)
149 {
150   if (xmin >= xmax || ymin >= ymax) {
151     sys_error (ERR_WARNING, "Minimum > Maximum [sgp2_viewport]");
152     return;
153   }
154     
155   xv_min = xmin;
156   yv_min = ymin;
157   xv_max = xmax;
158   yv_max = ymax;
159   m_bRecalcTransform = true;
160   
161   viewNDC[0] = xmin;                    // Array for clip_rect() 
162   viewNDC[1] = ymin;
163   viewNDC[2] = xmax;
164   viewNDC[3] = ymax;
165 }
166
167
168 // NAME
169 //      frameViewport           draw box around viewport
170
171 void
172 SGP::frameViewport (void)
173 {
174   stylusNDC (xv_min, yv_min, 0);
175   stylusNDC (xv_max, yv_min, 1);
176   stylusNDC (xv_max, yv_max, 1);
177   stylusNDC (xv_min, yv_max, 1);
178   stylusNDC (xv_min, yv_min, 1);
179 }
180
181 void
182 SGP::setTextColor (int iFGcolor, int iBGcolor)
183 {
184 }
185
186 void 
187 SGP::setColor (int icol)
188 {
189 }
190
191 void
192 SGP::setMarker (int idMarke, int iColor)
193 {
194 }
195
196 //==============================================================
197 // set line style.  Pass 16 bit repeating pattern               
198 //==============================================================
199 void 
200 SGP::setLineStyle (int style)
201 {
202 }
203
204 //==============================================================
205 // absolute draw to                                             
206 //*==============================================================
207
208 void 
209 SGP::lineAbs (double x, double y)
210 {
211   if (m_bRecalcTransform)
212     calc_transform();
213
214   double x1 = m_dCurrentWorldX;
215   double y1 = m_dCurrentWorldY;
216   mc_to_ndc.transformPoint (&x1, &y1);
217
218   double x2 = x;
219   double y2 = y;
220   mc_to_ndc.transformPoint (&x1, &y2);
221   
222   if (clip_rect (x1, y1, x2, y2, viewNDC) == true) { // clip to viewport 
223     stylusNDC (x1, y1, 0);  // move to first point
224     stylusNDC (x2, y2, 1);  // draw to second point 
225   }
226
227   m_dCurrentWorldX = x;
228   m_dCurrentWorldY = y;
229 }
230
231 void 
232 SGP::moveAbs (double x, double y)
233 {
234     m_dCurrentWorldX = x;
235     m_dCurrentWorldY = y;                       /* moves are not clipped */
236 }
237
238 void 
239 SGP::lineRel (double x, double y)
240 {
241   lineAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
242 }
243
244 void 
245 SGP::moveRel (double x, double y)
246 {
247   moveAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
248 }
249
250 void
251 SGP::setTextSize (double height)
252 {
253   if (m_driver.isG2())
254     g2_set_font_size(m_driver.idG2(), (height * m_iPhysicalYSize));
255 }
256
257 void
258 SGP::setTextAngle (double angle)
259 {
260   m_dTextAngle = angle;
261 }
262
263 void 
264 SGP::polylineAbs (double x[], double y[], int n)
265 {
266   if (m_bRecalcTransform)
267     calc_transform();
268
269   double x1 = x[0], y1 = y[0];
270   mc_to_ndc.transformPoint (&x1, &y1);
271   double x2 = x[1], y2 = y[1];
272   mc_to_ndc.transformPoint (&x2, &y2);
273
274   double xt = x2;       // don't pass (x2,y2) to clip, we need them 
275   double yt = y2;       // as the beginning point of the next line 
276
277   if (clip_rect (x1, y1, xt, yt, viewNDC)) {
278     stylusNDC (x1, y1, 0);
279     stylusNDC (xt, yt, 1);
280   }
281   
282   for (int i = 2; i < n; i++) {
283     x1 = x2; y1 = y2;                   // NDC endpoint of last line 
284     x2 = x[i];  y2 = y[i];
285     mc_to_ndc.transformPoint (&x2, &y2);
286     xt = x2;
287     yt = y2;
288     if (clip_rect (x1, y1, xt, yt, viewNDC)) {
289       stylusNDC (x1, y1, 0);
290       stylusNDC (xt, yt, 1);
291     }
292   }
293 }
294
295
296 void 
297 SGP::markerAbs (double x, double y)
298 {
299   if (m_bRecalcTransform)
300     calc_transform();
301
302   double xndc = x;
303   double yndc = y;
304   mc_to_ndc.transformPoint (&xndc, &yndc);
305   markerNDC (xndc, yndc); 
306   stylusNDC (xndc, yndc, false);            // move to location 
307   m_dCurrentWorldX = x;
308   m_dCurrentWorldY = y;
309 }
310
311
312 void 
313 SGP::markerRel (double x, double y)
314 {
315   markerAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
316 }
317
318
319 void 
320 SGP::pointAbs (double x, double y)
321 {
322   if (m_bRecalcTransform)
323     calc_transform();
324   double xndc = x, yndc = y;
325   mc_to_ndc.transformPoint (&xndc, &yndc);
326   pointNDC (xndc, yndc);
327   stylusNDC (xndc, yndc, false);            // move to location 
328   m_dCurrentWorldX = x;
329   m_dCurrentWorldY = y;
330 }
331
332
333 void 
334 SGP::pointRel (double x, double y)
335 {
336   pointAbs (x + m_dCurrentWorldX, y + m_dCurrentWorldY);
337 }
338
339
340 void
341 SGP::drawText (const string& rsMessage)
342 {
343   drawText (rsMessage.c_str());
344 }
345
346 void 
347 SGP::drawText (const char *pszMessage)
348 {
349   if (m_bRecalcTransform)
350     calc_transform();
351
352   double xndc = m_dCurrentWorldX;
353   double yndc = m_dCurrentWorldY;
354   mc_to_ndc.transformPoint (&xndc, &yndc);
355
356   stylusNDC (xndc, yndc, false);            // move to location 
357
358   if (m_driver.isG2()) {
359     if (m_dTextAngle == 0.)
360       g2_string (m_driver.idG2(), m_iCurrentPhysicalX, m_iCurrentPhysicalY, const_cast<char*>(pszMessage));
361   }
362   if (m_driver.isWX()) {
363   }
364 }
365
366
367 // NAME
368 //   drawRect                           Draw box in graphics mode
369 //
370 // SYNOPSIS
371 //   drawbox (xmin, ymin, xmax, ymax)
372 //   double xmin, ymin                  Lower left corner of box
373 //   double xmax, ymax                  Upper left corner of box
374 //
375 // NOTES
376 //   This routine leaves the current position of graphic cursor at lower
377 //   left corner of box.
378
379 void
380 SGP::drawRect (double xmin, double ymin, double xmax, double ymax)
381 {
382         moveAbs (xmin, ymin);
383         lineAbs (xmax, ymin);
384         lineAbs (xmax, ymax);
385         lineAbs (xmin, ymax);
386         lineAbs (xmin, ymin);
387 }
388
389 // FUNCTION
390 // sgp2_circle - draw circle of radius r at current center              
391  
392 void 
393 SGP::drawCircle (const double r)
394 {
395         drawArc (0.0, 7.0, r);
396 }
397
398 // =============================================================
399 // draw arc around current center.  pass angles and radius      
400 //==============================================================
401
402 void 
403 SGP::drawArc (double start, double stop, const double r)
404 {
405   if ((stop-start) > 2 * PI)
406     stop = start + 2 * PI;
407   if ((start-stop) > 2 * PI)
408     stop = start + 2 * PI;
409   while (start >= stop)
410     stop += 2*PI;
411
412   double x = r * cos ((double) start);
413   double y = r * sin ((double) start);
414   moveRel (x, y);          // move from center to start of arc 
415
416   double theta = 5 * PI / 180;
417   double c = cos(theta);
418   double s = sin(theta);
419
420   double angle, xp, yp;
421   for (angle = start; angle < stop - theta; angle += theta) {
422     xp = c * x - s * y;
423     yp = s * x + c * y;
424     lineRel (xp - x, yp - y);
425     x = xp; y = yp;
426   }
427
428   c = cos (stop - angle);
429   s = sin (stop - angle);
430   xp = c * x - s * y;
431   yp = s * x + c * y;
432   lineRel (xp - x, yp - y);
433
434   x = r * cos ((double) stop);
435   y = r * sin ((double) stop);
436   moveRel (-x, -y);             // move back to center of circle 
437 }
438
439
440
441 ///////////////////////////////////////////////////////////////////////
442 // Coordinate Transformations
443 ///////////////////////////////////////////////////////////////////////
444
445
446 void
447 SGP::transformNDCtoMC (double* x, double* y)
448 {
449   if (m_bRecalcTransform)
450     calc_transform();
451   ndc_to_mc.transformPoint (x, y);
452 }
453
454
455 void
456 SGP::transformMCtoNDC (double* x, double* y)
457 {
458   if (m_bRecalcTransform)
459     calc_transform();
460   mc_to_ndc.transformPoint (x, y);
461 }
462
463
464 void
465 SGP::transformMCtoNDC (double xIn, double yIn, double* x, double* y)
466 {
467   if (m_bRecalcTransform)
468     calc_transform();
469   *x = xIn;
470   *y = yIn;
471   mc_to_ndc.transformPoint (x, y);
472 }
473
474
475 // NAME
476 //      calc_transform                  Calculate transform matrices
477
478 void
479 SGP::calc_transform ()
480 {
481   double scaleX = (xv_max - xv_min) / (xw_max - xw_min);
482   double scaleY = (yv_max - yv_min) / (yw_max - yw_min);
483     
484   wc_to_ndc.setIdentity();
485   wc_to_ndc.mtx[0][0] = scaleX;
486   wc_to_ndc.mtx[2][0] = xv_min - scaleX * xw_min;
487   wc_to_ndc.mtx[1][1] = scaleY;
488   wc_to_ndc.mtx[2][1] = yv_min - scaleY * yw_min;
489
490   mc_to_ndc = m_ctm * wc_to_ndc;
491   ndc_to_mc = mc_to_ndc.invert();
492
493   m_bRecalcTransform = false;
494 }
495
496 void
497 SGP::ctmClear ()
498 {
499   m_ctm.setIdentity();
500   calc_transform();
501 }
502
503 void 
504 SGP::ctmSet (const TransformationMatrix2D& m)
505 {
506   m_ctm = m;
507   calc_transform();
508 }
509
510
511 void
512 SGP::preTranslate  (double x, double y)
513 {
514     TransformationMatrix2D m;
515
516     m.setTranslate (x, y);
517     ctmSet (m * m_ctm);
518 }
519
520
521 void 
522 SGP::postTranslate (double x, double y)
523 {
524     TransformationMatrix2D m;
525
526     m.setTranslate (x, y);
527     ctmSet (m_ctm * m);
528 }
529
530
531 void 
532 SGP::preScale (double sx, double sy)
533 {
534     TransformationMatrix2D m;
535
536     m.setScale (sx, sy);
537     ctmSet (m * m_ctm);
538 }
539
540
541 void 
542 SGP::postScale (double sx, double sy)
543 {
544     TransformationMatrix2D m;
545
546     m.setScale (sx, sy);
547     m_ctm = m_ctm * m;
548     ctmSet (m_ctm * m);
549 }
550
551
552 void 
553 SGP::preRotate (double theta)
554 {
555     TransformationMatrix2D m;
556
557     m.setRotate (theta);
558     m_ctm = m * m_ctm;
559     ctmSet (m * m_ctm);
560 }
561
562
563 void 
564 SGP::postRotate (double theta)
565 {
566     TransformationMatrix2D m;
567
568     m.setRotate (theta);
569     ctmSet (m_ctm * m);
570 }
571
572
573 void 
574 SGP::preShear (double shrx, double shry)
575 {
576     TransformationMatrix2D m;
577
578     m.setShear (shrx, shry);
579     ctmSet (m * m_ctm);
580 }
581
582
583 void 
584 SGP::postShear (double shrx, double shry)
585 {
586     TransformationMatrix2D m;
587
588     m.setShear (shrx, shry);
589     ctmSet (m_ctm * m);
590 }
591
592
593 ////////////////////////////////////////////////////////////////////////
594 //  Bitmap Markers
595 ////////////////////////////////////////////////////////////////////////
596
597 // Pixel patterns of marker symbols (1x1 to 5x5 matrix)
598 const unsigned char SGP::MARKER_BITMAP[MARK_COUNT][5] = 
599 {
600     {'\000', '\000', '\010', '\000', '\000'},    // small dot 
601     {'\000', '\034', '\024', '\034', '\000'},    // empty square 
602     {'\000', '\034', '\034', '\034', '\000'},    // filled square 
603     {'\000', '\010', '\024', '\010', '\000'},    // empty diamond 
604     {'\000', '\010', '\034', '\010', '\000'},    // filled diamond 
605     {'\010', '\010', '\076', '\010', '\010'},    // cross 
606     {'\000', '\024', '\010', '\024', '\000'},    // X 
607     {'\034', '\042', '\042', '\042', '\034'},    // open circle 
608     {'\034', '\076', '\076', '\076', '\034'},    // filled circle 
609     {'\076', '\042', '\042', '\042', '\076'},    // big open square 
610     {'\010', '\024', '\042', '\024', '\010'},    // big open diamond 
611 };