1 #include "GLView.h"
2 
3 #include "stdio.h"
4 #include "colormap.h"
5 #include "rendersettings.h"
6 #include "printutils.h"
7 #include "renderer.h"
8 #include "degree_trig.h"
9 #include <cmath>
10 #include "boost-utils.h"
11 #ifdef _WIN32
12 #include <GL/wglew.h>
13 #elif !defined(__APPLE__)
14 #include <GL/glxew.h>
15 #endif
16 
17 #ifdef ENABLE_OPENCSG
18 #include <opencsg.h>
19 #endif
20 
GLView()21 GLView::GLView()
22 {
23   aspectratio = 1;
24   showedges = false;
25   showfaces = true;
26   showaxes = false;
27   showcrosshairs = false;
28   showscale = false;
29   renderer = nullptr;
30   colorscheme = &ColorMap::inst()->defaultColorScheme();
31   cam = Camera();
32   far_far_away = RenderSettings::inst()->far_gl_clip_limit;
33 #ifdef ENABLE_OPENCSG
34   is_opencsg_capable = false;
35   has_shaders = false;
36   opencsg_support = true;
37   static int sId = 0;
38   this->opencsg_id = sId++;
39 #endif
40 }
41 
setRenderer(Renderer * r)42 void GLView::setRenderer(Renderer* r)
43 {
44   renderer = r;
45 }
46 
47 /* update the color schemes of the Renderer attached to this GLView
48 to match the colorscheme of this GLView.*/
updateColorScheme()49 void GLView::updateColorScheme()
50 {
51   if (this->renderer) this->renderer->setColorScheme(*this->colorscheme);
52 }
53 
54 /* change this GLView's colorscheme to the one given, and update the
55 Renderer attached to this GLView as well. */
setColorScheme(const ColorScheme & cs)56 void GLView::setColorScheme(const ColorScheme &cs)
57 {
58   this->colorscheme = &cs;
59   this->updateColorScheme();
60 }
61 
setColorScheme(const std::string & cs)62 void GLView::setColorScheme(const std::string &cs)
63 {
64   const auto colorscheme = ColorMap::inst()->findColorScheme(cs);
65   if (colorscheme) {
66     setColorScheme(*colorscheme);
67   }
68   else {
69 	LOG(message_group::UI_Warning,Location::NONE,"","GLView: unknown colorscheme %1$s",cs);
70   }
71 }
72 
resizeGL(int w,int h)73 void GLView::resizeGL(int w, int h)
74 {
75   cam.pixel_width = w;
76   cam.pixel_height = h;
77   glViewport(0, 0, w, h);
78   aspectratio = 1.0*w/h;
79 }
80 
setCamera(const Camera & cam)81 void GLView::setCamera(const Camera &cam)
82 {
83   this->cam = cam;
84 }
85 
setupCamera() const86 void GLView::setupCamera() const
87 {
88   glMatrixMode(GL_PROJECTION);
89   glLoadIdentity();
90   auto dist = cam.zoomValue();
91   switch (this->cam.projection) {
92 	case Camera::ProjectionType::PERSPECTIVE: {
93 		gluPerspective(cam.fov, aspectratio, 0.1 * dist, 100 * dist);
94 		break;
95 	}
96 	default:
97 	case Camera::ProjectionType::ORTHOGONAL: {
98 		auto height = dist * tan_degrees(cam.fov / 2);
99 		glOrtho(-height * aspectratio, height * aspectratio,
100 		        -height, height,
101 		        -100 * dist, +100 * dist);
102 		break;
103 	}
104   }
105   glMatrixMode(GL_MODELVIEW);
106   glLoadIdentity();
107   gluLookAt(0.0, -dist, 0.0,      // eye
108             0.0, 0.0,   0.0,      // center
109             0.0, 0.0,   1.0);     // up
110 
111   glRotated(cam.object_rot.x(), 1.0, 0.0, 0.0);
112   glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0);
113   glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0);
114 }
115 
paintGL()116 void GLView::paintGL()
117 {
118   glDisable(GL_LIGHTING);
119   auto bgcol = ColorMap::getColor(*this->colorscheme, RenderColor::BACKGROUND_COLOR);
120   auto axescolor = ColorMap::getColor(*this->colorscheme, RenderColor::AXES_COLOR);
121   auto crosshaircol = ColorMap::getColor(*this->colorscheme, RenderColor::CROSSHAIR_COLOR);
122   glClearColor(bgcol[0], bgcol[1], bgcol[2], 1.0);
123   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
124 
125   setupCamera();
126   // The crosshair should be fixed at the center of the viewport...
127   if (showcrosshairs) GLView::showCrosshairs(crosshaircol);
128   glTranslated(cam.object_trans.x(), cam.object_trans.y(), cam.object_trans.z());
129   // ...the axis lines need to follow the object translation.
130   if (showaxes) GLView::showAxes(axescolor);
131   // mark the scale along the axis lines
132   if (showaxes && showscale) GLView::showScalemarkers(axescolor);
133 
134   glEnable(GL_LIGHTING);
135   glDepthFunc(GL_LESS);
136   glCullFace(GL_BACK);
137   glDisable(GL_CULL_FACE);
138   glLineWidth(2);
139   glColor3d(1.0, 0.0, 0.0);
140 
141   if (this->renderer) {
142 #if defined(ENABLE_OPENCSG)
143     // FIXME: This belongs in the OpenCSG renderer, but it doesn't know about this ID yet
144     OpenCSG::setContext(this->opencsg_id);
145 #endif
146     this->renderer->draw(showfaces, showedges);
147   }
148 
149   glDisable(GL_LIGHTING);
150   if (showaxes) GLView::showSmallaxes(axescolor);
151 }
152 
153 #ifdef ENABLE_OPENCSG
154 
glErrorCheck()155 void glErrorCheck() {
156   GLenum err = glGetError();
157   if (err != GL_NO_ERROR) {
158     fprintf(stderr, "OpenGL Error: %s\n", gluErrorString(err));
159   }
160 }
161 
glCompileCheck(GLuint shader)162 void glCompileCheck(GLuint shader) {
163   GLint status;
164   glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
165   if (status == GL_FALSE) {
166     int loglen;
167     char logbuffer[1000];
168     glGetShaderInfoLog(shader, sizeof(logbuffer), &loglen, logbuffer);
169     PRINTDB("OpenGL Shader Program Compile Error:\n%s", logbuffer);
170   }
171 }
172 
enable_opencsg_shaders()173 void GLView::enable_opencsg_shaders()
174 {
175   const char *openscad_disable_gl20_env = getenv("OPENSCAD_DISABLE_GL20");
176   if (openscad_disable_gl20_env && !strcmp(openscad_disable_gl20_env, "0")) {
177     openscad_disable_gl20_env = nullptr;
178   }
179 
180   // All OpenGL 2 contexts are OpenCSG capable
181   if (GLEW_VERSION_2_0) {
182     if (!openscad_disable_gl20_env) {
183       this->is_opencsg_capable = true;
184       this->has_shaders = true;
185     }
186   }
187 
188   // If OpenGL < 2, check for extensions
189   else {
190     if (GLEW_ARB_framebuffer_object) this->is_opencsg_capable = true;
191     else if (GLEW_EXT_framebuffer_object && GLEW_EXT_packed_depth_stencil) {
192       this->is_opencsg_capable = true;
193     }
194 #ifdef _WIN32
195     else if (WGLEW_ARB_pbuffer && WGLEW_ARB_pixel_format) this->is_opencsg_capable = true;
196 #elif !defined(__APPLE__)
197     else if (GLXEW_SGIX_pbuffer && GLXEW_SGIX_fbconfig) this->is_opencsg_capable = true;
198 #endif
199   }
200 
201   if (!GLEW_VERSION_2_0 || !this->is_opencsg_capable) {
202     display_opencsg_warning();
203   }
204 
205   if (opencsg_support && this->has_shaders) {
206 
207     const char *vs_source = R"VS_PROG(
208       #version 110
209 
210       uniform vec4 color1;        // face color
211       uniform vec4 color2;        // edge color
212       attribute vec3 barycentric; // barycentric form of vertex coord
213                                   // either [1,0,0], [0,1,0] or [0,0,1] under normal circumstances (no edges disabled)
214       varying vec3 vBC;           // varying barycentric coords
215       varying float shading;      // multiplied by color1. color2 is without lighting
216 
217       void main(void) {
218         gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
219         vBC = barycentric;
220         vec3 normal, lightDir;
221         normal = normalize(gl_NormalMatrix * gl_Normal);
222         lightDir = normalize(vec3(gl_LightSource[0].position));
223         shading = 0.2 + abs(dot(normal, lightDir));
224       }
225     )VS_PROG";
226 
227     const char *fs_source = R"FS_PROG(
228       #version 110
229 
230       uniform vec4 color1, color2;
231       varying vec3 vBC;
232       varying float shading;
233 
234       vec3 smoothstep3f(vec3 edge0, vec3 edge1, vec3 x) {
235         vec3 t;
236         t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
237         return t * t * (3.0 - 2.0 * t);
238       }
239 
240       float edgeFactor() {
241         const float th = 1.414; // total thickness of half-edge (per triangle) including fade, (must be >= fade)
242         const float fade = 1.414; // thickness of fade (antialiasing) in screen pixels
243         vec3 d = fwidth(vBC);
244         vec3 a3 = smoothstep((th-fade)*d, th*d, vBC);
245         return min(min(a3.x, a3.y), a3.z);
246       }
247 
248       void main(void) {
249         gl_FragColor = mix(color2, vec4(color1.rgb * shading, color1.a), edgeFactor());
250       }
251     )FS_PROG";
252 
253     auto vs = glCreateShader(GL_VERTEX_SHADER);
254     glShaderSource(vs, 1, (const GLchar**)&vs_source, nullptr);
255     glCompileShader(vs);
256     glCompileCheck(vs);
257     glErrorCheck();
258 
259     auto fs = glCreateShader(GL_FRAGMENT_SHADER);
260     glShaderSource(fs, 1, (const GLchar**)&fs_source, nullptr);
261     glCompileShader(fs);
262     glCompileCheck(fs);
263     glErrorCheck();
264 
265     auto edgeshader_prog = glCreateProgram();
266     glAttachShader(edgeshader_prog, vs);
267     glAttachShader(edgeshader_prog, fs);
268     glLinkProgram(edgeshader_prog);
269     glErrorCheck();
270 
271     shaderinfo.progid = edgeshader_prog; // 0
272     shaderinfo.type = GLView::shaderinfo_t::CSG_RENDERING;
273     shaderinfo.data.csg_rendering.color_area = glGetUniformLocation(edgeshader_prog, "color1"); // 1
274     glErrorCheck();
275     shaderinfo.data.csg_rendering.color_edge = glGetUniformLocation(edgeshader_prog, "color2"); // 2
276     glErrorCheck();
277     shaderinfo.data.csg_rendering.barycentric = glGetAttribLocation(edgeshader_prog, "barycentric");
278     glErrorCheck();
279 
280     GLint status;
281     glGetProgramiv(edgeshader_prog, GL_LINK_STATUS, &status);
282     if (status == GL_FALSE) {
283       int loglen;
284       char logbuffer[1000];
285       glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer);
286       fprintf(stderr, "OpenGL Program Linker Error:\n%.*s", loglen, logbuffer);
287     } else {
288       int loglen;
289       char logbuffer[1000];
290       glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer);
291       if (loglen > 0) {
292         fprintf(stderr, "OpenGL Program Link OK:\n%.*s", loglen, logbuffer);
293       }
294       glValidateProgram(edgeshader_prog);
295       glGetProgramInfoLog(edgeshader_prog, sizeof(logbuffer), &loglen, logbuffer);
296       if (loglen > 0) {
297         fprintf(stderr, "OpenGL Program Validation results:\n%.*s", loglen, logbuffer);
298       }
299     }
300   }
301 }
302 #endif
303 
304 
305 #ifdef DEBUG
306 // Requires OpenGL 4.3+
307 /*
308   void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity,
309                                   GLsizei length, const GLchar* message, const void* userParam)
310   {
311     fprintf(stderr, "GL CALLBACK: %s type = 0x%X, severity = 0x%X, message = %s\n",
312             (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
313             type, severity, message);
314   }
315 //*/
316 #endif
317 
initializeGL()318 void GLView::initializeGL()
319 {
320 #ifdef DEBUG
321 /*
322   // Requires OpenGL 4.3+
323   glEnable              ( GL_DEBUG_OUTPUT );
324   glDebugMessageCallback( MessageCallback, 0 );
325 //*/
326 #endif
327 
328   glEnable(GL_DEPTH_TEST);
329   glDepthRange(-far_far_away, +far_far_away);
330 
331   glEnable(GL_BLEND);
332   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
333 
334   GLfloat light_diffuse[] = {1.0, 1.0, 1.0, 1.0};
335   GLfloat light_position0[] = {-1.0, +1.0, +1.0, 0.0};
336   GLfloat light_position1[] = {+1.0, -1.0, -1.0, 0.0};
337 
338   glMatrixMode(GL_MODELVIEW);
339   glLoadIdentity();
340   glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
341   glLightfv(GL_LIGHT0, GL_POSITION, light_position0);
342   glEnable(GL_LIGHT0);
343   glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);
344   glLightfv(GL_LIGHT1, GL_POSITION, light_position1);
345   glEnable(GL_LIGHT1);
346   glEnable(GL_LIGHTING);
347   glEnable(GL_NORMALIZE);
348 
349   glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
350   // The following line is reported to fix issue #71
351 	glMateriali(GL_FRONT_AND_BACK, GL_SHININESS, 64);
352   glEnable(GL_COLOR_MATERIAL);
353 #ifdef ENABLE_OPENCSG
354   enable_opencsg_shaders();
355 #endif
356 }
357 
showSmallaxes(const Color4f & col)358 void GLView::showSmallaxes(const Color4f &col)
359 {
360   auto dpi = this->getDPI();
361   // Small axis cross in the lower left corner
362   glDepthFunc(GL_ALWAYS);
363 
364 	// Set up an orthographic projection of the axis cross in the corner
365   glMatrixMode(GL_PROJECTION);
366   glLoadIdentity();
367   glTranslatef(-0.8f, -0.8f, 0.0f);
368   auto scale = 90;
369   glOrtho(-scale*dpi*aspectratio,scale*dpi*aspectratio,
370 					-scale*dpi,scale*dpi,
371 					-scale*dpi,scale*dpi);
372   gluLookAt(0.0, -1.0, 0.0,
373 						0.0, 0.0, 0.0,
374 						0.0, 0.0, 1.0);
375 
376   glMatrixMode(GL_MODELVIEW);
377   glLoadIdentity();
378   glRotated(cam.object_rot.x(), 1.0, 0.0, 0.0);
379   glRotated(cam.object_rot.y(), 0.0, 1.0, 0.0);
380   glRotated(cam.object_rot.z(), 0.0, 0.0, 1.0);
381 
382   glLineWidth(dpi);
383   glBegin(GL_LINES);
384   glColor3d(1.0, 0.0, 0.0);
385   glVertex3d(0, 0, 0); glVertex3d(10*dpi, 0, 0);
386   glColor3d(0.0, 1.0, 0.0);
387   glVertex3d(0, 0, 0); glVertex3d(0, 10*dpi, 0);
388   glColor3d(0.0, 0.0, 1.0);
389   glVertex3d(0, 0, 0); glVertex3d(0, 0, 10*dpi);
390   glEnd();
391 
392   GLdouble mat_model[16];
393   glGetDoublev(GL_MODELVIEW_MATRIX, mat_model);
394 
395   GLdouble mat_proj[16];
396   glGetDoublev(GL_PROJECTION_MATRIX, mat_proj);
397 
398   GLint viewport[4];
399   glGetIntegerv(GL_VIEWPORT, viewport);
400 
401   GLdouble xlabel_x, xlabel_y, xlabel_z;
402   gluProject(12*dpi, 0, 0, mat_model, mat_proj, viewport, &xlabel_x, &xlabel_y, &xlabel_z);
403   xlabel_x = std::round(xlabel_x); xlabel_y = std::round(xlabel_y);
404 
405   GLdouble ylabel_x, ylabel_y, ylabel_z;
406   gluProject(0, 12*dpi, 0, mat_model, mat_proj, viewport, &ylabel_x, &ylabel_y, &ylabel_z);
407   ylabel_x = std::round(ylabel_x); ylabel_y = std::round(ylabel_y);
408 
409   GLdouble zlabel_x, zlabel_y, zlabel_z;
410   gluProject(0, 0, 12*dpi, mat_model, mat_proj, viewport, &zlabel_x, &zlabel_y, &zlabel_z);
411   zlabel_x = std::round(zlabel_x); zlabel_y = std::round(zlabel_y);
412 
413   glMatrixMode(GL_PROJECTION);
414   glLoadIdentity();
415   glTranslated(-1, -1, 0);
416   glScaled(2.0/viewport[2], 2.0/viewport[3], 1);
417 
418   glMatrixMode(GL_MODELVIEW);
419   glLoadIdentity();
420 
421   glColor3f(col[0], col[1], col[2]);
422 
423   float d = 3*dpi;
424   glBegin(GL_LINES);
425   // X Label
426   glVertex3d(xlabel_x-d, xlabel_y-d, 0); glVertex3d(xlabel_x+d, xlabel_y+d, 0);
427   glVertex3d(xlabel_x-d, xlabel_y+d, 0); glVertex3d(xlabel_x+d, xlabel_y-d, 0);
428   // Y Label
429   glVertex3d(ylabel_x-d, ylabel_y-d, 0); glVertex3d(ylabel_x+d, ylabel_y+d, 0);
430   glVertex3d(ylabel_x-d, ylabel_y+d, 0); glVertex3d(ylabel_x, ylabel_y, 0);
431   // Z Label
432   glVertex3d(zlabel_x-d, zlabel_y-d, 0); glVertex3d(zlabel_x+d, zlabel_y-d, 0);
433   glVertex3d(zlabel_x-d, zlabel_y+d, 0); glVertex3d(zlabel_x+d, zlabel_y+d, 0);
434   glVertex3d(zlabel_x-d, zlabel_y-d, 0); glVertex3d(zlabel_x+d, zlabel_y+d, 0);
435   glEnd();
436 }
437 
showAxes(const Color4f & col)438 void GLView::showAxes(const Color4f &col)
439 {
440   auto l = cam.zoomValue();
441 
442   // Large gray axis cross inline with the model
443   glLineWidth(this->getDPI());
444   glColor3f(col[0], col[1], col[2]);
445 
446   glBegin(GL_LINES);
447   glVertex3d(0, 0, 0);
448   glVertex3d(+l, 0, 0);
449   glVertex3d(0, 0, 0);
450   glVertex3d(0, +l, 0);
451   glVertex3d(0, 0, 0);
452   glVertex3d(0, 0, +l);
453   glEnd();
454 
455   glPushAttrib(GL_LINE_BIT);
456   glEnable(GL_LINE_STIPPLE);
457   glLineStipple(3, 0xAAAA);
458   glBegin(GL_LINES);
459   glVertex3d(0, 0, 0);
460   glVertex3d(-l, 0, 0);
461   glVertex3d(0, 0, 0);
462   glVertex3d(0, -l, 0);
463   glVertex3d(0, 0, 0);
464   glVertex3d(0, 0, -l);
465   glEnd();
466   glPopAttrib();
467 }
468 
showCrosshairs(const Color4f & col)469 void GLView::showCrosshairs(const Color4f &col)
470 {
471   glLineWidth(this->getDPI());
472   glColor3f(col[0], col[1], col[2]);
473   glBegin(GL_LINES);
474   for (double xf = -1; xf <= +1; xf += 2)
475   for (double yf = -1; yf <= +1; yf += 2) {
476     auto vd = cam.zoomValue()/8;
477     glVertex3d(-xf*vd, -yf*vd, -vd);
478     glVertex3d(+xf*vd, +yf*vd, +vd);
479   }
480   glEnd();
481 }
482 
showScalemarkers(const Color4f & col)483 void GLView::showScalemarkers(const Color4f &col)
484 {
485 	// Add scale ticks on large axes
486 	auto l = cam.zoomValue();
487 	glLineWidth(this->getDPI());
488 	glColor3f(col[0], col[1], col[2]);
489 
490 	// Take log of l, discretize, then exponentiate. This is done so that the tick
491 	// denominations change every time the viewport gets 10x bigger or smaller,
492 	// but stays constant in-between. l_adjusted is a step function of l.
493 	const int log_l = static_cast<int>(floor(log10(l)));
494 	const double l_adjusted = pow(10, log_l);
495 
496 	// Calculate tick width.
497 	const double tick_width = l_adjusted / 10.0;
498 
499 	const int size_div_sm = 60; // divisor for l to determine minor tick size
500 	int line_cnt = 0;
501 	for (double i=0; i<l; i+=tick_width){ // i represents the position along the axis
502 		int size_div;
503 		if (line_cnt > 0 && line_cnt % 10 == 0){ // major tick
504 			size_div = size_div_sm * .5; // resize to a major tick
505 			GLView::decodeMarkerValue(i, l, size_div_sm);    // print number
506 		} else {                    // minor tick
507 			size_div = size_div_sm;      // set the minor tick to the standard size
508 
509 			// Draw additional labels if there are few major tick labels visible due to
510 			// zoom. Because the spacing/units of major tick marks only change when the
511 			// viewport changes size by a factor of 10, it can be hard to see the
512 			// major tick labels when when the viewport is slightly larger than size at
513 			// which the last tick spacing change occurred. When zoom level is such
514 			// that very few major tick marks are visible, additional labels are drawn
515 			// every 2 minor ticks. We can detect that very few major ticks are visible
516 			// by checking if the viewport size is larger than the adjusted scale by
517 			// only a small ratio.
518 			const double more_labels_threshold = 3;
519 			// draw additional labels every 2 minor ticks
520 			const int more_labels_freq = 2;
521 			if (line_cnt > 0 && line_cnt % more_labels_freq == 0 && l / l_adjusted < more_labels_threshold) {
522 				GLView::decodeMarkerValue(i, l, size_div_sm);    // print number
523 			}
524 		}
525 		line_cnt++;
526 
527 		/*
528 		 * The length of each tick is proportional to the length of the axis
529 		 * (which changes with the zoom value.)  l/size_div provides the
530 		 * proportional length
531 		 *
532 		 * Commented glVertex3d lines provide additional 'arms' for the tick
533 		 * the number of arms will (hopefully) eventually be driven via Preferences
534 		 */
535 
536 		// positive axes
537 		glBegin(GL_LINES);
538 		// x
539 		glVertex3d(i,0,0); glVertex3d(i,-l/size_div,0); // 1 arm
540 		//glVertex3d(i,-l/size_div,0); glVertex3d(i,l/size_div,0); // 2 arms
541 		//glVertex3d(i,0,-l/size_div); glVertex3d(i,0,l/size_div); // 4 arms (w/ 2 arms line)
542 
543 		// y
544 		glVertex3d(0,i,0); glVertex3d(-l/size_div,i,0); // 1 arm
545 		//glVertex3d(-l/size_div,i,0); glVertex3d(l/size_div,i,0); // 2 arms
546 		//glVertex3d(0,i,-l/size_div); glVertex3d(0,i,l/size_div); // 4 arms (w/ 2 arms line)
547 
548 		// z
549 		glVertex3d(0,0,i); glVertex3d(-l/size_div,0,i); // 1 arm
550 		//glVertex3d(-l/size_div,0,i); glVertex3d(l/size_div,0,i); // 2 arms
551 		//glVertex3d(0,-l/size_div,i); glVertex3d(0,l/size_div,i); // 4 arms (w/ 2 arms line)
552 		glEnd();
553 
554 		// negative axes
555 		glPushAttrib(GL_LINE_BIT);
556 		glEnable(GL_LINE_STIPPLE);
557 		glLineStipple(3, 0xAAAA);
558 		glBegin(GL_LINES);
559 		// x
560 		glVertex3d(-i,0,0); glVertex3d(-i,-l/size_div,0); // 1 arm
561 		//glVertex3d(-i,-l/size_div,0); glVertex3d(-i,l/size_div,0); // 2 arms
562 		//glVertex3d(-i,0,-l/size_div); glVertex3d(-i,0,l/size_div); // 4 arms (w/ 2 arms line)
563 
564 		// y
565 		glVertex3d(0,-i,0); glVertex3d(-l/size_div,-i,0); // 1 arm
566 		//glVertex3d(-l/size_div,-i,0); glVertex3d(l/size_div,-i,0); // 2 arms
567 		//glVertex3d(0,-i,-l/size_div); glVertex3d(0,-i,l/size_div); // 4 arms (w/ 2 arms line)
568 
569 		// z
570 		glVertex3d(0,0,-i); glVertex3d(-l/size_div,0,-i); // 1 arm
571 		//glVertex3d(-l/size_div,0,-i); glVertex3d(l/size_div,0,-i); // 2 arms
572 		//glVertex3d(0,-l/size_div,-i); glVertex3d(0,l/size_div,-i); // 4 arms (w/ 2 arms line)
573 		glEnd();
574 		glPopAttrib();
575 	}
576 }
577 
decodeMarkerValue(double i,double l,int size_div_sm)578 void GLView::decodeMarkerValue(double i, double l, int size_div_sm)
579 {
580 	const auto unsigned_digit = STR(i);
581 
582 	// setup how far above the axis (or tick TBD) to draw the number
583 	double dig_buf = (l/size_div_sm)/4;
584 	// setup the size of the character box
585 	double dig_w = (l/size_div_sm)/2;
586 	double dig_h = (l/size_div_sm) + dig_buf;
587 	// setup the distance between characters
588 	double kern = dig_buf;
589 	double dig_wk = (dig_w) + kern;
590 
591 	// set up ordering for different axes
592 	int ax[6][3] = {
593 		{0,1,2},
594 		{1,0,2},
595 		{1,2,0},
596 		{0,1,2},
597 		{1,0,2},
598 		{1,2,0}};
599 
600 	// set up character vertex sequences for different axes
601 	int or_2[6][6]={
602 		{0,1,3,2,4,5},
603 		{1,0,2,3,5,4},
604 		{1,0,2,3,5,4},
605 		{1,0,2,3,5,4},
606 		{0,1,3,2,4,5},
607 		{0,1,3,2,4,5}};
608 
609 	int or_3[6][7]={
610 		{0,1,3,2,3,5,4},
611 		{1,0,2,3,2,4,5},
612 		{1,0,2,3,2,4,5},
613 		{1,0,2,3,2,4,5},
614 		{0,1,3,2,3,5,4},
615 		{0,1,3,2,3,5,4}};
616 
617 	int or_4[6][5]={
618 		{0,2,3,1,5},
619 		{1,3,2,0,4},
620 		{1,3,2,0,4},
621 		{1,3,2,0,4},
622 		{0,2,3,1,5},
623 		{0,2,3,1,5}};
624 
625 	int or_5[6][6]={
626 		{1,0,2,3,5,4},
627 		{0,1,3,2,4,5},
628 		{0,1,3,2,4,5},
629 		{0,1,3,2,4,5},
630 		{1,0,2,3,5,4},
631 		{1,0,2,3,5,4}};
632 
633 	int or_6[6][6]={
634 		{1,0,4,5,3,2},
635 		{0,1,5,4,2,3},
636 		{0,1,5,4,2,3},
637 		{0,1,5,4,2,3},
638 		{1,0,4,5,3,2},
639 		{1,0,4,5,3,2}};
640 
641 	int or_7[6][3]={
642 		{0,1,4},
643 		{1,0,5},
644 		{1,0,5},
645 		{1,0,5},
646 		{0,1,4},
647 		{0,1,4}};
648 
649 	int or_9[6][5]={
650 		{5,1,0,2,3},
651 		{4,0,1,3,2},
652 		{4,0,1,3,2},
653 		{4,0,1,3,2},
654 		{5,1,0,2,3},
655 		{5,1,0,2,3}};
656 
657 	int or_e[6][7]={
658 		{1,0,2,3,2,4,5},
659 		{0,1,3,2,3,5,4},
660 		{0,1,3,2,3,5,4},
661 		{0,1,3,2,3,5,4},
662 		{1,0,2,3,2,4,5},
663 		{1,0,2,3,2,4,5}};
664 
665 	// walk through axes
666 	for (int di=0; di<6; ++di){
667 
668 		// setup negative axes
669 		double polarity = 1;
670 		auto digit = unsigned_digit;
671 		if (di>2){
672 			polarity = -1;
673 			digit.insert(0, "-");
674 		}
675 
676 		// fix the axes that need to run the opposite direction
677 		if (di>0 && di<4){
678 			std::reverse(digit.begin(),digit.end());
679 		}
680 
681 		// walk through and render the characters of the string
682 		for(std::string::size_type char_num = 0; char_num < digit.size(); ++char_num){
683 			// setup the vertices for the char rendering based on the axis and position
684 			double dig_vrt[6][3] = {
685 				{polarity*((i+((char_num)*dig_wk))-(dig_w/2)),dig_h,0},
686 				{polarity*((i+((char_num)*dig_wk))+(dig_w/2)),dig_h,0},
687 				{polarity*((i+((char_num)*dig_wk))-(dig_w/2)),dig_h/2+dig_buf,0},
688 				{polarity*((i+((char_num)*dig_wk))+(dig_w/2)),dig_h/2+dig_buf,0},
689 				{polarity*((i+((char_num)*dig_wk))-(dig_w/2)),dig_buf,0},
690 				{polarity*((i+((char_num)*dig_wk))+(dig_w/2)),dig_buf,0}};
691 
692 			// convert the char into lines appropriate for the axis being used
693 			// pseudo 7 segment vertices are:
694 			// A--B
695 			// |  |
696 			// C--D
697 			// |  |
698 			// E--F
699 			switch(digit[char_num]){
700 			case '1':
701 				glBegin(GL_LINES);
702 				glVertex3d(dig_vrt[0][ax[di][0]],dig_vrt[0][ax[di][1]],dig_vrt[0][ax[di][2]]);  //a
703 				glVertex3d(dig_vrt[4][ax[di][0]],dig_vrt[4][ax[di][1]],dig_vrt[4][ax[di][2]]);  //e
704 				glEnd();
705 				break;
706 
707 			case '2':
708 				glBegin(GL_LINE_STRIP);
709 				glVertex3d(dig_vrt[or_2[di][0]][ax[di][0]],dig_vrt[or_2[di][0]][ax[di][1]],dig_vrt[or_2[di][0]][ax[di][2]]);  //a
710 				glVertex3d(dig_vrt[or_2[di][1]][ax[di][0]],dig_vrt[or_2[di][1]][ax[di][1]],dig_vrt[or_2[di][1]][ax[di][2]]);  //b
711 				glVertex3d(dig_vrt[or_2[di][2]][ax[di][0]],dig_vrt[or_2[di][2]][ax[di][1]],dig_vrt[or_2[di][2]][ax[di][2]]);  //d
712 				glVertex3d(dig_vrt[or_2[di][3]][ax[di][0]],dig_vrt[or_2[di][3]][ax[di][1]],dig_vrt[or_2[di][3]][ax[di][2]]);  //c
713 				glVertex3d(dig_vrt[or_2[di][4]][ax[di][0]],dig_vrt[or_2[di][4]][ax[di][1]],dig_vrt[or_2[di][4]][ax[di][2]]);  //e
714 				glVertex3d(dig_vrt[or_2[di][5]][ax[di][0]],dig_vrt[or_2[di][5]][ax[di][1]],dig_vrt[or_2[di][5]][ax[di][2]]);  //f
715 				glEnd();
716 				break;
717 
718 			case '3':
719 				glBegin(GL_LINE_STRIP);
720 				glVertex3d(dig_vrt[or_3[di][0]][ax[di][0]],dig_vrt[or_3[di][0]][ax[di][1]],dig_vrt[or_3[di][0]][ax[di][2]]);  //a
721 				glVertex3d(dig_vrt[or_3[di][1]][ax[di][0]],dig_vrt[or_3[di][1]][ax[di][1]],dig_vrt[or_3[di][1]][ax[di][2]]);  //b
722 				glVertex3d(dig_vrt[or_3[di][2]][ax[di][0]],dig_vrt[or_3[di][2]][ax[di][1]],dig_vrt[or_3[di][2]][ax[di][2]]);  //d
723 				glVertex3d(dig_vrt[or_3[di][3]][ax[di][0]],dig_vrt[or_3[di][3]][ax[di][1]],dig_vrt[or_3[di][3]][ax[di][2]]);  //c
724 				glVertex3d(dig_vrt[or_3[di][4]][ax[di][0]],dig_vrt[or_3[di][4]][ax[di][1]],dig_vrt[or_3[di][4]][ax[di][2]]);  //d
725 				glVertex3d(dig_vrt[or_3[di][5]][ax[di][0]],dig_vrt[or_3[di][5]][ax[di][1]],dig_vrt[or_3[di][5]][ax[di][2]]);  //f
726 				glVertex3d(dig_vrt[or_3[di][6]][ax[di][0]],dig_vrt[or_3[di][6]][ax[di][1]],dig_vrt[or_3[di][6]][ax[di][2]]);  //e
727 				glEnd();
728 				break;
729 
730 			case '4':
731 				glBegin(GL_LINE_STRIP);
732 				glVertex3d(dig_vrt[or_4[di][0]][ax[di][0]],dig_vrt[or_4[di][0]][ax[di][1]],dig_vrt[or_4[di][0]][ax[di][2]]);  //a
733 				glVertex3d(dig_vrt[or_4[di][1]][ax[di][0]],dig_vrt[or_4[di][1]][ax[di][1]],dig_vrt[or_4[di][1]][ax[di][2]]);  //c
734 				glVertex3d(dig_vrt[or_4[di][2]][ax[di][0]],dig_vrt[or_4[di][2]][ax[di][1]],dig_vrt[or_4[di][2]][ax[di][2]]);  //d
735 				glVertex3d(dig_vrt[or_4[di][3]][ax[di][0]],dig_vrt[or_4[di][3]][ax[di][1]],dig_vrt[or_4[di][3]][ax[di][2]]);  //b
736 				glVertex3d(dig_vrt[or_4[di][4]][ax[di][0]],dig_vrt[or_4[di][4]][ax[di][1]],dig_vrt[or_4[di][4]][ax[di][2]]);  //f
737 				glEnd();
738 				break;
739 
740 			case '5':
741 				glBegin(GL_LINE_STRIP);
742 				glVertex3d(dig_vrt[or_5[di][0]][ax[di][0]],dig_vrt[or_5[di][0]][ax[di][1]],dig_vrt[or_5[di][0]][ax[di][2]]);  //b
743 				glVertex3d(dig_vrt[or_5[di][1]][ax[di][0]],dig_vrt[or_5[di][1]][ax[di][1]],dig_vrt[or_5[di][1]][ax[di][2]]);  //a
744 				glVertex3d(dig_vrt[or_5[di][2]][ax[di][0]],dig_vrt[or_5[di][2]][ax[di][1]],dig_vrt[or_5[di][2]][ax[di][2]]);  //c
745 				glVertex3d(dig_vrt[or_5[di][3]][ax[di][0]],dig_vrt[or_5[di][3]][ax[di][1]],dig_vrt[or_5[di][3]][ax[di][2]]);  //d
746 				glVertex3d(dig_vrt[or_5[di][4]][ax[di][0]],dig_vrt[or_5[di][4]][ax[di][1]],dig_vrt[or_5[di][4]][ax[di][2]]);  //f
747 				glVertex3d(dig_vrt[or_5[di][5]][ax[di][0]],dig_vrt[or_5[di][5]][ax[di][1]],dig_vrt[or_5[di][5]][ax[di][2]]);  //e
748 				glEnd();
749 				break;
750 
751 			case '6':
752 				glBegin(GL_LINE_STRIP);
753 				glVertex3d(dig_vrt[or_6[di][0]][ax[di][0]],dig_vrt[or_6[di][0]][ax[di][1]],dig_vrt[or_6[di][0]][ax[di][2]]);  //b
754 				glVertex3d(dig_vrt[or_6[di][1]][ax[di][0]],dig_vrt[or_6[di][1]][ax[di][1]],dig_vrt[or_6[di][1]][ax[di][2]]);  //a
755 				glVertex3d(dig_vrt[or_6[di][2]][ax[di][0]],dig_vrt[or_6[di][2]][ax[di][1]],dig_vrt[or_6[di][2]][ax[di][2]]);  //e
756 				glVertex3d(dig_vrt[or_6[di][3]][ax[di][0]],dig_vrt[or_6[di][3]][ax[di][1]],dig_vrt[or_6[di][3]][ax[di][2]]);  //f
757 				glVertex3d(dig_vrt[or_6[di][4]][ax[di][0]],dig_vrt[or_6[di][4]][ax[di][1]],dig_vrt[or_6[di][4]][ax[di][2]]);  //d
758 				glVertex3d(dig_vrt[or_6[di][5]][ax[di][0]],dig_vrt[or_6[di][5]][ax[di][1]],dig_vrt[or_6[di][5]][ax[di][2]]);  //c
759 				glEnd();
760 				break;
761 
762 			case '7':
763 				glBegin(GL_LINE_STRIP);
764 				glVertex3d(dig_vrt[or_7[di][0]][ax[di][0]],dig_vrt[or_7[di][0]][ax[di][1]],dig_vrt[or_7[di][0]][ax[di][2]]);  //a
765 				glVertex3d(dig_vrt[or_7[di][1]][ax[di][0]],dig_vrt[or_7[di][1]][ax[di][1]],dig_vrt[or_7[di][1]][ax[di][2]]);  //b
766 				glVertex3d(dig_vrt[or_7[di][2]][ax[di][0]],dig_vrt[or_7[di][2]][ax[di][1]],dig_vrt[or_7[di][2]][ax[di][2]]);  //e
767 				glEnd();
768 				break;
769 
770 			case '8':
771 				glBegin(GL_LINE_STRIP);
772 				glVertex3d(dig_vrt[2][ax[di][0]],dig_vrt[2][ax[di][1]],dig_vrt[2][ax[di][2]]);  //c
773 				glVertex3d(dig_vrt[3][ax[di][0]],dig_vrt[3][ax[di][1]],dig_vrt[3][ax[di][2]]);  //d
774 				glVertex3d(dig_vrt[1][ax[di][0]],dig_vrt[1][ax[di][1]],dig_vrt[1][ax[di][2]]);  //b
775 				glVertex3d(dig_vrt[0][ax[di][0]],dig_vrt[0][ax[di][1]],dig_vrt[0][ax[di][2]]);  //a
776 				glVertex3d(dig_vrt[4][ax[di][0]],dig_vrt[4][ax[di][1]],dig_vrt[4][ax[di][2]]);  //e
777 				glVertex3d(dig_vrt[5][ax[di][0]],dig_vrt[5][ax[di][1]],dig_vrt[5][ax[di][2]]);  //f
778 				glVertex3d(dig_vrt[3][ax[di][0]],dig_vrt[3][ax[di][1]],dig_vrt[3][ax[di][2]]);  //d
779 				glEnd();
780 				break;
781 
782 			case '9':
783 				glBegin(GL_LINE_STRIP);
784 				glVertex3d(dig_vrt[or_9[di][0]][ax[di][0]],dig_vrt[or_9[di][0]][ax[di][1]],dig_vrt[or_9[di][0]][ax[di][2]]);  //f
785 				glVertex3d(dig_vrt[or_9[di][1]][ax[di][0]],dig_vrt[or_9[di][1]][ax[di][1]],dig_vrt[or_9[di][1]][ax[di][2]]);  //b
786 				glVertex3d(dig_vrt[or_9[di][2]][ax[di][0]],dig_vrt[or_9[di][2]][ax[di][1]],dig_vrt[or_9[di][2]][ax[di][2]]);  //a
787 				glVertex3d(dig_vrt[or_9[di][3]][ax[di][0]],dig_vrt[or_9[di][3]][ax[di][1]],dig_vrt[or_9[di][3]][ax[di][2]]);  //c
788 				glVertex3d(dig_vrt[or_9[di][4]][ax[di][0]],dig_vrt[or_9[di][4]][ax[di][1]],dig_vrt[or_9[di][4]][ax[di][2]]);  //d
789 				glEnd();
790 				break;
791 
792 			case '0':
793 				glBegin(GL_LINE_LOOP);
794 				glVertex3d(dig_vrt[0][ax[di][0]],dig_vrt[0][ax[di][1]],dig_vrt[0][ax[di][2]]);  //a
795 				glVertex3d(dig_vrt[1][ax[di][0]],dig_vrt[1][ax[di][1]],dig_vrt[1][ax[di][2]]);  //b
796 				glVertex3d(dig_vrt[5][ax[di][0]],dig_vrt[5][ax[di][1]],dig_vrt[5][ax[di][2]]);  //f
797 				glVertex3d(dig_vrt[4][ax[di][0]],dig_vrt[4][ax[di][1]],dig_vrt[4][ax[di][2]]);  //e
798 				glEnd();
799 				break;
800 
801 			case '-':
802 				glBegin(GL_LINES);
803 				glVertex3d(dig_vrt[2][ax[di][0]],dig_vrt[2][ax[di][1]],dig_vrt[2][ax[di][2]]);  //c
804 				glVertex3d(dig_vrt[3][ax[di][0]],dig_vrt[3][ax[di][1]],dig_vrt[3][ax[di][2]]);  //d
805 				glEnd();
806 				break;
807 
808 			case '.':
809 				glBegin(GL_LINES);
810 				glVertex3d(dig_vrt[4][ax[di][0]],dig_vrt[4][ax[di][1]],dig_vrt[4][ax[di][2]]);  //e
811 				glVertex3d(dig_vrt[5][ax[di][0]],dig_vrt[5][ax[di][1]],dig_vrt[5][ax[di][2]]);  //f
812 				glEnd();
813 				break;
814 
815 			case 'e':
816 				glBegin(GL_LINE_STRIP);
817 				glVertex3d(dig_vrt[or_e[di][0]][ax[di][0]],dig_vrt[or_e[di][0]][ax[di][1]],dig_vrt[or_e[di][0]][ax[di][2]]);  //b
818 				glVertex3d(dig_vrt[or_e[di][1]][ax[di][0]],dig_vrt[or_e[di][1]][ax[di][1]],dig_vrt[or_e[di][1]][ax[di][2]]);  //a
819 				glVertex3d(dig_vrt[or_e[di][2]][ax[di][0]],dig_vrt[or_e[di][2]][ax[di][1]],dig_vrt[or_e[di][2]][ax[di][2]]);  //c
820 				glVertex3d(dig_vrt[or_e[di][3]][ax[di][0]],dig_vrt[or_e[di][3]][ax[di][1]],dig_vrt[or_e[di][3]][ax[di][2]]);  //d
821 				glVertex3d(dig_vrt[or_e[di][4]][ax[di][0]],dig_vrt[or_e[di][4]][ax[di][1]],dig_vrt[or_e[di][4]][ax[di][2]]);  //c
822 				glVertex3d(dig_vrt[or_e[di][5]][ax[di][0]],dig_vrt[or_e[di][5]][ax[di][1]],dig_vrt[or_e[di][5]][ax[di][2]]);  //e
823 				glVertex3d(dig_vrt[or_e[di][6]][ax[di][0]],dig_vrt[or_e[di][6]][ax[di][1]],dig_vrt[or_e[di][6]][ax[di][2]]);  //f
824 				glEnd();
825 				break;
826 
827 			}
828 		}
829 	}
830 }
831 
832