1 
2 /*
3  *  Ump - Unnamed Math Program
4  *  Copyright (c) 2005-2006 by Mattias Hultgren <mattias_hultgren@tele2.se>
5  *
6  *  See main.cpp
7  */
8 
9 /*
10 	This is based on $XFree86: xc/programs/glxgears/glxgears.c,v 1.3tsi Exp $
11 */
12 
13 #ifdef PLOT_3D
14 
15 #include <math.h>
16 #include <stdlib.h>
17 #include <sys/types.h>
18 #include <sys/wait.h>
19 #include <signal.h>
20 #include <unistd.h>
21 
22 #include "ump_3d_viewer.h"
23 #include "main.h"
24 #include "vector.h"
25 #include "utf8_string.h"
26 #include "keyfile.h"
27 
28 #include <X11/Xlib.h>
29 #include <X11/Xutil.h>
30 #include <X11/keysym.h>
31 #include <GL/gl.h>
32 #include <GL/glx.h>
33 #include <GL/glu.h>
34 #undef Complex
35 
36 
37 void addon_3d_clear(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
38                     throw(error_obj);
39 void addon_3d_render(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
40                      throw(error_obj);
41 void addon_3d_set_prop(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
42                        throw(error_obj);
43 void addon_3d_get_prop(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
44                        throw(error_obj);
45 void addon_3d_add_plane(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
46                         throw(error_obj);
47 
48 struct _3d_vertex
49 {
50 	float x,y,z;
51 };
52 
53 struct _3d_point
54 {
55 	float x,y,z;
56 	picture_h::colorrgb color;
57 	bool isValid;
58 };
59 
60 enum _3d_plane_type { _3d_plane_wireframed, _3d_plane_solid, _3d_plane_semi_solid };
61 class _3d_plane
62 {
63 public:
64 	uint32 width, height;
65 	_3d_point *points;
66 	_3d_plane_type type;
67 	GLint draw_list;
68 
_3d_plane()69 	_3d_plane() { width = 0; height = 0; points = 0; type = _3d_plane_solid; }
_3d_plane(const _3d_plane & src)70 	_3d_plane( const _3d_plane &src ) throw(error_obj) { width=0; height=0; points=0; type=_3d_plane_solid; *this=src; }
~_3d_plane()71 	~_3d_plane() { clear(); }
operator =(const _3d_plane & src)72 	void operator=(const _3d_plane &src) throw(error_obj)
73 		{
74 			set_size( src.width, src.height );
75 			for( uint32 i=0; i<width*height; i++ )
76 				points[i] = src.points[i];
77 			type = src.type;
78 		}
clear(void)79 	void clear(void) { delete [] points; width = 0; height = 0; points = 0; }
set_size(int new_width,int new_height)80 	void set_size(int new_width, int new_height) throw(error_obj)
81 		{
82 			clear();
83 
84 			if( new_width <= 0  ||  new_height <= 0 )
85 				return;
86 
87 			try{ points = new _3d_point[new_width*new_height]; }
88 			catch(...) { THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") ); }
89 			width = new_width;
90 			height = new_height;
91 		}
92 };
93 
94 
95 struct _3d_Properties
96 {
97 	int width, height;
98 	_3d_vertex look_at;
99 	float camera_dist, camera_angle1, camera_angle2;
100 	picture_h::colorrgb bg_color;
101 	bool only_one_viewer;
102 
103 
104 	Vector<_3d_plane> planes;
105 }_3d_prop;
106 
107 
render_plane(const _3d_plane * plane)108 void render_plane( const _3d_plane *plane )
109 {
110 	bool prev_line_full, line_full;
111 
112 	if( plane->type == _3d_plane_solid )
113 		glPolygonMode( GL_BACK, GL_FILL );
114 	else
115 		glPolygonMode( GL_BACK, GL_LINE );
116 
117 	if( plane->type == _3d_plane_wireframed )
118 		glPolygonMode( GL_FRONT, GL_LINE );
119 	else
120 		glPolygonMode( GL_FRONT, GL_FILL );
121 
122 	line_full = true;
123 	for( uint32 x=0; x<plane->width; x++ )
124 		line_full &= plane->points[x].isValid;
125 
126 	for( uint32 y=0; y<(plane->height-1); y++ )
127 	{
128 		prev_line_full = line_full;
129 
130 		line_full = true;
131 		for( uint32 x=0,tmp=(y+1)*plane->width; x<plane->width; x++ )
132 			line_full &= plane->points[x + tmp].isValid;
133 
134 		if( prev_line_full  &&  line_full )
135 		{
136 			glBegin( GL_QUAD_STRIP );
137 			for( uint32 x=0; x<plane->width; x++ )
138 			{
139 				glColor3f( plane->points[x + (y+1)*plane->width].color.red/255.0f,
140 					   plane->points[x + (y+1)*plane->width].color.green/255.0f,
141 					   plane->points[x + (y+1)*plane->width].color.blue/255.0f );
142 				glVertex3f( plane->points[x + (y+1)*plane->width].x,
143 					    plane->points[x + (y+1)*plane->width].y,
144 					    plane->points[x + (y+1)*plane->width].z );
145 				glColor3f( plane->points[x + y*plane->width].color.red/255.0f,
146 					   plane->points[x + y*plane->width].color.green/255.0f,
147 					   plane->points[x + y*plane->width].color.blue/255.0f );
148 				glVertex3f( plane->points[x + y*plane->width].x,
149 					    plane->points[x + y*plane->width].y,
150 					    plane->points[x + y*plane->width].z );
151 			}
152 			glEnd();
153 		}
154 		else
155 		{
156 			glBegin( GL_QUADS );
157 			for( uint32 x=0; x<(plane->width-1); x++ )
158 			{
159 				if( plane->points[x + y*plane->width].isValid  &&
160 				    plane->points[x+1 + y*plane->width].isValid  &&
161 				    plane->points[x+1 + (y+1)*plane->width].isValid  &&
162 				    plane->points[x + (y+1)*plane->width].isValid )
163 				{
164 					glColor3f( plane->points[x + y*plane->width].color.red/255.0f,
165 						   plane->points[x + y*plane->width].color.green/255.0f,
166 						   plane->points[x + y*plane->width].color.blue/255.0f );
167 					glVertex3f( plane->points[x + y*plane->width].x,
168 						    plane->points[x + y*plane->width].y,
169 						    plane->points[x + y*plane->width].z );
170 					glColor3f( plane->points[x+1 + y*plane->width].color.red/255.0f,
171 						   plane->points[x+1 + y*plane->width].color.green/255.0f,
172 						   plane->points[x+1 + y*plane->width].color.blue/255.0f );
173 					glVertex3f( plane->points[x+1 + y*plane->width].x,
174 						    plane->points[x+1 + y*plane->width].y,
175 						    plane->points[x+1 + y*plane->width].z );
176 					glColor3f( plane->points[x+1 + (y+1)*plane->width].color.red/255.0f,
177 						   plane->points[x+1 + (y+1)*plane->width].color.green/255.0f,
178 						   plane->points[x+1 + (y+1)*plane->width].color.blue/255.0f );
179 					glVertex3f( plane->points[x+1 + (y+1)*plane->width].x,
180 						    plane->points[x+1 + (y+1)*plane->width].y,
181 						    plane->points[x+1 + (y+1)*plane->width].z );
182 					glColor3f( plane->points[x + (y+1)*plane->width].color.red/255.0f,
183 						   plane->points[x + (y+1)*plane->width].color.green/255.0f,
184 						   plane->points[x + (y+1)*plane->width].color.blue/255.0f );
185 					glVertex3f( plane->points[x + (y+1)*plane->width].x,
186 						    plane->points[x + (y+1)*plane->width].y,
187 						    plane->points[x + (y+1)*plane->width].z );
188 				}
189 			}
190 			glEnd();
191 		}
192 	}
193 }
194 
render_scene(const _3d_Properties & render_prop)195 void render_scene( const _3d_Properties &render_prop )
196 {
197 	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
198 
199 	glPushMatrix();
200 
201 	for( int i=0; i<render_prop.planes.get_size(); i++ )
202 	{
203 		// draw the plane from list if it's avaliable
204 		if( render_prop.planes[i]->draw_list != 0 )
205 			glCallList( render_prop.planes[i]->draw_list );
206 		else
207 			render_plane( render_prop.planes[i] );
208 	}
209 
210 	glPopMatrix();
211 	glFlush();
212 }
213 
make_window(const _3d_Properties & render_prop,Display * dpy,Window * winRet,GLXContext * ctxRet)214 void make_window( const _3d_Properties &render_prop, Display *dpy, Window *winRet,
215                   GLXContext *ctxRet) throw(error_obj)
216 {
217 	int attrib[] = { GLX_RGBA,
218 	                 GLX_RED_SIZE, 1,
219 	                 GLX_GREEN_SIZE, 1,
220 	                 GLX_BLUE_SIZE, 1,
221 	                 GLX_DOUBLEBUFFER,
222 	                 GLX_DEPTH_SIZE, 1,
223 	                 None };
224 	int scrnum;
225 	XSetWindowAttributes attr;
226 	unsigned long mask;
227 	Window root;
228 	Window win;
229 	GLXContext ctx;
230 	XVisualInfo *visinfo;
231 	GLint max[2] = { 0, 0 };
232 
233 	scrnum = XScreenNumberOfScreen( XDefaultScreenOfDisplay(dpy) );
234 	root   = XRootWindow(dpy, scrnum);
235 
236 	visinfo = glXChooseVisual( dpy, scrnum, attrib );
237 	if( !visinfo )
238 		THROW_ERROR( ErrorType_General, _("Couldn't get a RGB, Double-buffered visual.") );
239 
240 	// window attributes
241 	attr.background_pixel = 0;
242 	attr.border_pixel = 0;
243 	attr.colormap = XCreateColormap( dpy, root, visinfo->visual, AllocNone );
244 	attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
245 	mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
246 
247 	win = XCreateWindow( dpy, root, 0, 0, render_prop.width, render_prop.height,
248 	                     0, visinfo->depth, InputOutput,
249 	                     visinfo->visual, mask, &attr );
250 
251    // set hints and properties
252 	{
253 		XSizeHints sizehints;
254 		sizehints.width  = render_prop.width;
255 		sizehints.height = render_prop.height;
256 		sizehints.flags = USSize;
257 		XSetNormalHints( dpy, win, &sizehints );
258 		XSetStandardProperties( dpy, win, PRG_NAME, PRG_NAME, None, (char **)NULL, 0, &sizehints);
259 	}
260 
261 	ctx = glXCreateContext( dpy, visinfo, NULL, True );
262 	if( !ctx )
263 		THROW_ERROR( ErrorType_General, _("glXCreateContext failed") );
264 
265 	XFree(visinfo);
266 
267 	XMapWindow(dpy, win);
268 	glXMakeCurrent(dpy, win, ctx);
269 
270 	// Check for maximum size supported by the GL rasterizer
271 	glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max);
272 	if( render_prop.width > max[0]  ||  render_prop.height > max[1] )
273 		THROW_ERROR( ErrorType_General, _("Requested window size is larger than maximum supported by GL engine.") );
274 
275 	*winRet = win;
276 	*ctxRet = ctx;
277 }
278 
update_camera(const _3d_Properties & render_prop)279 void update_camera( const _3d_Properties &render_prop )
280 {
281 	glLoadIdentity();
282 
283 	gluLookAt( render_prop.camera_dist + render_prop.look_at.x, render_prop.look_at.y, render_prop.look_at.z,
284 		       render_prop.look_at.x, render_prop.look_at.y, render_prop.look_at.z,
285 			   0.0f, 1.0f, 0.0f );
286 
287 	glTranslatef( render_prop.look_at.x, render_prop.look_at.y, render_prop.look_at.z );
288 	glRotatef( render_prop.camera_angle1, 0.0, 1.0, 0.0);
289 	glRotatef( render_prop.camera_angle2, 1.0, 0.0, 0.0);
290 	glTranslatef( -render_prop.look_at.x, -render_prop.look_at.y, -render_prop.look_at.z );
291 }
292 
size_changed(const _3d_Properties & render_prop,int w,int h)293 void size_changed( const _3d_Properties &render_prop, int w, int h )
294 {
295 	if(h == 0)
296 		h = 1;
297 
298 	float ratio = float(w) / float(h);
299 
300 	// Reset the coordinate system before modifying
301 	glMatrixMode(GL_PROJECTION);
302 	glLoadIdentity();
303 
304 	// Set the viewport to be the entire window
305 	glViewport(0, 0, w, h);
306 
307 	// Set the correct perspective.
308 	gluPerspective( 45, ratio, 1, 1000 );
309 	glMatrixMode(GL_MODELVIEW);
310 
311 	update_camera( render_prop );
312 }
313 
event_loop(_3d_Properties & render_prop,Display * dpy,Window win)314 void event_loop( _3d_Properties &render_prop, Display *dpy, Window win )
315 {
316 	XEvent event;
317 	char buffer[10];
318 	int code;
319 
320 	while(true)
321 	{
322 		XNextEvent( dpy, &event );
323 		switch( event.type )
324 		{
325 		case Expose:
326 			render_scene( render_prop );
327 			glXSwapBuffers( dpy, win );
328 			break;
329 		case ConfigureNotify:
330 		   size_changed( render_prop, event.xconfigure.width, event.xconfigure.height );
331 		   break;
332 		case KeyPress:
333 			code = XLookupKeysym(&event.xkey, 0);
334 			if( code == XK_Left )
335 			{
336 				render_prop.camera_angle1 += 5.0f;
337 				if( render_prop.camera_angle1 >= 360.0f )
338 					render_prop.camera_angle1 -= 360.0f;
339 				update_camera( render_prop );
340 				render_scene( render_prop );
341 				glXSwapBuffers( dpy, win );
342 			}
343 			else if( code == XK_Right )
344 			{
345 				render_prop.camera_angle1 -= 5.0f;
346 				if( render_prop.camera_angle1 <= 0.0f )
347 					render_prop.camera_angle1 += 360.0f;
348 				update_camera( render_prop );
349 				render_scene( render_prop );
350 				glXSwapBuffers( dpy, win );
351 			}
352 			else if( code == XK_Page_Up )
353 			{
354 				render_prop.camera_dist *= 0.95f;
355 				update_camera( render_prop );
356 				render_scene( render_prop );
357 				glXSwapBuffers( dpy, win );
358 			}
359 			else if( code == XK_Page_Down )
360 			{
361 				render_prop.camera_dist *= 1.05f;
362 				update_camera( render_prop );
363 				render_scene( render_prop );
364 				glXSwapBuffers( dpy, win );
365 			}
366 			else if( code == XK_Down )
367 			{
368 				render_prop.camera_angle2 += 5.0f;
369 				if( render_prop.camera_angle2 >= 360.0f )
370 					render_prop.camera_angle2 -= 360.0f;
371 				update_camera( render_prop );
372 				render_scene( render_prop );
373 				glXSwapBuffers( dpy, win );
374 			}
375 			else if( code == XK_Up )
376 			{
377 				render_prop.camera_angle2 -= 5.0f;
378 				if( render_prop.camera_angle2 <= 0.0f )
379 					render_prop.camera_angle2 += 360.0f;
380 				update_camera( render_prop );
381 				render_scene( render_prop );
382 				glXSwapBuffers( dpy, win );
383 			}
384 			else if( code == XK_Escape )
385 				return;
386 			else
387 			{
388 				XLookupString( &event.xkey, buffer, sizeof(buffer), NULL, NULL );
389 				switch( buffer[0] )
390 				{
391 				case 'Q': case 'q': case 'X': case 'x':
392 					return;
393 				}
394 			}
395 			break;
396 		}
397 	}
398 }
399 
_3d_viewer_child(_3d_Properties render_prop)400 void _3d_viewer_child( _3d_Properties render_prop )
401 {
402 	try
403 	{
404 		Display *disp;
405 		Window win;
406 		GLXContext glx_cont;
407 
408 		//setup a glx-window
409 		disp = XOpenDisplay(NULL);
410 		if( !disp )
411 			THROW_ERROR( ErrorType_General, _("Couldn't open display") );
412 
413 		make_window( render_prop, disp, &win, &glx_cont );
414 		size_changed( render_prop, render_prop.width, render_prop.height );
415 
416 		glClearColor( render_prop.bg_color.red/255.0f, render_prop.bg_color.green/255.0f,
417 		              render_prop.bg_color.blue/255.0f, 1.0f );
418 		glClearDepth( 1.0f );
419 		glDepthFunc( GL_LESS );
420 		glEnable( GL_DEPTH_TEST );
421 		glShadeModel( GL_SMOOTH );
422 
423 
424 
425 		// creating draw lists
426 		for( int i=0; i<render_prop.planes.get_size(); i++ )
427 		{
428 			if( (render_prop.planes[i]->draw_list = glGenLists(1)) != 0 )
429 			{
430 				glNewList( render_prop.planes[i]->draw_list, GL_COMPILE );
431 				render_plane( render_prop.planes[i] );
432 				glEndList();
433 			}
434 		}
435 
436 		// enter viewing/interaction mode
437 		event_loop( render_prop, disp, win );
438 
439 		// cleaning up
440 		glXDestroyContext( disp, glx_cont );
441 		XDestroyWindow( disp, win );
442 		XCloseDisplay( disp );
443 	}
444 	catch( error_obj error )
445 	{
446 		printf("Error: _3d_viewer: %s\n", error.msg );
447 	}
448 	exit(0); // terminating the child process
449 }
450 
_3d_viewer_init(void)451 void _3d_viewer_init(void) throw(error_obj)
452 {
453 	math::add_function( "_3d_clear", addon_3d_clear, false, false );
454 	math::add_function( "_3d_render", addon_3d_render, false, false );
455 	math::add_function( "_3d_set_prop", addon_3d_set_prop, false, true );
456 	math::add_function( "_3d_get_prop", addon_3d_get_prop, false, true );
457 	math::add_function( "_3d_add_plane", addon_3d_add_plane, false, true );
458 
459 	_3d_prop.width = 400;
460 	_3d_prop.height = 400;
461 	_3d_prop.look_at.x = 0.0f;
462 	_3d_prop.look_at.y = 0.0f;
463 	_3d_prop.look_at.z = -0.0f;
464 
465 	_3d_prop.camera_dist = 10.0f;
466 	_3d_prop.camera_angle1 = 0.0f;
467 	_3d_prop.camera_angle2 = 0.0f;
468 
469 	_3d_prop.bg_color.red = 255;
470 	_3d_prop.bg_color.green = 255;
471 	_3d_prop.bg_color.blue = 255;
472 
473 	_3d_prop.only_one_viewer = true;
474 }
475 
476 
477 
478 
479 
480 
481 
482 
483 
484 
485 ///////////////////////////////////////
486 // Here comes the 3d addon functions //
487 ///////////////////////////////////////
488 
addon_3d_clear(math::Variable * res,math::Variable * left,const math::Variable * right,math::VariableList * private_varlist)489 void addon_3d_clear(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
490                     throw(error_obj)
491 {
492 	if( left != 0  ||  right != 0 )
493 		THROW_ERROR( ErrorType_General, "Internal error: _3d_clear wasn't called the right way" );
494 
495 	_3d_prop.planes.clear();
496 
497 	if( res != 0 )
498 		res->set_void();
499 }
500 
501 Vector<pid_t> pid_list;
502 
addon_3d_render(math::Variable * res,math::Variable * left,const math::Variable * right,math::VariableList * private_varlist)503 void addon_3d_render(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
504                      throw(error_obj)
505 {
506 	pid_t child_pid;
507 	if( left != 0  ||  right != 0 )
508 		THROW_ERROR( ErrorType_General, "Internal error: _3d_render wasn't called the right way" );
509 
510 	if( _3d_prop.only_one_viewer )
511 	{
512 // terminate all old viewers
513 		for( int i=0; i<pid_list.get_size(); i++ )
514 			kill( *pid_list[i], 15 );
515 	}
516 
517 	// this loops throu all old child and removes the zombies...
518 	for( int i=0; i<pid_list.get_size(); i++ )
519 	{
520 		if( waitpid( *pid_list[i], 0, WNOHANG ) != 0 )
521 		{
522 			pid_list.remove( i );
523 			i--;
524 		}
525 	}
526 
527 	switch( child_pid = fork() )
528 	{
529 	case -1:
530 		(*TEXTVIEW_FUNCTION)("\nfork failed\n");
531 		break;
532 	case 0:
533 		_3d_viewer_child( _3d_prop );
534 		break;
535 	default:
536 		pid_list.append( child_pid );
537 		break;
538 	}
539 
540 	if( res != 0 )
541 		res->set_void();
542 }
addon_3d_set_prop(math::Variable * res,math::Variable * left,const math::Variable * right,math::VariableList * private_varlist)543 void addon_3d_set_prop(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
544                        throw(error_obj)
545 {
546 	Matrix matr;
547 	Complex tmp;
548 	const math::Array *array;
549 	const utf8_string *str;
550 
551 	if( left != 0  ||  right == 0 )
552 		THROW_ERROR( ErrorType_General, "Internal error: _3d_set_prop wasn't called the right way" );
553 
554 	if( right->get_type() != math::VariableType_Array )
555 	{
556 		char tmp_err[ERROR_OBJ_MSG_LEN];
557 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Data type is unsupported by '%s'"), "_3d_set_prop" );
558 		THROW_ERROR( ErrorType_General, tmp_err );
559 	}
560 
561 	array = right->get_array();
562 
563 	for( uint32 i=0; i<array->get_size(); i++ )
564 	{
565 		if( array->get_variable(i)->get_type() != math::VariableType_String )
566 		{
567 			char tmp_err[ERROR_OBJ_MSG_LEN];
568 			snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Data type is unsupported by '%s'"), "_3d_set_prop" );
569 			THROW_ERROR( ErrorType_General, tmp_err );
570 		}
571 
572 		str = array->get_variable(i)->get_string();
573 
574 		if( *str == "Width" )
575 		{
576 			Integer w;
577 
578 			if( array->get_variable(i+1)->get_type() != math::VariableType_Complex )
579 			{
580 				char tmp_err[ERROR_OBJ_MSG_LEN];
581 				snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Data type is unsupported by '%s'"), "_3d_set_prop" );
582 				THROW_ERROR( ErrorType_General, tmp_err );
583 			}
584 			tmp = *( array->get_variable(i+1)->get_complex() );
585 
586 			if( tmp.imaginary != Real(0.0)  ||  !tmp.real.isInteger() )
587 				THROW_ERROR( ErrorType_General, _("_3d_set_prop: 'Width' must be an integer.") );
588 
589 			tmp.real.get_top( &w );
590 
591 			_3d_prop.width = int( int64(w) );
592 
593 			i++;
594 		}
595 		else if( *str == "Height" )
596 		{
597 			Integer h;
598 
599 			if( array->get_variable(i+1)->get_type() != math::VariableType_Complex )
600 			{
601 				char tmp_err[ERROR_OBJ_MSG_LEN];
602 				snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Data type is unsupported by '%s'"), "_3d_set_prop" );
603 				THROW_ERROR( ErrorType_General, tmp_err );
604 			}
605 			tmp = *( array->get_variable(i+1)->get_complex() );
606 
607 			if( tmp.imaginary != Real(0.0)  ||  !tmp.real.isInteger() )
608 				THROW_ERROR( ErrorType_General, _("_3d_set_prop: 'Width' must be an integer.") );
609 
610 			tmp.real.get_top( &h );
611 
612 			_3d_prop.height = int( int64(h) );
613 
614 			i++;
615 		}
616 		else if( *str == "Camera distance" )
617 		{
618 			if( array->get_variable(i+1)->get_type() != math::VariableType_Complex )
619 			{
620 				char tmp_err[ERROR_OBJ_MSG_LEN];
621 				snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Data type is unsupported by '%s'"), "_3d_set_prop" );
622 				THROW_ERROR( ErrorType_General, tmp_err );
623 			}
624 			tmp = *( array->get_variable(i+1)->get_complex() );
625 			if( tmp.imaginary != Real(0.0)  ||  tmp.real <= Real(0.0) )
626 				THROW_ERROR( ErrorType_General, _("_3d_set_prop: 'Camera distance' must be a positive real value.") );
627 			_3d_prop.camera_dist = float(tmp.real);
628 
629 			i++;
630 		}
631 		else if( *str == "Look at" )
632 		{
633 			float x,y,z;
634 
635 			if( array->get_variable(i+1)->get_type() != math::VariableType_Matrix )
636 			{
637 				char tmp_err[ERROR_OBJ_MSG_LEN];
638 				snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Data type is unsupported by '%s'"), "_3d_set_prop" );
639 				THROW_ERROR( ErrorType_General, tmp_err );
640 			}
641 
642 			matr = *( array->get_variable(i+1)->get_matrix() );
643 
644 			if( matr.get_height() != 3  ||  matr.get_width() != 1 )
645 				THROW_ERROR( ErrorType_General, _("_3d_set_prop: 'Look at's vertex matrix's size dosn't match.") );
646 
647 			tmp = matr.get_complex( 1, 1 );
648 			if( tmp.imaginary != Real(0.0) )
649 				THROW_ERROR( ErrorType_General, _("_3d_set_prop: 'Look at's coordinates must be real values.") );
650 			x = float(tmp.real);
651 
652 			tmp = matr.get_complex( 1, 2 );
653 			if( tmp.imaginary != Real(0.0) )
654 				THROW_ERROR( ErrorType_General, _("_3d_set_prop: \"Look at\"s coordinates must be real values.") );
655 			y = float(tmp.real);
656 
657 			tmp = matr.get_complex( 1, 3 );
658 			if( tmp.imaginary != Real(0.0) )
659 				THROW_ERROR( ErrorType_General, _("_3d_set_prop: \"Look at\"s coordinates must be real values.") );
660 			z = float(tmp.real);
661 
662 			_3d_prop.look_at.x = x;
663 			_3d_prop.look_at.y = z;
664 			_3d_prop.look_at.z = -y;
665 
666 			i++;
667 		}
668 		else
669 		{
670 			utf8_string str2( _("_3d_set_prop: Unknown property: ") );
671 			str2.append( *str );
672 			THROW_ERROR( ErrorType_General, str2.c_str() );
673 		}
674 	}
675 
676 	if( res != 0 )
677 		res->set_void();
678 }
679 
addon_3d_get_prop(math::Variable * res,math::Variable * left,const math::Variable * right,math::VariableList * private_varlist)680 void addon_3d_get_prop(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
681                        throw(error_obj)
682 {
683 	Matrix matr_res;
684 	Complex tmp;
685 	const utf8_string *str;
686 
687 	if( left != 0  ||  right == 0 )
688 		THROW_ERROR( ErrorType_General, "Internal error: _3d_get_prop wasn't called the right way" );
689 
690 	if( res == 0 )
691 		return;
692 
693 	if( right->get_type() != math::VariableType_String )
694 	{
695 		char tmp_err[ERROR_OBJ_MSG_LEN];
696 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Data type is unsupported by '%s'"), "_3d_get_prop" );
697 		THROW_ERROR( ErrorType_General, tmp_err );
698 	}
699 
700 	str = right->get_string();
701 
702 	if( *str == "Width" )
703 	{
704 		tmp.imaginary = Real(0.0);
705 		tmp.real = Real(_3d_prop.width);
706 		res->set_complex( tmp );
707 	}
708 	else if( *str == "Height" )
709 	{
710 		tmp.imaginary = Real(0.0);
711 		tmp.real = Real(_3d_prop.height);
712 		res->set_complex( tmp );
713 	}
714 	else if( *str == "Camera distance" )
715 	{
716 		tmp.imaginary = Real(0.0);
717 		tmp.real = Real(_3d_prop.camera_dist);
718 		res->set_complex( tmp );
719 	}
720 	else if( *str == "Look at" )
721 	{
722 		tmp.imaginary = Real(0.0);
723 		matr_res.set_size( 1, 3 );
724 		tmp.real = Real(_3d_prop.look_at.x);
725 		matr_res.set_complex( 1, 1, tmp );
726 		tmp.real = Real(_3d_prop.look_at.y);
727 		matr_res.set_complex( 1, 3, tmp );
728 		tmp.real = Real(-_3d_prop.look_at.z);
729 		matr_res.set_complex( 1, 2, tmp );
730 
731 		res->set_matrix( &matr_res );
732 	}
733 	else
734 	{
735 		char tmp_err[ERROR_OBJ_MSG_LEN];
736 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Unknown 3d property: %s"), str->c_str() );
737 		THROW_ERROR( ErrorType_General, tmp_err );
738 	}
739 }
740 
addon_3d_add_plane(math::Variable * res,math::Variable * left,const math::Variable * right,math::VariableList * private_varlist)741 void addon_3d_add_plane(math::Variable *res, math::Variable *left, const math::Variable *right, math::VariableList *private_varlist)
742                         throw(error_obj)
743 {
744 	Matrix x_matr, y_matr, z_matr;
745 	floatx min=0.0, max=1.0;
746 	Vector<picture_h::colorrgb> colors;
747 	_3d_plane *plane;
748 	_3d_point *point;
749 
750 	if( left != 0  ||  right == 0 )
751 		THROW_ERROR( ErrorType_General, "Internal error: _3d_ad_plane wasn't called the right way" );
752 
753 
754 	try{  plane = new _3d_plane;  }
755 	catch(...) {  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
756 
757 	try
758 	{
759 		Matrix colormatrix;
760 		math::colorrgb_to_colormatrix( picture_h::color::BLUE, &colormatrix);
761 		utf8_string str("Solid");
762 
763 		argument_check( "_3d_add_plane", right,
764 		                math::type_matrix, &x_matr,
765 		                math::type_matrix, &y_matr,
766 		                math::type_matrix, &z_matr,
767 		                math::type_string_default, &str,
768 		                math::type_floatx_default, &min,
769 		                math::type_floatx_default, &max,
770 		                math::type_matrix_default, &colormatrix,
771 		                math::type_end );
772 
773 		if( str == "Solid" )
774 			plane->type = _3d_plane_solid;
775 		else if( str == "Semi-solid" )
776 			plane->type = _3d_plane_semi_solid;
777 		else if( str == "Wireframed" )
778 			plane->type = _3d_plane_wireframed;
779 		else
780 			THROW_ERROR( ErrorType_General, _("Unknown 3D plane-type") );
781 
782 		math::colormatrix_to_colorrgbs( colormatrix, colors );
783 
784 		if( min >= max )
785 			THROW_ERROR( ErrorType_General, _("_3d_add_plane: Max must be larger than min.") );
786 
787 		if( x_matr.get_width() <= 1  || x_matr.get_height() < 1 ||
788 		    y_matr.get_width() <= 1  || y_matr.get_height() < 1 ||
789 		    z_matr.get_width() <= 1  || z_matr.get_height() <= 1 )
790 		{
791 			if( res != 0 )
792 				res->set_void();
793 			return;
794 		}
795 
796 		if( x_matr.get_width() != z_matr.get_width()  ||
797 		    y_matr.get_width() != z_matr.get_height() )
798 			THROW_ERROR( ErrorType_General, _("_3d_add_plane: X, Y & Z's matrix sizes doesn't match.") );
799 
800 		plane->set_size( z_matr.get_width(), z_matr.get_height() );
801 
802 		for( uint32 x=0; x<plane->width; x++ )
803 		{
804 			for( uint32 y=0; y<plane->height; y++ )
805 			{
806 				point = &(plane->points[x + y*plane->width]);
807 
808 				if( x_matr.get_complex( x+1, 1 ).imaginary != Real(0.0)  ||
809 				    y_matr.get_complex( y+1, 1 ).imaginary != Real(0.0)  ||
810 				    z_matr.get_complex( x+1, y+1 ).imaginary != Real(0.0) )
811 					point->isValid = false;
812 				else
813 				{
814 					point->isValid = true;
815 
816 					// rearranging the axis...
817 					point->x = float(  x_matr.get_complex( x+1, 1 ).real );
818 					point->y = float(  z_matr.get_complex( x+1, y+1 ).real );
819 					point->z = float( -y_matr.get_complex( y+1, 1 ).real );
820 
821 					math::color_list_get_color( colors, min, max,
822 					                            z_matr.get_complex(x+1,y+1).real, &(point->color) );
823 				}
824 			}
825 		}
826 		_3d_prop.planes.append_this( plane );
827 	}
828 	catch(...)
829 	{
830 		delete plane;
831 		throw;
832 	}
833 
834 	if( res != 0 )
835 		res->set_void();
836 }
837 
838 #else
_3d_viewer_init(void)839 	void _3d_viewer_init(void) {} // dummy function when opengl support shouldn't get compiled
840 #endif // PLOT_3D
841