1 /* GENIUS Calculator
2  * Copyright (C) 2003-2021 Jiri (George) Lebl
3  *
4  * Author: Jiri (George) Lebl
5  *
6  * This file is part of Genius.
7  *
8  * Genius is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 #include "config.h"
22 
23 #include <string.h>
24 #include <gdk/gdk.h>
25 #include <gdk/gdkkeysyms.h>
26 #include <gtk/gtk.h>
27 #include <glib.h>
28 #include <math.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 
32 #include <vicious.h>
33 
34 #include "gtkextra.h"
35 
36 #include "calc.h"
37 #include "eval.h"
38 #include "util.h"
39 #include "dict.h"
40 #include "funclib.h"
41 #include "matrixw.h"
42 #include "compil.h"
43 #include "plugin.h"
44 #include "geloutput.h"
45 #include "mpwrap.h"
46 #include "matop.h"
47 
48 #include "gnome-genius.h"
49 
50 #include "graphing.h"
51 
52 #define MAXFUNC 10
53 
54 static GtkWidget *graph_window = NULL;
55 static GtkWidget *plot_canvas = NULL;
56 static GtkWidget *plot_dialog = NULL;
57 static GtkWidget *plot_notebook = NULL;
58 static GtkWidget *function_notebook = NULL;
59 static GtkWidget *errors_label_box = NULL;
60 
61 static GtkWidget *solver_dialog = NULL;
62 gboolean solver_dialog_slopefield = TRUE;
63 
64 static GtkWidget *plot_zoomout_item = NULL;
65 static GtkWidget *plot_zoomin_item = NULL;
66 static GtkWidget *plot_zoomfit_item = NULL;
67 static GtkWidget *plot_resetzoom_item = NULL;
68 static GtkWidget *plot_print_item = NULL;
69 static GtkWidget *plot_exportps_item = NULL;
70 static GtkWidget *plot_exporteps_item = NULL;
71 static GtkWidget *plot_exportpdf_item = NULL;
72 static GtkWidget *plot_exportpng_item = NULL;
73 
74 static GtkWidget *view_menu_item = NULL;
75 static GtkWidget *solver_menu_item = NULL;
76 
77 enum {
78 	MODE_LINEPLOT,
79 	MODE_LINEPLOT_PARAMETRIC,
80 	MODE_LINEPLOT_SLOPEFIELD,
81 	MODE_LINEPLOT_VECTORFIELD,
82 	MODE_SURFACE
83 } plot_mode = MODE_LINEPLOT;
84 
85 static GtkPlotCanvasChild *plot_child = NULL;
86 
87 /* Smallest plot window */
88 #define MINPLOT (1e-10)
89 
90 /*
91    plot (lineplot)
92  */
93 static GtkWidget *line_plot = NULL;
94 
95 static GtkPlotData *line_data[MAXFUNC] = { NULL };
96 static GtkPlotData *parametric_data = NULL;
97 static GtkPlotData *slopefield_data = NULL;
98 static GtkPlotData *vectorfield_data = NULL;
99 
100 static GtkWidget *plot_entries[MAXFUNC] = { NULL };
101 static GtkWidget *plot_y_labels[MAXFUNC] = { NULL };
102 static GtkWidget *plot_entries_status[MAXFUNC] = { NULL };
103 
104 static GtkWidget *lineplot_info_label = NULL;
105 
106 static GtkWidget *parametric_entry_x = NULL;
107 static GtkWidget *parametric_entry_y = NULL;
108 static GtkWidget *parametric_entry_z = NULL;
109 static GtkWidget *parametric_status_x = NULL;
110 static GtkWidget *parametric_status_y = NULL;
111 static GtkWidget *parametric_status_z = NULL;
112 
113 static GtkWidget *parametric_info_label = NULL;
114 static GtkWidget *parametric_x_label = NULL;
115 static GtkWidget *parametric_y_label = NULL;
116 static GtkWidget *parametric_z_label = NULL;
117 static GtkWidget *parametric_trange_label = NULL;
118 
119 static GtkWidget *slopefield_entry = NULL;
120 static GtkWidget *slopefield_status = NULL;
121 static GtkWidget *slopefield_info_label = NULL;
122 static GtkWidget *slopefield_der_label = NULL;
123 
124 static GtkWidget *solver_x_entry = NULL;
125 static GtkWidget *solver_y_entry = NULL;
126 
127 static GtkWidget *solver_xinc_entry = NULL;
128 static GtkWidget *solver_tinc_entry = NULL;
129 static GtkWidget *solver_tlen_entry = NULL;
130 
131 static double solver_x = 0.0;
132 static double solver_y = 0.0;
133 static double solver_xinc = 0.1;
134 static double solver_tinc = 0.1;
135 static double solver_tlen = 5.0;
136 
137 static GtkWidget *solver_xinc_label = NULL;
138 static GtkWidget *solver_tinc_label = NULL;
139 static GtkWidget *solver_tlen_label = NULL;
140 static GtkWidget *solver_x_pt_label = NULL;
141 static GtkWidget *solver_y_pt_label = NULL;
142 
143 static GtkWidget *vectorfield_entry_x = NULL;
144 static GtkWidget *vectorfield_status_x = NULL;
145 static GtkWidget *vectorfield_entry_y = NULL;
146 static GtkWidget *vectorfield_status_y = NULL;
147 
148 static GtkWidget *vectorfield_info_label = NULL;
149 static GtkWidget *vectorfield_xder_label = NULL;
150 static GtkWidget *vectorfield_yder_label = NULL;
151 
152 static GtkWidget *lineplot_x_range_label = NULL;
153 static GtkWidget *lineplot_y_range_label = NULL;
154 
155 static GtkWidget *surface_info_label = NULL;
156 static GtkWidget *surface_x_range_label = NULL;
157 static GtkWidget *surface_y_range_label = NULL;
158 
159 static char *lp_x_name = NULL;
160 static char *lp_y_name = NULL;
161 static char *lp_z_name = NULL;
162 static char *lp_t_name = NULL;
163 
164 static char *sp_x_name = NULL;
165 static char *sp_y_name = NULL;
166 static char *sp_z_name = NULL;
167 
168 static GSList *solutions_list = NULL;
169 
170 static GtkWidget *spinx1_entry = NULL;
171 static const char *spinx1_default = "-10";
172 static GtkWidget *spinx2_entry = NULL;
173 static const char *spinx2_default = "10";
174 static GtkWidget *spiny1_entry = NULL;
175 static const char *spiny1_default = "-10";
176 static GtkWidget *spiny2_entry = NULL;
177 static const char *spiny2_default = "10";
178 
179 static GtkWidget *spint1_entry = NULL;
180 static const char *spint1_default = "0.0";
181 static GtkWidget *spint2_entry = NULL;
182 static const char *spint2_default = "1.0";
183 static GtkWidget *spintinc_entry = NULL;
184 static const char *spintinc_default = "0.01";
185 
186 static int spinSVtick = 20;
187 static int spinSHtick = 20;
188 
189 static int spinVVtick = 20;
190 static int spinVHtick = 20;
191 
192 static double defx1 = -10;
193 static double defx2 = 10;
194 static double defy1 = -10;
195 static double defy2 = 10;
196 static double deft1 = 0.0;
197 static double deft2 = 1.0;
198 static double deftinc = 0.01;
199 
200 static gboolean lineplot_draw_legends = TRUE;
201 static gboolean lineplot_draw_legends_cb = TRUE;
202 static gboolean lineplot_draw_labels = TRUE;
203 static gboolean lineplot_draw_labels_cb = TRUE;
204 static gboolean lineplot_fit_dependent_axis_cb = TRUE;
205 static gboolean vectorfield_normalize_arrow_length = FALSE;
206 static gboolean vectorfield_normalize_arrow_length_cb = FALSE;
207 static gboolean vectorfield_normalize_arrow_length_parameter = FALSE;
208 static gboolean surfaceplot_draw_legends = TRUE;
209 static gboolean surfaceplot_draw_legends_cb = TRUE;
210 static gboolean surfaceplot_fit_dependent_axis_cb = TRUE;
211 
212 static GtkWidget* surfaceplot_dep_axis_buttons = NULL;
213 static GtkWidget* lineplot_dep_axis_buttons = NULL;
214 static GtkWidget* lineplot_depx_axis_buttons = NULL;
215 static GtkWidget* lineplot_fit_dep_axis_checkbox = NULL;
216 
217 /* Replotting info */
218 static GelEFunc *plot_func[MAXFUNC] = { NULL };
219 static char *plot_func_name[MAXFUNC] = { NULL };
220 static GelEFunc *parametric_func_x = NULL;
221 static GelEFunc *parametric_func_y = NULL;
222 static GelEFunc *parametric_func_z = NULL;
223 static char *parametric_name = NULL;
224 static GelEFunc *slopefield_func = NULL;
225 static char *slopefield_name = NULL;
226 static GelEFunc *vectorfield_func_x = NULL;
227 static GelEFunc *vectorfield_func_y = NULL;
228 static char *vectorfield_name_x = NULL;
229 static char *vectorfield_name_y = NULL;
230 static double plotx1 = -10;
231 static double plotx2 = 10;
232 static double ploty1 = -10;
233 static double ploty2 = 10;
234 static double plott1 = 0.0;
235 static double plott2 = 1.0;
236 static double plottinc = 0.01;
237 
238 /* for the zoom reset */
239 static double reset_plotx1 = -10;
240 static double reset_plotx2 = 10;
241 static double reset_ploty1 = -10;
242 static double reset_ploty2 = 10;
243 
244 static int plot_sf_Vtick = 20;
245 static int plot_sf_Htick = 20;
246 
247 static int plot_vf_Vtick = 20;
248 static int plot_vf_Htick = 20;
249 
250 static int plotVtick = 20;
251 static int plotHtick = 20;
252 
253 static double *plot_points_x = NULL;
254 static double *plot_points_y = NULL;
255 static double *plot_points_dx = NULL;
256 static double *plot_points_dy = NULL;
257 
258 static int plot_points_num = 0;
259 
260 /*
261    Surface
262  */
263 static GtkWidget *surface_plot = NULL;
264 
265 static GtkPlotData *surface_data = NULL;
266 
267 static GtkWidget *surface_entry = NULL;
268 static GtkWidget *surface_entry_status = NULL;
269 static GtkWidget *surf_spinx1_entry = NULL;
270 static const char *surf_spinx1_default = "-10";
271 static GtkWidget *surf_spinx2_entry = NULL;
272 static const char *surf_spinx2_default = "10";
273 static GtkWidget *surf_spiny1_entry = NULL;
274 static const char *surf_spiny1_default = "-10";
275 static GtkWidget *surf_spiny2_entry = NULL;
276 static const char *surf_spiny2_default = "10";
277 static GtkWidget *surf_spinz1_entry = NULL;
278 static const char *surf_spinz1_default = "-10";
279 static GtkWidget *surf_spinz2_entry = NULL;
280 static const char *surf_spinz2_default = "10";
281 
282 static double surf_defx1 = -10;
283 static double surf_defx2 = 10;
284 static double surf_defy1 = -10;
285 static double surf_defy2 = 10;
286 static double surf_defz1 = -10;
287 static double surf_defz2 = 10;
288 
289 /* Replotting info */
290 static GelEFunc *surface_func = NULL;
291 static char *surface_func_name = NULL;
292 static double surfacex1 = -10;
293 static double surfacex2 = 10;
294 static double surfacey1 = -10;
295 static double surfacey2 = 10;
296 static double surfacez1 = -10;
297 static double surfacez2 = 10;
298 
299 /* for the zoom reset */
300 static double reset_surfacex1 = -10;
301 static double reset_surfacex2 = 10;
302 static double reset_surfacey1 = -10;
303 static double reset_surfacey2 = 10;
304 static double reset_surfacez1 = -10;
305 static double reset_surfacez2 = 10;
306 
307 /* surface data */
308 static double *surface_data_x = NULL;
309 static double *surface_data_y = NULL;
310 static double *surface_data_z = NULL;
311 static int surface_data_len = 0;
312 
313 
314 /* used for both */
315 static double plot_maxy = - G_MAXDOUBLE/2;
316 static double plot_miny = G_MAXDOUBLE/2;
317 static double plot_maxx = - G_MAXDOUBLE/2;
318 static double plot_minx = G_MAXDOUBLE/2;
319 static double plot_maxz = - G_MAXDOUBLE/2;
320 static double plot_minz = G_MAXDOUBLE/2;
321 
322 static GelCtx *plot_ctx = NULL;
323 static GelETree *plot_arg = NULL;
324 static GelETree *plot_arg2 = NULL;
325 static GelETree *plot_arg3 = NULL;
326 
327 static int plot_in_progress = 0;
328 static gboolean whack_window_after_plot = FALSE;
329 
330 static void plot_axis (void);
331 
332 /* lineplots */
333 static void plot_functions (gboolean do_window_present,
334 			    gboolean from_gui,
335 			    gboolean fit);
336 static void recompute_functions (gboolean fitting);
337 
338 /* surfaces */
339 static void plot_surface_functions (gboolean do_window_present, gboolean fit_function);
340 static void recompute_surface_function (gboolean fitting);
341 
342 static void plot_freeze (void);
343 static void plot_thaw (void);
344 
345 /* replot the slope/vector fields after zoom or other axis changing event */
346 static void replot_fields (void);
347 
348 static void slopefield_draw_solution (double x, double y, double dx, gboolean is_gui);
349 static void vectorfield_draw_solution (double x, double y, double dt, double tlen, gboolean is_gui);
350 
351 static void set_lineplot_labels (void);
352 static void set_solver_labels (void);
353 static void set_surface_labels (void);
354 
355 static gboolean get_number_from_entry (GtkWidget *entry, GtkWidget *win, double *num);
356 static GtkWidget *
357 create_range_boxes (const char *title, GtkWidget **titlew,
358 		    const char *def1, GtkWidget **w1,
359 		    const char *totitle, GtkWidget **totitlew,
360 		    const char *def2, GtkWidget **w2,
361 		    const char *bytitle,
362 		    const char *defby, GtkWidget **wb,
363 		    GCallback activate_callback);
364 
365 #define WIDTH 700
366 /* FIXME: for long graphs #define WIDTH (2*700) */
367 #define HEIGHT 500
368 #define ASPECT ((double)HEIGHT/(double)WIDTH)
369 
370 #define PROPORTION 0.85
371 #define PROPORTION3D 0.80
372 #define PROPORTION_OFFSETX 0.1
373 /* FIXME: for long graphs #define PROPORTION_OFFSETX 0.05 */
374 #define PROPORTION_OFFSETY 0.075
375 #define PROPORTION3D_OFFSET 0.12
376 
377 #include "funclibhelper.cP"
378 
379 enum {
380 	RESPONSE_STOP = 1,
381 	RESPONSE_PLOT,
382 	RESPONSE_CLEAR
383 };
384 
385 enum {
386 	EXPORT_PS,
387 	EXPORT_EPS,
388 	EXPORT_PDF,
389 	EXPORT_PNG
390 };
391 
392 
393 static gboolean
is_letter_or_underscore(char l)394 is_letter_or_underscore (char l)
395 {
396 	if ((l >= 'a' && l <= 'z') ||
397 	    (l >= 'A' && l <= 'Z') ||
398 	    l == '_')
399 		return TRUE;
400 	else
401 		return FALSE;
402 }
403 
404 static gboolean
is_letter_underscore_or_number(char l)405 is_letter_underscore_or_number (char l)
406 {
407 	if ((l >= 'a' && l <= 'z') ||
408 	    (l >= 'A' && l <= 'Z') ||
409 	    (l >= '0' && l <= '9') ||
410 	    l == '_')
411 		return TRUE;
412 	else
413 		return FALSE;
414 }
415 
416 static gboolean
is_identifier(const char * e)417 is_identifier (const char *e)
418 {
419 	int i;
420 	if ( ! is_letter_or_underscore (e[0]))
421 		return FALSE;
422 	for (i = 1; e[i] != '\0'; i++) {
423 		if ( ! is_letter_underscore_or_number (e[i]))
424 			return FALSE;
425 	}
426 	return TRUE;
427 }
428 
429 static void
init_var_names(void)430 init_var_names (void)
431 {
432 	/* this means we have already initialized */
433 	if (lp_x_name != NULL)
434 		return;
435 
436 	lp_x_name = g_strdup ("x");
437 	lp_y_name = g_strdup ("y");
438 	lp_z_name = g_strdup ("z");
439 	lp_t_name = g_strdup ("t");
440 
441 	sp_x_name = g_strdup ("x");
442 	sp_y_name = g_strdup ("y");
443 	sp_z_name = g_strdup ("z");
444 }
445 
446 /* FIXME: This seems like a rather ugly hack, am I missing something about
447  * spinboxes or are they really this stupid */
448 static void
update_spinboxes(GtkWidget * w)449 update_spinboxes (GtkWidget *w)
450 {
451 	if (GTK_IS_SPIN_BUTTON (w))
452 		gtk_spin_button_update (GTK_SPIN_BUTTON (w));
453 	else if (GTK_IS_CONTAINER (w)) {
454 		GList *children, *li;
455 
456 		children = gtk_container_get_children (GTK_CONTAINER (w));
457 
458 		for (li = children; li != NULL; li = li->next) {
459 			update_spinboxes (li->data);
460 		}
461 
462 		g_list_free (children);
463 	}
464 }
465 
466 
467 static void
plot_window_setup(void)468 plot_window_setup (void)
469 {
470 	if (graph_window != NULL) {
471 		if (plot_in_progress == 0 &&
472 		    whack_window_after_plot) {
473 			gtk_widget_destroy (graph_window);
474 			whack_window_after_plot = FALSE;
475 			return;
476 		}
477 
478 		if (plot_in_progress)
479 			genius_setup_window_cursor (plot_canvas, GDK_WATCH);
480 		else
481 			genius_unsetup_window_cursor (plot_canvas);
482 
483 		gtk_dialog_set_response_sensitive (GTK_DIALOG (graph_window),
484 						   RESPONSE_STOP, plot_in_progress || gel_calc_running);
485 		gtk_widget_set_sensitive (plot_zoomout_item, ! plot_in_progress);
486 		gtk_widget_set_sensitive (plot_zoomin_item, ! plot_in_progress);
487 
488 		if (plot_mode == MODE_LINEPLOT_SLOPEFIELD ||
489 		    plot_mode == MODE_LINEPLOT_VECTORFIELD) {
490 			gtk_widget_set_sensitive (plot_zoomfit_item, FALSE);
491 		} else {
492 			gtk_widget_set_sensitive (plot_zoomfit_item, ! plot_in_progress);
493 		}
494 
495 		gtk_widget_set_sensitive (plot_resetzoom_item, ! plot_in_progress);
496 		gtk_widget_set_sensitive (plot_print_item, ! plot_in_progress);
497 		gtk_widget_set_sensitive (plot_exportps_item, ! plot_in_progress);
498 		gtk_widget_set_sensitive (plot_exporteps_item, ! plot_in_progress);
499 		if (plot_exportpdf_item != NULL)
500 			gtk_widget_set_sensitive (plot_exportpdf_item, ! plot_in_progress);
501 		gtk_widget_set_sensitive (plot_exportpng_item, ! plot_in_progress);
502 		gtk_widget_set_sensitive (view_menu_item, ! plot_in_progress);
503 		gtk_widget_set_sensitive (solver_menu_item, ! plot_in_progress);
504 
505 		if (plot_mode == MODE_SURFACE) {
506 			gtk_widget_show (view_menu_item);
507 		} else {
508 			gtk_widget_hide (view_menu_item);
509 		}
510 
511 		if (plot_mode == MODE_LINEPLOT_SLOPEFIELD) {
512 			if ( ! solver_dialog_slopefield &&
513 			    solver_dialog != NULL) {
514 				gtk_widget_destroy (solver_dialog);
515 			}
516 			gtk_widget_show (solver_menu_item);
517 		} else if (plot_mode == MODE_LINEPLOT_VECTORFIELD) {
518 			if (solver_dialog_slopefield &&
519 			    solver_dialog != NULL) {
520 				gtk_widget_destroy (solver_dialog);
521 			}
522 			gtk_widget_show (solver_menu_item);
523 		} else {
524 			if (solver_dialog != NULL) {
525 				gtk_widget_destroy (solver_dialog);
526 			}
527 			gtk_widget_hide (solver_menu_item);
528 		}
529 	}
530 }
531 
532 static void
show_z_axis(gboolean do_show)533 show_z_axis (gboolean do_show)
534 {
535 	GtkPlotAxis *zx, *zy;
536 
537 	zx = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot),
538 				  GTK_PLOT_SIDE_ZX);
539 
540 	zy = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot),
541 				  GTK_PLOT_SIDE_ZY);
542 
543 	if (do_show) {
544 		gtk_plot_axis_show_labels (zy,
545 					   GTK_PLOT_LABEL_OUT);
546 		gtk_plot_axis_show_labels (zx,
547 					   GTK_PLOT_LABEL_OUT);
548 		gtk_plot_axis_show_ticks (zy,
549 					  GTK_PLOT_TICKS_OUT,
550 					  GTK_PLOT_TICKS_OUT);
551 		gtk_plot_axis_show_ticks (zx,
552 					  GTK_PLOT_TICKS_OUT,
553 					  GTK_PLOT_TICKS_OUT);
554 		gtk_plot_axis_show_title (zy);
555 		gtk_plot_axis_show_title (zx);
556 	} else {
557 		gtk_plot_axis_show_labels (zy,
558 					   GTK_PLOT_LABEL_NONE);
559 		gtk_plot_axis_show_labels (zx,
560 					   GTK_PLOT_LABEL_NONE);
561 		gtk_plot_axis_show_ticks (zy,
562 					  GTK_PLOT_TICKS_NONE,
563 					  GTK_PLOT_TICKS_NONE);
564 		gtk_plot_axis_show_ticks (zx,
565 					  GTK_PLOT_TICKS_NONE,
566 					  GTK_PLOT_TICKS_NONE);
567 		gtk_plot_axis_hide_title (zx);
568 		gtk_plot_axis_hide_title (zy);
569 	}
570 }
571 
572 static void
rotate_x_cb(GtkWidget * button,gpointer data)573 rotate_x_cb (GtkWidget *button, gpointer data)
574 {
575 	int rot = GPOINTER_TO_INT (data);
576 
577 	gtk_plot3d_rotate_x (GTK_PLOT3D (surface_plot), rot);
578 
579 	show_z_axis (TRUE);
580 
581 	gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
582 	gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
583 }
584 
585 static void
rotate_y_cb(GtkWidget * button,gpointer data)586 rotate_y_cb (GtkWidget *button, gpointer data)
587 {
588 	int rot = GPOINTER_TO_INT (data);
589 
590 	gtk_plot3d_rotate_y (GTK_PLOT3D (surface_plot), rot);
591 
592 	show_z_axis (TRUE);
593 
594 	gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
595 	gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
596 }
597 
598 static void
rotate_z_cb(GtkWidget * button,gpointer data)599 rotate_z_cb (GtkWidget *button, gpointer data)
600 {
601 	int rot = GPOINTER_TO_INT (data);
602 
603 	gtk_plot3d_rotate_z (GTK_PLOT3D (surface_plot), rot);
604 
605 	/* don't neccessarily show the z axis here, if we're in top
606 	   view this could be legitimate */
607 
608 	gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
609 	gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
610 }
611 
612 static void
rotate_cb(GtkWidget * item,gpointer data)613 rotate_cb (GtkWidget *item, gpointer data)
614 {
615 	GtkWidget *req = NULL;
616 	GtkWidget *hbox, *w, *b;
617         GtkSizeGroup *sg;
618 	char *tmp;
619 
620 	if (surface_plot == NULL)
621 		return;
622 
623 	req = gtk_dialog_new_with_buttons
624 		(_("Rotate") /* title */,
625 		 GTK_WINDOW (graph_window) /* parent */,
626 		 GTK_DIALOG_MODAL /* flags */,
627 		 _("_Close"),
628 		 GTK_RESPONSE_CLOSE,
629 		 NULL);
630 	gtk_dialog_set_default_response (GTK_DIALOG (req),
631 					 GTK_RESPONSE_CLOSE);
632 
633 	sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
634 
635 	/* X dir */
636 
637 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
638 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
639 			    hbox, TRUE, TRUE, 0);
640 
641 	tmp = g_strdup_printf (_("Rotate about %s axis: "),
642 			       sp_x_name);
643 	w = gtk_label_new (tmp);
644 	g_free (tmp);
645 
646 	gtk_size_group_add_widget (sg, w);
647 	gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
648 
649 	b = gtk_button_new ();
650 	gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
651 	gtk_container_add (GTK_CONTAINER (b),
652 	                   gtk_image_new_from_icon_name ("pan-start-symbolic",
653 	                                                 GTK_ICON_SIZE_BUTTON));
654 	g_signal_connect (G_OBJECT (b), "clicked",
655 			  G_CALLBACK (rotate_x_cb),
656 			  GINT_TO_POINTER (360-10));
657 	b = gtk_button_new ();
658 	gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
659 	gtk_container_add (GTK_CONTAINER (b),
660 			   gtk_image_new_from_icon_name ("pan-end-symbolic",
661 	                                                 GTK_ICON_SIZE_BUTTON));
662 	g_signal_connect (G_OBJECT (b), "clicked",
663 			  G_CALLBACK (rotate_x_cb),
664 			  GINT_TO_POINTER (10));
665 
666 	/* Y dir */
667 
668 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
669 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
670 			    hbox, TRUE, TRUE, 0);
671 
672 	tmp = g_strdup_printf (_("Rotate about %s axis: "),
673 			       sp_y_name);
674 	w = gtk_label_new (tmp);
675 	g_free (tmp);
676 
677 	gtk_size_group_add_widget (sg, w);
678 	gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
679 
680 	b = gtk_button_new ();
681 	gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
682 	gtk_container_add (GTK_CONTAINER (b),
683 	                   gtk_image_new_from_icon_name ("pan-start-symbolic",
684 	                                                 GTK_ICON_SIZE_BUTTON));
685 	g_signal_connect (G_OBJECT (b), "clicked",
686 			  G_CALLBACK (rotate_y_cb),
687 			  GINT_TO_POINTER (360-10));
688 	b = gtk_button_new ();
689 	gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
690 	gtk_container_add (GTK_CONTAINER (b),
691 	                   gtk_image_new_from_icon_name ("pan-end-symbolic",
692 	                                                 GTK_ICON_SIZE_BUTTON));
693 	g_signal_connect (G_OBJECT (b), "clicked",
694 			  G_CALLBACK (rotate_y_cb),
695 			  GINT_TO_POINTER (10));
696 
697 	/* Z dir */
698 
699 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
700 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
701 			    hbox, TRUE, TRUE, 0);
702 
703 	w = gtk_label_new (_("Rotate about dependent axis: "));
704 	gtk_size_group_add_widget (sg, w);
705 	gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
706 
707 	b = gtk_button_new ();
708 	gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
709 	gtk_container_add (GTK_CONTAINER (b),
710 	                   gtk_image_new_from_icon_name ("pan-start-symbolic",
711 	                                                 GTK_ICON_SIZE_BUTTON));
712 	g_signal_connect (G_OBJECT (b), "clicked",
713 			  G_CALLBACK (rotate_z_cb),
714 			  GINT_TO_POINTER (360-10));
715 	b = gtk_button_new ();
716 	gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
717 	gtk_container_add (GTK_CONTAINER (b),
718 	                   gtk_image_new_from_icon_name ("pan-end-symbolic",
719 	                                                 GTK_ICON_SIZE_BUTTON));
720 	g_signal_connect (G_OBJECT (b), "clicked",
721 			  G_CALLBACK (rotate_z_cb),
722 			  GINT_TO_POINTER (10));
723 
724 	g_signal_connect (G_OBJECT (req), "destroy",
725 			  G_CALLBACK (gtk_widget_destroyed),
726 			  &req);
727 
728 	gtk_widget_show_all (req);
729 
730 	gtk_dialog_run (GTK_DIALOG (req));
731 	gtk_widget_destroy (req);
732 }
733 
734 static int anim_timeout_id = 0;
735 
736 static gboolean
anim_timeout(gpointer data)737 anim_timeout (gpointer data)
738 {
739 	if (surface_plot == NULL || plot_canvas == NULL) {
740 		anim_timeout_id = 0;
741 		return FALSE;
742 	}
743 
744 	gtk_plot3d_rotate_z (GTK_PLOT3D (surface_plot), 360-2);
745 
746 	gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
747 	gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
748 
749 	/*while (gtk_events_pending ())
750 		gtk_main_iteration ();*/
751 
752 	return TRUE;
753 }
754 
755 static void
start_rotate_anim_cb(GtkWidget * item,gpointer data)756 start_rotate_anim_cb (GtkWidget *item, gpointer data)
757 {
758 	if (anim_timeout_id == 0) {
759 		anim_timeout_id = g_timeout_add_full (G_PRIORITY_LOW,
760 						      100, anim_timeout, NULL, NULL);
761 	}
762 }
763 
764 static void
stop_rotate_anim_cb(GtkWidget * item,gpointer data)765 stop_rotate_anim_cb (GtkWidget *item, gpointer data)
766 {
767 	if (anim_timeout_id != 0) {
768 		g_source_remove (anim_timeout_id);
769 		anim_timeout_id = 0;
770 	}
771 }
772 
773 static void
reset_angles_cb(GtkWidget * button,gpointer data)774 reset_angles_cb (GtkWidget *button, gpointer data)
775 {
776 	if (surface_plot != NULL) {
777 		gtk_plot3d_reset_angles (GTK_PLOT3D (surface_plot));
778 		gtk_plot3d_rotate_x (GTK_PLOT3D (surface_plot), 60.0);
779 		gtk_plot3d_rotate_z (GTK_PLOT3D (surface_plot), 30.0);
780 
781 		show_z_axis (TRUE);
782 
783 		gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
784 		gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
785 	}
786 }
787 
788 static void
top_view_cb(GtkWidget * button,gpointer data)789 top_view_cb (GtkWidget *button, gpointer data)
790 {
791 	if (surface_plot != NULL) {
792 		gtk_plot3d_reset_angles (GTK_PLOT3D (surface_plot));
793 		/*gtk_plot3d_rotate_y (GTK_PLOT3D (surface_plot), 90.0);*/
794 
795 		show_z_axis (FALSE);
796 
797 		gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
798 		gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
799 	}
800 }
801 
802 static gboolean
graph_window_delete_event(GtkWidget * w,gpointer data)803 graph_window_delete_event (GtkWidget *w, gpointer data)
804 {
805 	if (plot_in_progress > 0) {
806 		gel_interrupted = TRUE;
807 		whack_window_after_plot = TRUE;
808 		return TRUE;
809 	} else {
810 		return FALSE;
811 	}
812 }
813 
814 
815 static void
graph_window_response(GtkWidget * w,int response,gpointer data)816 graph_window_response (GtkWidget *w, int response, gpointer data)
817 {
818 	if (response == GTK_RESPONSE_CLOSE ||
819 	    response == GTK_RESPONSE_DELETE_EVENT) {
820 		if (plot_in_progress > 0) {
821 			gel_interrupted = TRUE;
822 			whack_window_after_plot = TRUE;
823 		} else {
824 			gtk_widget_destroy (graph_window);
825 		}
826 	} else if (response == RESPONSE_STOP && (plot_in_progress > 0 || gel_calc_running > 0)) {
827 		gel_interrupted = TRUE;
828 	}
829 }
830 
831 static void
ok_dialog_entry_activate(GtkWidget * entry,gpointer data)832 ok_dialog_entry_activate (GtkWidget *entry, gpointer data)
833 {
834 	gtk_dialog_response (GTK_DIALOG (data), GTK_RESPONSE_OK);
835 }
836 
837 
838 static void
plot_print_cb(void)839 plot_print_cb (void)
840 {
841 	gboolean ret;
842 	GtkWidget *req = NULL;
843 	GtkWidget *hbox, *w, *cmd;
844 	int fd;
845 	char *tmpfile;
846 	static char *last_cmd = NULL;
847 
848 	if (last_cmd == NULL)
849 		last_cmd = g_strdup ("lpr");
850 
851 	req = gtk_dialog_new_with_buttons
852 		(_("Print") /* title */,
853 		 GTK_WINDOW (graph_window) /* parent */,
854 		 GTK_DIALOG_MODAL /* flags */,
855 		 _("_Cancel"),
856 		 GTK_RESPONSE_CANCEL,
857 		 _("_Print"),
858 		 GTK_RESPONSE_OK,
859 		 NULL);
860 	gtk_dialog_set_default_response (GTK_DIALOG (req),
861 					 GTK_RESPONSE_OK);
862 
863 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
864 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
865 			    hbox, TRUE, TRUE, 0);
866 
867 	w = gtk_label_new (_("Print command: "));
868 	gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
869 
870 	cmd = gtk_entry_new ();
871 	g_signal_connect (G_OBJECT (cmd), "activate",
872 			  G_CALLBACK (ok_dialog_entry_activate), req);
873 	gtk_box_pack_start (GTK_BOX (hbox), cmd, TRUE, TRUE, 0);
874 
875 	gtk_entry_set_text (GTK_ENTRY (cmd), last_cmd);
876 
877 	gtk_widget_show_all (hbox);
878 
879 	g_signal_connect (G_OBJECT (req), "destroy",
880 			  G_CALLBACK (gtk_widget_destroyed),
881 			  &req);
882 
883 	if (gtk_dialog_run (GTK_DIALOG (req)) != GTK_RESPONSE_OK) {
884 		gtk_widget_destroy (req);
885 		return;
886 	}
887 
888 	g_free (last_cmd);
889 	last_cmd = g_strdup (gtk_entry_get_text (GTK_ENTRY (cmd)));
890 
891 	gtk_widget_destroy (req);
892 
893 	tmpfile = g_build_filename (g_get_tmp_dir (), "genius-ps-XXXXXX", NULL);
894 	fd = g_mkstemp (tmpfile);
895 	if (fd < 0) {
896 		g_free (tmpfile);
897 		genius_display_error (graph_window, _("Cannot open temporary file, cannot print."));
898 		return;
899 	}
900 
901 	plot_in_progress ++;
902 	gel_calc_running ++;
903 	plot_window_setup ();
904 
905 	/* Letter will fit on A4, so just currently do that */
906 	if (plot_canvas != NULL)
907 		ret = gtk_plot_canvas_export_ps (GTK_PLOT_CANVAS (plot_canvas),
908 						 tmpfile,
909 						 GTK_PLOT_LANDSCAPE,
910 						 FALSE /* epsflag */,
911 						 GTK_PLOT_LETTER);
912 	else
913 		ret = FALSE;
914 
915 	/* need this for some reason */
916 	if (plot_canvas != NULL) {
917 		gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
918 	}
919 
920 	if ( ! ret || gel_interrupted) {
921 		plot_in_progress --;
922 		gel_calc_running --;
923 		plot_window_setup ();
924 
925 		if ( ! gel_interrupted)
926 			genius_display_error (graph_window, _("Printing failed"));
927 		gel_interrupted = FALSE;
928 		close (fd);
929 		unlink (tmpfile);
930 		g_free (tmpfile);
931 		return;
932 	}
933 
934 	{
935 		char *cmdstring = g_strdup_printf ("cat %s | %s", tmpfile, last_cmd);
936 		errno = 0;
937 		if (system (cmdstring) < 0) {
938 			char *err = g_strdup_printf (
939 			_("Printing failed: %s"),
940 				      g_strerror (errno));
941 			genius_display_error (graph_window, err);
942 			g_free (err);
943 		}
944 
945 
946 		g_free (cmdstring);
947 
948 	}
949 
950 	plot_in_progress --;
951 	gel_calc_running --;
952 	plot_window_setup ();
953 
954 	close (fd);
955 	unlink (tmpfile);
956 	g_free (tmpfile);
957 }
958 
959 static char *last_export_dir = NULL;
960 
961 static void
really_export_cb(GtkFileChooser * fs,int response,gpointer data)962 really_export_cb (GtkFileChooser *fs, int response, gpointer data)
963 {
964 	char *s;
965 	char *base;
966 	gboolean ret;
967 	int export_type;
968 	char *tmpfile = NULL;
969 	char *file_to_write = NULL;
970 	int fd = -1;
971 	gboolean run_epsi = FALSE;
972 	GtkWidget *w;
973 
974 	export_type = GPOINTER_TO_INT (data);
975 
976 	if (response != GTK_RESPONSE_OK) {
977 		gtk_widget_destroy (GTK_WIDGET (fs));
978 		/* FIXME: don't want to deal with modality issues right now */
979 		gtk_widget_set_sensitive (graph_window, TRUE);
980 		return;
981 	}
982 
983 	/* run epsi checkbox */
984 	if (export_type == EXPORT_EPS) {
985 		w = gtk_file_chooser_get_extra_widget (GTK_FILE_CHOOSER (fs));
986 		if (w != NULL &&
987 		    gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w))) {
988 			run_epsi = TRUE;
989 		}
990 	}
991 
992 	s = g_strdup (gtk_file_chooser_get_filename (fs));
993 
994 	if (s == NULL)
995 		return;
996 	base = g_path_get_basename (s);
997 	if (base != NULL && base[0] != '\0' &&
998 	    strchr (base, '.') == NULL) {
999 		char *n;
1000 		if (export_type == EXPORT_EPS)
1001 			n = g_strconcat (s, ".eps", NULL);
1002 		else if (export_type == EXPORT_PDF)
1003 			n = g_strconcat (s, ".pdf", NULL);
1004 		else
1005 			n = g_strconcat (s, ".ps", NULL);
1006 		g_free (s);
1007 		s = n;
1008 	}
1009 	g_free (base);
1010 
1011 	if (access (s, F_OK) == 0 &&
1012 	    ! genius_ask_question (GTK_WIDGET (fs),
1013 				   _("File already exists.  Overwrite it?"))) {
1014 		g_free (s);
1015 		return;
1016 	}
1017 
1018 	g_free (last_export_dir);
1019 	last_export_dir = gtk_file_chooser_get_current_folder (fs);
1020 
1021 	gtk_widget_destroy (GTK_WIDGET (fs));
1022 	/* FIXME: don't want to deal with modality issues right now */
1023 	gtk_widget_set_sensitive (graph_window, TRUE);
1024 
1025 	file_to_write = s;
1026 	if (export_type == EXPORT_EPS && run_epsi && ve_is_prog_in_path ("ps2epsi")) {
1027 		tmpfile = g_build_filename (g_get_tmp_dir (), "genius-ps-XXXXXX", NULL);
1028 		fd = g_mkstemp (tmpfile);
1029 		/* FIXME: tell about errors ?*/
1030 		if (fd >= 0) {
1031 			file_to_write = tmpfile;
1032 		}
1033 	} else if (export_type == EXPORT_PDF) {
1034 		tmpfile = g_build_filename (g_get_tmp_dir (), "genius-ps-XXXXXX", NULL);
1035 		fd = g_mkstemp (tmpfile);
1036 		/* FIXME: tell about errors ?*/
1037 		if (fd >= 0) {
1038 			file_to_write = tmpfile;
1039 		}
1040 	}
1041 
1042 	plot_in_progress ++;
1043 	gel_calc_running ++;
1044 	plot_window_setup ();
1045 
1046 	/* FIXME: There should be some options about size and stuff */
1047 	if (plot_canvas != NULL)
1048 		ret = gtk_plot_canvas_export_ps_with_size
1049 			(GTK_PLOT_CANVAS (plot_canvas),
1050 			 file_to_write,
1051 			 GTK_PLOT_PORTRAIT,
1052 			 (export_type == EXPORT_EPS ||
1053 			  export_type == EXPORT_PDF) /* epsflag */,
1054 			 GTK_PLOT_PSPOINTS,
1055 			 400, ASPECT * 400);
1056 	else
1057 		ret = FALSE;
1058 
1059 	/* need this for some reason */
1060 	if (plot_canvas != NULL) {
1061 		gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
1062 	}
1063 
1064 	/* If we used a temporary file, now use ps2epsi or ps2pdf */
1065 	if (fd >= 0 && run_epsi) {
1066 		int status;
1067 		char *qs = g_shell_quote (s);
1068 		char *cmd = g_strdup_printf ("ps2epsi %s %s", tmpfile, qs);
1069 		if ( ! g_spawn_command_line_sync  (cmd,
1070 						   NULL /*stdout*/,
1071 						   NULL /*stderr*/,
1072 						   &status,
1073 						   NULL /* error */)) {
1074 			status = -1;
1075 		}
1076 		close (fd);
1077 		if (status == 0) {
1078 			unlink (tmpfile);
1079 		} else {
1080 			/* EEK, couldn't run ps2epsi for some reason */
1081 			rename (tmpfile, s);
1082 		}
1083 		g_free (cmd);
1084 		g_free (qs);
1085 	} else if (fd >= 0 && export_type == EXPORT_PDF) {
1086 		int status;
1087 		char *qs = g_shell_quote (s);
1088 		char *cmd = g_strdup_printf ("ps2pdf -dEPSCrop -dPDFSETTINGS=/prepress %s %s", tmpfile, qs);
1089 		if ( ! g_spawn_command_line_sync  (cmd,
1090 						   NULL /*stdout*/,
1091 						   NULL /*stderr*/,
1092 						   &status,
1093 						   NULL /* error */)) {
1094 			status = -1;
1095 		}
1096 		close (fd);
1097 		if (status == 0) {
1098 			unlink (tmpfile);
1099 		} else {
1100 			/* EEK, couldn't run ps2pdf for some reason */
1101 			rename (tmpfile, s);
1102 		}
1103 		g_free (cmd);
1104 		g_free (qs);
1105 	}
1106 
1107 	plot_in_progress --;
1108 	gel_calc_running --;
1109 	plot_window_setup ();
1110 
1111 	if ( ! ret || gel_interrupted) {
1112 		if ( ! gel_interrupted)
1113 			genius_display_error (graph_window, _("Export failed"));
1114 		g_free (s);
1115 		if (tmpfile != NULL)
1116 			g_free (tmpfile);
1117 		gel_interrupted = FALSE;
1118 		return;
1119 	}
1120 
1121 	g_free (s);
1122 	if (tmpfile != NULL)
1123 		g_free (tmpfile);
1124 }
1125 
1126 static void
really_export_png_cb(GtkFileChooser * fs,int response,gpointer data)1127 really_export_png_cb (GtkFileChooser *fs, int response, gpointer data)
1128 {
1129 	char *s;
1130 	char *base;
1131 	GdkPixbuf *pix;
1132 
1133 	if (response != GTK_RESPONSE_OK) {
1134 		gtk_widget_destroy (GTK_WIDGET (fs));
1135 		/* FIXME: don't want to deal with modality issues right now */
1136 		gtk_widget_set_sensitive (graph_window, TRUE);
1137 		return;
1138 	}
1139 
1140 	s = g_strdup (gtk_file_chooser_get_filename (fs));
1141 	if (s == NULL)
1142 		return;
1143 	base = g_path_get_basename (s);
1144 	if (base != NULL && base[0] != '\0' &&
1145 	    strchr (base, '.') == NULL) {
1146 		char *n = g_strconcat (s, ".png", NULL);
1147 		g_free (s);
1148 		s = n;
1149 	}
1150 	g_free (base);
1151 
1152 	if (access (s, F_OK) == 0 &&
1153 	    ! genius_ask_question (GTK_WIDGET (fs),
1154 				   _("File already exists.  Overwrite it?"))) {
1155 		g_free (s);
1156 		return;
1157 	}
1158 
1159 	g_free (last_export_dir);
1160 	last_export_dir = gtk_file_chooser_get_current_folder (fs);
1161 
1162 	gtk_widget_destroy (GTK_WIDGET (fs));
1163 	/* FIXME: don't want to deal with modality issues right now */
1164 	gtk_widget_set_sensitive (graph_window, TRUE);
1165 
1166 
1167 	/* sanity */
1168 	if (GTK_PLOT_CANVAS (plot_canvas)->pixmap == NULL) {
1169 		genius_display_error (graph_window, _("Export failed"));
1170 		return;
1171 	}
1172 
1173 	pix = gdk_pixbuf_get_from_surface
1174 		(GTK_PLOT_CANVAS (plot_canvas)->pixmap,
1175 		 0 /* src x */, 0 /* src y */,
1176 		 GTK_PLOT_CANVAS (plot_canvas)->pixmap_width,
1177 		 GTK_PLOT_CANVAS (plot_canvas)->pixmap_height);
1178 
1179 	if (pix == NULL ||
1180 	    ! gdk_pixbuf_save (pix, s, "png", NULL /* error */, NULL)) {
1181 		if (pix != NULL)
1182 			g_object_unref (G_OBJECT (pix));
1183 		g_free (s);
1184 		genius_display_error (graph_window, _("Export failed"));
1185 		return;
1186 	}
1187 
1188 	g_object_unref (G_OBJECT (pix));
1189 	g_free (s);
1190 }
1191 
1192 static void
do_export_cb(int export_type)1193 do_export_cb (int export_type)
1194 {
1195 	static GtkWidget *fs = NULL;
1196 	GtkFileFilter *filter_ps;
1197 	GtkFileFilter *filter_all;
1198 	const char *title;
1199 
1200 	if (fs != NULL) {
1201 		gtk_window_present (GTK_WINDOW (fs));
1202 		return;
1203 	}
1204 
1205 	/* FIXME: don't want to deal with modality issues right now */
1206 	gtk_widget_set_sensitive (graph_window, FALSE);
1207 
1208 	if (export_type == EXPORT_EPS)
1209 		title = _("Export encapsulated postscript");
1210 	else if (export_type == EXPORT_PS)
1211 		title = _("Export postscript");
1212 	else if (export_type == EXPORT_PDF)
1213 		title = _("Export PDF");
1214 	else if (export_type == EXPORT_PNG)
1215 		title = _("Export PNG");
1216 	else
1217 		/* should never happen */
1218 		title = "Export ???";
1219 
1220 	if (export_type == EXPORT_PDF &&
1221 	    ! ve_is_prog_in_path ("ps2pdf")) {
1222 		genius_display_error (graph_window, _("Missing ps2pdf command, perhaps ghostscript is not installed."));
1223 		return;
1224 	}
1225 
1226 	fs = gtk_file_chooser_dialog_new (title,
1227 					  GTK_WINDOW (graph_window),
1228 					  GTK_FILE_CHOOSER_ACTION_SAVE,
1229 					  _("_Cancel"), GTK_RESPONSE_CANCEL,
1230 					  _("_Save"), GTK_RESPONSE_OK,
1231 					  NULL);
1232 	gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fs), TRUE);
1233 
1234 
1235 	filter_ps = gtk_file_filter_new ();
1236 	if (export_type == EXPORT_EPS) {
1237 		gtk_file_filter_set_name (filter_ps, _("EPS files"));
1238 		gtk_file_filter_add_pattern (filter_ps, "*.eps");
1239 		gtk_file_filter_add_pattern (filter_ps, "*.EPS");
1240 	} else if (export_type == EXPORT_PS) {
1241 		gtk_file_filter_set_name (filter_ps, _("PS files"));
1242 		gtk_file_filter_add_pattern (filter_ps, "*.ps");
1243 		gtk_file_filter_add_pattern (filter_ps, "*.PS");
1244 	} else if (export_type == EXPORT_PDF) {
1245 		gtk_file_filter_set_name (filter_ps, _("PDF files"));
1246 		gtk_file_filter_add_pattern (filter_ps, "*.pdf");
1247 		gtk_file_filter_add_pattern (filter_ps, "*.PDF");
1248 	} else if (export_type == EXPORT_PNG) {
1249 		gtk_file_filter_set_name (filter_ps, _("PNG files"));
1250 		gtk_file_filter_add_pattern (filter_ps, "*.png");
1251 		gtk_file_filter_add_pattern (filter_ps, "*.PNG");
1252 	}
1253 
1254 	filter_all = gtk_file_filter_new ();
1255 	gtk_file_filter_set_name (filter_all, _("All files"));
1256 	gtk_file_filter_add_pattern (filter_all, "*");
1257 
1258 	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fs), filter_ps);
1259 	gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fs), filter_all);
1260 	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (fs), filter_ps);
1261 
1262 	if (export_type == EXPORT_EPS && ve_is_prog_in_path ("ps2epsi")) {
1263 		GtkWidget *w;
1264 		w = gtk_check_button_new_with_label (_("Generate preview in EPS file (with ps2epsi)"));
1265 		gtk_widget_show (w);
1266 		gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (fs), w);
1267 	}
1268 
1269 
1270 	g_signal_connect (G_OBJECT (fs), "destroy",
1271 			  G_CALLBACK (gtk_widget_destroyed), &fs);
1272 	if (export_type == EXPORT_EPS ||
1273 	    export_type == EXPORT_PS ||
1274 	    export_type == EXPORT_PDF) {
1275 		g_signal_connect (G_OBJECT (fs), "response",
1276 				  G_CALLBACK (really_export_cb),
1277 				  GINT_TO_POINTER (export_type));
1278 	} else if (export_type == EXPORT_PNG) {
1279 		g_signal_connect (G_OBJECT (fs), "response",
1280 				  G_CALLBACK (really_export_png_cb),
1281 				  NULL);
1282 	}
1283 
1284 	if (last_export_dir != NULL) {
1285 		gtk_file_chooser_set_current_folder
1286 			(GTK_FILE_CHOOSER (fs), last_export_dir);
1287 	} else {
1288 		char *s = g_get_current_dir ();
1289 		gtk_file_chooser_set_current_folder
1290 			(GTK_FILE_CHOOSER (fs), s);
1291 		g_free (s);
1292 	}
1293 
1294 	gtk_widget_show (fs);
1295 }
1296 
1297 static void
plot_exportps_cb(void)1298 plot_exportps_cb (void)
1299 {
1300 	do_export_cb (EXPORT_PS);
1301 }
1302 
1303 static void
plot_exporteps_cb(void)1304 plot_exporteps_cb (void)
1305 {
1306 	do_export_cb (EXPORT_EPS);
1307 }
1308 
1309 static void
plot_exportpdf_cb(void)1310 plot_exportpdf_cb (void)
1311 {
1312 	do_export_cb (EXPORT_PDF);
1313 }
1314 
1315 static void
plot_exportpng_cb(void)1316 plot_exportpng_cb (void)
1317 {
1318 	do_export_cb (EXPORT_PNG);
1319 }
1320 
1321 static void
plot_zoomin_cb(void)1322 plot_zoomin_cb (void)
1323 {
1324 	if (plot_in_progress == 0) {
1325 		double len;
1326 		long last_errnum = total_errors;
1327 
1328 		if (plot_mode == MODE_LINEPLOT ||
1329 		    plot_mode == MODE_LINEPLOT_PARAMETRIC ||
1330 		    plot_mode == MODE_LINEPLOT_SLOPEFIELD ||
1331 		    plot_mode == MODE_LINEPLOT_VECTORFIELD) {
1332 			len = plotx2 - plotx1;
1333 			plotx2 -= len/4.0;
1334 			plotx1 += len/4.0;
1335 
1336 			len = ploty2 - ploty1;
1337 			ploty2 -= len/4.0;
1338 			ploty1 += len/4.0;
1339 		} else if (plot_mode == MODE_SURFACE) {
1340 			len = surfacex2 - surfacex1;
1341 			surfacex2 -= len/4.0;
1342 			surfacex1 += len/4.0;
1343 
1344 			len = surfacey2 - surfacey1;
1345 			surfacey2 -= len/4.0;
1346 			surfacey1 += len/4.0;
1347 
1348 			len = surfacez2 - surfacez1;
1349 			surfacez2 -= len/4.0;
1350 			surfacez1 += len/4.0;
1351 		}
1352 
1353 		plot_axis ();
1354 
1355 		if (gel_interrupted)
1356 			gel_interrupted = FALSE;
1357 
1358 		gel_printout_infos_parent (graph_window);
1359 		if (last_errnum != total_errors &&
1360 		    ! genius_setup.error_box) {
1361 			gtk_widget_show (errors_label_box);
1362 		}
1363 	}
1364 }
1365 
1366 static void
plot_zoomout_cb(void)1367 plot_zoomout_cb (void)
1368 {
1369 	if (plot_in_progress == 0) {
1370 		double len;
1371 		long last_errnum = total_errors;
1372 
1373 		if (plot_mode == MODE_LINEPLOT ||
1374 		    plot_mode == MODE_LINEPLOT_PARAMETRIC ||
1375 		    plot_mode == MODE_LINEPLOT_SLOPEFIELD ||
1376 		    plot_mode == MODE_LINEPLOT_VECTORFIELD) {
1377 			len = plotx2 - plotx1;
1378 			plotx2 += len/2.0;
1379 			plotx1 -= len/2.0;
1380 
1381 			len = ploty2 - ploty1;
1382 			ploty2 += len/2.0;
1383 			ploty1 -= len/2.0;
1384 		} else if (plot_mode == MODE_SURFACE) {
1385 			len = surfacex2 - surfacex1;
1386 			surfacex2 += len/2.0;
1387 			surfacex1 -= len/2.0;
1388 
1389 			len = surfacey2 - surfacey1;
1390 			surfacey2 += len/2.0;
1391 			surfacey1 -= len/2.0;
1392 
1393 			len = surfacez2 - surfacez1;
1394 			surfacez2 += len/2.0;
1395 			surfacez1 -= len/2.0;
1396 		}
1397 
1398 		plot_axis ();
1399 
1400 		if (gel_interrupted)
1401 			gel_interrupted = FALSE;
1402 
1403 		gel_printout_infos_parent (graph_window);
1404 		if (last_errnum != total_errors &&
1405 		    ! genius_setup.error_box) {
1406 			gtk_widget_show (errors_label_box);
1407 		}
1408 	}
1409 }
1410 
1411 static void
plot_zoomfit_cb(void)1412 plot_zoomfit_cb (void)
1413 {
1414 	if (plot_mode == MODE_LINEPLOT_SLOPEFIELD ||
1415 	    plot_mode == MODE_LINEPLOT_VECTORFIELD) {
1416 		/* No zoom to fit during slopefield/vectorfield plots */
1417 		return;
1418 	}
1419 
1420 	if (plot_in_progress == 0) {
1421 		double size;
1422 		long last_errnum = total_errors;
1423 
1424 		if (plot_mode == MODE_LINEPLOT) {
1425 			size = plot_maxy - plot_miny;
1426 			if (size <= 0.0) {
1427 				/* Just don't do anything */
1428 				return;
1429 			}
1430 
1431 			ploty1 = plot_miny - size * 0.05;
1432 			ploty2 = plot_maxy + size * 0.05;
1433 
1434 			/* sanity */
1435 			if (ploty2 <= ploty1)
1436 				ploty2 = ploty1 + 0.1;
1437 
1438 			/* sanity */
1439 			if (ploty1 < -(G_MAXDOUBLE/2))
1440 				ploty1 = -(G_MAXDOUBLE/2);
1441 			if (ploty2 > (G_MAXDOUBLE/2))
1442 				ploty2 = (G_MAXDOUBLE/2);
1443 
1444 		} else if (plot_mode == MODE_LINEPLOT_PARAMETRIC) {
1445 			double sizex;
1446 			size = plot_maxy - plot_miny;
1447 			if (size <= 0.0) {
1448 				/* Just don't do anything */
1449 				return;
1450 			}
1451 			sizex = plot_maxx - plot_minx;
1452 			if (sizex <= 0.0) {
1453 				/* Just don't do anything */
1454 				return;
1455 			}
1456 
1457 			plotx1 = plot_minx - sizex * 0.05;
1458 			plotx2 = plot_maxx + sizex * 0.05;
1459 
1460 			/* sanity */
1461 			if (plotx2 <= plotx1)
1462 				plotx2 = plotx1 + 0.1;
1463 
1464 			/* sanity */
1465 			if (plotx1 < -(G_MAXDOUBLE/2))
1466 				plotx1 = -(G_MAXDOUBLE/2);
1467 			if (plotx2 > (G_MAXDOUBLE/2))
1468 				plotx2 = (G_MAXDOUBLE/2);
1469 
1470 			ploty1 = plot_miny - size * 0.05;
1471 			ploty2 = plot_maxy + size * 0.05;
1472 
1473 			/* sanity */
1474 			if (ploty2 <= ploty1)
1475 				ploty2 = ploty1 + 0.1;
1476 
1477 			/* sanity */
1478 			if (ploty1 < -(G_MAXDOUBLE/2))
1479 				ploty1 = -(G_MAXDOUBLE/2);
1480 			if (ploty2 > (G_MAXDOUBLE/2))
1481 				ploty2 = (G_MAXDOUBLE/2);
1482 
1483 		} else if (plot_mode == MODE_SURFACE) {
1484 			size = plot_maxz - plot_minz;
1485 			if (size <= 0.0) {
1486 				/* Just don't do anything */
1487 				return;
1488 			}
1489 			surfacez1 = plot_minz - size * 0.05;
1490 			surfacez2 = plot_maxz + size * 0.05;
1491 
1492 			/* sanity */
1493 			if (surfacez2 <= surfacez1)
1494 				surfacez2 = surfacez1 + 0.1;
1495 
1496 			/* sanity */
1497 			if (surfacez1 < -(G_MAXDOUBLE/2))
1498 				surfacez1 = -(G_MAXDOUBLE/2);
1499 			if (surfacez2 > (G_MAXDOUBLE/2))
1500 				surfacez2 = (G_MAXDOUBLE/2);
1501 		}
1502 
1503 		plot_axis ();
1504 
1505 		if (gel_interrupted)
1506 			gel_interrupted = FALSE;
1507 
1508 		gel_printout_infos_parent (graph_window);
1509 		if (last_errnum != total_errors &&
1510 		    ! genius_setup.error_box) {
1511 			gtk_widget_show (errors_label_box);
1512 		}
1513 	}
1514 }
1515 
1516 static void
plot_resetzoom_cb(void)1517 plot_resetzoom_cb (void)
1518 {
1519 	if (plot_in_progress == 0) {
1520 		long last_errnum = total_errors;
1521 
1522 		if (plot_mode == MODE_LINEPLOT ||
1523 		    plot_mode == MODE_LINEPLOT_PARAMETRIC ||
1524 		    plot_mode == MODE_LINEPLOT_SLOPEFIELD ||
1525 		    plot_mode == MODE_LINEPLOT_VECTORFIELD) {
1526 			plotx1 = reset_plotx1;
1527 			plotx2 = reset_plotx2;
1528 			ploty1 = reset_ploty1;
1529 			ploty2 = reset_ploty2;
1530 		} else if (plot_mode == MODE_SURFACE) {
1531 			surfacex1 = reset_surfacex1;
1532 			surfacex2 = reset_surfacex2;
1533 			surfacey1 = reset_surfacey1;
1534 			surfacey2 = reset_surfacey2;
1535 			surfacez1 = reset_surfacez1;
1536 			surfacez2 = reset_surfacez2;
1537 		}
1538 
1539 		plot_axis ();
1540 
1541 		if (gel_interrupted)
1542 			gel_interrupted = FALSE;
1543 
1544 		gel_printout_infos_parent (graph_window);
1545 		if (last_errnum != total_errors &&
1546 		    ! genius_setup.error_box) {
1547 			gtk_widget_show (errors_label_box);
1548 		}
1549 	}
1550 }
1551 
1552 static void
lineplot_move_graph(double horiz,double vert)1553 lineplot_move_graph (double horiz, double vert)
1554 {
1555 	if (plot_in_progress == 0 &&
1556 	    (plot_mode == MODE_LINEPLOT ||
1557 	     plot_mode == MODE_LINEPLOT_PARAMETRIC ||
1558 	     plot_mode == MODE_LINEPLOT_SLOPEFIELD ||
1559 	     plot_mode == MODE_LINEPLOT_VECTORFIELD)) {
1560 		double len;
1561 		long last_errnum = total_errors;
1562 
1563 		len = plotx2 - plotx1;
1564 		plotx1 = plotx1 + horiz * len;
1565 		plotx2 = plotx2 + horiz* len;
1566 
1567 		len = ploty2 - ploty1;
1568 		ploty1 = ploty1 + vert * len;
1569 		ploty2 = ploty2 + vert* len;
1570 
1571 		plot_axis ();
1572 
1573 		if (gel_interrupted)
1574 			gel_interrupted = FALSE;
1575 
1576 		gel_printout_infos_parent (graph_window);
1577 		if (last_errnum != total_errors &&
1578 		    ! genius_setup.error_box) {
1579 			gtk_widget_show (errors_label_box);
1580 		}
1581 	}
1582 }
1583 
1584 static guint dozoom_idle_id = 0;
1585 static gdouble dozoom_xmin;
1586 static gdouble dozoom_ymin;
1587 static gdouble dozoom_xmax;
1588 static gdouble dozoom_ymax;
1589 static gboolean dozoom_just_click;
1590 
1591 static gboolean wait_for_click = FALSE;
1592 static gdouble click_x = 0.0;
1593 static gdouble click_y = 0.0;
1594 
1595 static gboolean
dozoom_idle(gpointer data)1596 dozoom_idle (gpointer data)
1597 {
1598 	dozoom_idle_id = 0;
1599 
1600 	if (plot_in_progress == 0 && line_plot != NULL) {
1601 		double len;
1602 		long last_errnum = total_errors;
1603 
1604 		/* just click, so zoom in */
1605 		if (dozoom_just_click) {
1606 			len = plotx2 - plotx1;
1607 			plotx1 += len * dozoom_xmin - len / 4.0;
1608 			plotx2 = plotx1 + len / 2.0;
1609 
1610 			len = ploty2 - ploty1;
1611 			ploty1 += len * dozoom_ymin - len / 4.0;
1612 			ploty2 = ploty1 + len / 2.0;
1613 		} else {
1614 			len = plotx2 - plotx1;
1615 			plotx1 += len * dozoom_xmin;
1616 			plotx2 = plotx1 + (len * (dozoom_xmax-dozoom_xmin));
1617 
1618 			len = ploty2 - ploty1;
1619 			ploty1 += len * dozoom_ymin;
1620 			ploty2 = ploty1 + (len * (dozoom_ymax-dozoom_ymin));
1621 		}
1622 
1623 		/* sanity */
1624 		if (plotx2 - plotx1 < MINPLOT)
1625 			plotx2 = plotx1 + MINPLOT;
1626 		/* sanity */
1627 		if (ploty2 - ploty1 < MINPLOT)
1628 			ploty2 = ploty1 + MINPLOT;
1629 
1630 		plot_axis ();
1631 
1632 		if (gel_interrupted)
1633 			gel_interrupted = FALSE;
1634 
1635 		gel_printout_infos_parent (graph_window);
1636 		if (last_errnum != total_errors &&
1637 		    ! genius_setup.error_box) {
1638 			gtk_widget_show (errors_label_box);
1639 		}
1640 	}
1641 
1642 	return FALSE;
1643 }
1644 
1645 static void
plot_select_region(GtkPlotCanvas * canvas,gdouble xmin,gdouble ymin,gdouble xmax,gdouble ymax)1646 plot_select_region (GtkPlotCanvas *canvas,
1647 		    gdouble xmin,
1648 		    gdouble ymin,
1649 		    gdouble xmax,
1650 		    gdouble ymax)
1651 {
1652 	double len;
1653 	double px, py, pw, ph;
1654 
1655 	dozoom_just_click = FALSE;
1656 	if (fabs(xmin-xmax) < 0.001 ||
1657 	    fabs(ymin-ymax) < 0.001) {
1658 		dozoom_just_click = TRUE;
1659 	}
1660 
1661 	/* FIXME: evil because this is the selection thingie,
1662 	   hmmm, I dunno another way to do this though */
1663 
1664 	gtk_plot_get_position (GTK_PLOT (line_plot), &px, &py);
1665 	gtk_plot_get_size (GTK_PLOT (line_plot), &pw, &ph);
1666 
1667 	xmin -= px;
1668 	ymin -= py;
1669 	xmax -= px;
1670 	ymax -= py;
1671 
1672 	xmin /= pw;
1673 	ymin /= ph;
1674 	xmax /= pw;
1675 	ymax /= ph;
1676 
1677 	{ /* flip the y coordinate */
1678 		double oldymin = ymin;
1679 		ymin = 1.0 - ymax;
1680 		ymax = 1.0 - oldymin;
1681 	}
1682 
1683 	if (xmin > xmax) {
1684 		double tmp = xmax;
1685 		xmax = xmin;
1686 		xmin = tmp;
1687 	}
1688 
1689 	if (ymin > ymax) {
1690 		double tmp = ymax;
1691 		ymax = ymin;
1692 		ymin = tmp;
1693 	}
1694 
1695 	if (wait_for_click) {
1696 		double x, y;
1697 		len = plotx2 - plotx1;
1698 		x = plotx1 + len * xmin;
1699 		len = ploty2 - ploty1;
1700 		y = ploty1 + len * ymin;
1701 		click_x = x;
1702 		click_y = y;
1703 		wait_for_click = FALSE;
1704 		return;
1705 	}
1706 
1707 
1708 	if (plot_in_progress == 0 &&
1709 	    line_plot != NULL &&
1710 	    (plot_mode == MODE_LINEPLOT_SLOPEFIELD ||
1711 	     plot_mode == MODE_LINEPLOT_VECTORFIELD) &&
1712 	    solver_dialog != NULL) {
1713 		double x, y;
1714 		len = plotx2 - plotx1;
1715 		x = plotx1 + len * xmin;
1716 		len = ploty2 - ploty1;
1717 		y = ploty1 + len * ymin;
1718 
1719 		if (solver_x_entry != NULL) {
1720 			char *s = g_strdup_printf ("%g", x);
1721 			gtk_entry_set_text (GTK_ENTRY (solver_x_entry), s);
1722 			g_free (s);
1723 		}
1724 		if (solver_y_entry != NULL) {
1725 			char *s = g_strdup_printf ("%g", y);
1726 			gtk_entry_set_text (GTK_ENTRY (solver_y_entry), s);
1727 			g_free (s);
1728 		}
1729 
1730 		if (plot_mode == MODE_LINEPLOT_SLOPEFIELD)
1731 			slopefield_draw_solution (x, y, solver_xinc, TRUE /*is_gui*/);
1732 		else if (plot_mode == MODE_LINEPLOT_VECTORFIELD)
1733 			vectorfield_draw_solution (x, y, solver_tinc,
1734 						   solver_tlen, TRUE /*is_gui*/);
1735 
1736 		return;
1737 	}
1738 
1739 	/* only for line plots! */
1740 	if (plot_in_progress == 0 && line_plot != NULL) {
1741 		dozoom_xmin = xmin;
1742 		dozoom_xmax = xmax;
1743 		dozoom_ymin = ymin;
1744 		dozoom_ymax = ymax;
1745 		if (dozoom_idle_id == 0) {
1746 			dozoom_idle_id = g_idle_add (dozoom_idle, NULL);
1747 		}
1748 	}
1749 }
1750 
1751 static void
line_plot_move_about(void)1752 line_plot_move_about (void)
1753 {
1754 	if (line_plot == NULL)
1755 		return;
1756 
1757 	if ((plot_mode == MODE_LINEPLOT_PARAMETRIC ||
1758 	     plot_mode == MODE_LINEPLOT_SLOPEFIELD ||
1759 	     plot_mode == MODE_LINEPLOT_VECTORFIELD) &&
1760 	    lineplot_draw_legends) {
1761 		/* move plot out of the way if we are in parametric mode and
1762 		 * there is a legend */
1763 		gtk_plot_move (GTK_PLOT (line_plot),
1764 			       PROPORTION_OFFSETX,
1765 			       PROPORTION_OFFSETY);
1766 		gtk_plot_resize (GTK_PLOT (line_plot),
1767 				 1.0-2*PROPORTION_OFFSETX,
1768 				 1.0-2*PROPORTION_OFFSETY-0.05);
1769 
1770 		gtk_plot_legends_move (GTK_PLOT (line_plot),
1771 				       0.0,
1772 				       1.07);
1773 	} else {
1774 		gtk_plot_move (GTK_PLOT (line_plot),
1775 			       PROPORTION_OFFSETX,
1776 			       PROPORTION_OFFSETY);
1777 		gtk_plot_resize (GTK_PLOT (line_plot),
1778 				 1.0-2*PROPORTION_OFFSETX,
1779 				 1.0-2*PROPORTION_OFFSETY);
1780 		gtk_plot_legends_move (GTK_PLOT (line_plot), 0.80, 0.05);
1781 	}
1782 
1783 	if (lineplot_draw_legends)
1784 		gtk_plot_show_legends (GTK_PLOT (line_plot));
1785 	else
1786 		gtk_plot_hide_legends (GTK_PLOT (line_plot));
1787 }
1788 
1789 static void
add_line_plot(void)1790 add_line_plot (void)
1791 {
1792 	GtkPlotAxis *top, *right, *bottom, *left;
1793 
1794 	line_plot = gtk_plot_new_with_size (NULL, PROPORTION, PROPORTION);
1795 	gtk_widget_show (line_plot);
1796 	g_signal_connect (G_OBJECT (line_plot),
1797 			  "destroy",
1798 			  G_CALLBACK (gtk_widget_destroyed),
1799 			  &line_plot);
1800 
1801 	plot_child = gtk_plot_canvas_plot_new (GTK_PLOT (line_plot));
1802 	gtk_plot_canvas_put_child (GTK_PLOT_CANVAS (plot_canvas),
1803 				   plot_child,
1804 				   PROPORTION_OFFSETX,
1805 				   PROPORTION_OFFSETY,
1806 				   1.0-PROPORTION_OFFSETX,
1807 				   1.0-PROPORTION_OFFSETY);
1808 
1809 	GTK_PLOT_CANVAS_SET_FLAGS (GTK_PLOT_CANVAS (plot_canvas),
1810 				   GTK_PLOT_CANVAS_CAN_SELECT);
1811 
1812 	top = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_TOP);
1813 	right = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_RIGHT);
1814 	bottom = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_BOTTOM);
1815 	left = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_LEFT);
1816 
1817 	gtk_plot_axis_set_visible (top, TRUE);
1818 	gtk_plot_axis_set_visible (right, TRUE);
1819 	gtk_plot_grids_set_visible (GTK_PLOT (line_plot),
1820 				    FALSE, FALSE, FALSE, FALSE);
1821 	gtk_plot_axis_hide_title (top);
1822 	gtk_plot_axis_hide_title (right);
1823 	gtk_plot_axis_hide_title (left);
1824 	gtk_plot_axis_hide_title (bottom);
1825 	/*gtk_plot_axis_set_title (left, "Y");
1826 	gtk_plot_axis_set_title (bottom, "X");*/
1827 	gtk_plot_set_legends_border (GTK_PLOT (line_plot),
1828 				     GTK_PLOT_BORDER_LINE, 3);
1829 
1830 	gtk_plot_clip_data (GTK_PLOT (line_plot), TRUE);
1831 
1832 	line_plot_move_about ();
1833 }
1834 
1835 static void
surface_plot_move_about(void)1836 surface_plot_move_about (void)
1837 {
1838 	if (surface_plot == NULL)
1839 		return;
1840 
1841 	if (surfaceplot_draw_legends) {
1842 		gtk_plot_move (GTK_PLOT (surface_plot),
1843 			       0.0,
1844 			       PROPORTION3D_OFFSET);
1845 	} else {
1846 		gtk_plot_move (GTK_PLOT (surface_plot),
1847 			       PROPORTION3D_OFFSET,
1848 			       PROPORTION3D_OFFSET);
1849 	}
1850 }
1851 
1852 
1853 static void
add_surface_plot(void)1854 add_surface_plot (void)
1855 {
1856 	GtkPlotAxis *xy, *xz, *yx, *yz, *zx, *zy;
1857 	GtkPlotAxis *top, *left, *bottom;
1858 
1859 	surface_plot = gtk_plot3d_new (NULL);
1860 	gtk_widget_show (surface_plot);
1861 	g_signal_connect (G_OBJECT (surface_plot),
1862 			  "destroy",
1863 			  G_CALLBACK (gtk_widget_destroyed),
1864 			  &surface_plot);
1865 
1866 	plot_child = gtk_plot_canvas_plot_new (GTK_PLOT (surface_plot));
1867 	gtk_plot_canvas_put_child (GTK_PLOT_CANVAS (plot_canvas),
1868 				   plot_child,
1869 				   0.0,
1870 				   PROPORTION3D_OFFSET,
1871 				   0.8,
1872 				   1.0-PROPORTION3D_OFFSET);
1873 
1874 	GTK_PLOT_CANVAS_UNSET_FLAGS (GTK_PLOT_CANVAS (plot_canvas),
1875 				     GTK_PLOT_CANVAS_CAN_SELECT);
1876 
1877 	xy = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_XY);
1878 	xz = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_XZ);
1879 	yx = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_YX);
1880 	yz = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_YZ);
1881 	zx = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_ZX);
1882 	zy = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_ZY);
1883 
1884 	gtk_plot_axis_show_title (xy);
1885 	gtk_plot_axis_show_title (xz);
1886 	gtk_plot_axis_show_title (yx);
1887 	gtk_plot_axis_show_title (yz);
1888 	gtk_plot_axis_show_title (zx);
1889 	gtk_plot_axis_show_title (zy);
1890 
1891 	top = gtk_plot_get_axis (GTK_PLOT (surface_plot), GTK_PLOT_AXIS_TOP);
1892 	bottom = gtk_plot_get_axis (GTK_PLOT (surface_plot), GTK_PLOT_AXIS_BOTTOM);
1893 	left = gtk_plot_get_axis (GTK_PLOT (surface_plot), GTK_PLOT_AXIS_LEFT);
1894 
1895 	gtk_plot_axis_set_title (bottom, sp_x_name);
1896 	gtk_plot_axis_set_title (left, sp_y_name);
1897 	gtk_plot_axis_set_title (top, "");
1898 
1899 	gtk_plot_set_legends_border (GTK_PLOT (surface_plot),
1900 				     GTK_PLOT_BORDER_LINE, 3);
1901 	gtk_plot_legends_move (GTK_PLOT (surface_plot), 0.93, 0.05);
1902 
1903 	surface_plot_move_about ();
1904 }
1905 
1906 static void
clear_solutions(void)1907 clear_solutions (void)
1908 {
1909 	GSList *sl, *li;
1910 	sl = solutions_list;
1911 	solutions_list = NULL;
1912 
1913 	for (li = sl; li != NULL; li = li->next) {
1914 		GtkWidget *d = li->data;
1915 		li->data = NULL;
1916 		if (d != NULL)
1917 			gtk_widget_destroy (d);
1918 	}
1919 
1920 	g_slist_free (sl);
1921 
1922 	if (plot_canvas != NULL) {
1923 		gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
1924 		gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
1925 	}
1926 }
1927 
1928 static void
solver_dialog_response(GtkWidget * w,int response,gpointer data)1929 solver_dialog_response (GtkWidget *w, int response, gpointer data)
1930 {
1931 	if (response == RESPONSE_PLOT) {
1932 		if (plot_mode == MODE_LINEPLOT_SLOPEFIELD) {
1933 			if (get_number_from_entry (solver_x_entry, w, &solver_x) &&
1934 			    get_number_from_entry (solver_y_entry, w, &solver_y) &&
1935 			    get_number_from_entry (solver_xinc_entry, w, &solver_xinc)) {
1936 				slopefield_draw_solution (solver_x, solver_y, solver_xinc, TRUE /*is_gui*/);
1937 			}
1938 		} else {
1939 			if (get_number_from_entry (solver_x_entry, w, &solver_x) &&
1940 			    get_number_from_entry (solver_y_entry, w, &solver_y) &&
1941 			    get_number_from_entry (solver_tinc_entry, w, &solver_tinc) &&
1942 			    get_number_from_entry (solver_tlen_entry, w, &solver_tlen)) {
1943 				vectorfield_draw_solution (solver_x, solver_y, solver_tinc, solver_tlen, TRUE /*is_gui*/);
1944 			}
1945 		}
1946 	} else if (response == RESPONSE_CLEAR) {
1947 		clear_solutions ();
1948 	} else  {
1949 		gtk_widget_destroy (solver_dialog);
1950 	}
1951 }
1952 
1953 static void
solver_entry_activate(void)1954 solver_entry_activate (void)
1955 {
1956 	if (solver_dialog != NULL)
1957 		gtk_dialog_response (GTK_DIALOG (solver_dialog),
1958 				     RESPONSE_PLOT);
1959 }
1960 
1961 static void
solver_cb(GtkWidget * item,gpointer data)1962 solver_cb (GtkWidget *item, gpointer data)
1963 {
1964 	GtkWidget *box, *w;
1965 	char *def1, *def2;
1966 
1967 	if (line_plot == NULL ||
1968 	    (plot_mode != MODE_LINEPLOT_SLOPEFIELD &&
1969 	     plot_mode != MODE_LINEPLOT_VECTORFIELD))
1970 		return;
1971 
1972 	if (solver_dialog != NULL) {
1973 		gtk_window_present (GTK_WINDOW (solver_dialog));
1974 		return;
1975 	}
1976 
1977 	solver_dialog = gtk_dialog_new_with_buttons
1978 		(_("Solver") /* title */,
1979 		 GTK_WINDOW (graph_window) /* parent */,
1980 		 0 /* flags */,
1981 		 _("_Close"),
1982 		 GTK_RESPONSE_CLOSE,
1983 		 _("Clea_r solutions"),
1984 		 RESPONSE_CLEAR,
1985 		 _("_Plot solution"),
1986 		 RESPONSE_PLOT,
1987 		 NULL);
1988 	g_signal_connect (G_OBJECT (solver_dialog),
1989 			  "destroy",
1990 			  G_CALLBACK (gtk_widget_destroyed),
1991 			  &solver_dialog);
1992 	g_signal_connect (G_OBJECT (solver_dialog),
1993 			  "response",
1994 			  G_CALLBACK (solver_dialog_response),
1995 			  NULL);
1996 	gtk_dialog_set_default_response (GTK_DIALOG (solver_dialog),
1997 					 RESPONSE_PLOT);
1998 
1999 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
2000 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (solver_dialog))),
2001 			    box, TRUE, TRUE, 0);
2002 	gtk_container_set_border_width (GTK_CONTAINER (box), GENIUS_PAD);
2003 
2004 	w = gtk_label_new (_("Clicking on the graph window now will draw a "
2005 			     "solution according to the parameters set "
2006 			     "below, starting at the point clicked.  "
2007 			     "To be able to zoom by mouse again, close this "
2008 			     "window."));
2009 	gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
2010 	gtk_label_set_max_width_chars (GTK_LABEL (w), 30);
2011 	gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
2012 
2013 	if (plot_mode == MODE_LINEPLOT_SLOPEFIELD) {
2014 		solver_dialog_slopefield = TRUE;
2015 
2016 		solver_xinc = (plotx2-plotx1) / 100;
2017 
2018 		def1 = g_strdup_printf ("%g", solver_xinc);
2019 
2020 		w = create_range_boxes (_("X increment:"), &solver_xinc_label,
2021 					def1, &solver_xinc_entry,
2022 					NULL, NULL,
2023 					NULL, NULL,
2024 					NULL, NULL, NULL,
2025 					solver_entry_activate);
2026 		gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
2027 
2028 		g_free (def1);
2029 	} else {
2030 		solver_dialog_slopefield = FALSE;
2031 
2032 		solver_tinc = solver_tlen / 100;
2033 
2034 		def1 = g_strdup_printf ("%g", solver_tinc);
2035 		def2 = g_strdup_printf ("%g", solver_tlen);
2036 
2037 		w = create_range_boxes (_("T increment:"), &solver_tinc_label,
2038 					def1, &solver_tinc_entry,
2039 					_("T interval length:"), &solver_tlen_label,
2040 					def2, &solver_tlen_entry,
2041 					NULL, NULL, NULL,
2042 					solver_entry_activate);
2043 		gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
2044 
2045 		g_free (def1);
2046 		g_free (def2);
2047 	}
2048 
2049 	if (solver_x < plotx1 || solver_x > plotx2)
2050 		solver_x = plotx1 + (plotx2-plotx1)/2;
2051 	if (solver_y < ploty1 || solver_y > ploty2)
2052 		solver_y = ploty1 + (ploty2-ploty1)/2;
2053 
2054 	def1 = g_strdup_printf ("%g", solver_x);
2055 	def2 = g_strdup_printf ("%g", solver_y);
2056 
2057 	w = create_range_boxes (_("Point x:"), &solver_x_pt_label,
2058 				def1, &solver_x_entry,
2059 				_("y:"), &solver_y_pt_label,
2060 				def2, &solver_y_entry,
2061 				NULL, NULL, NULL,
2062 				solver_entry_activate);
2063 	gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
2064 	g_free (def1);
2065 	g_free (def2);
2066 
2067 	set_solver_labels ();
2068 
2069 	gtk_widget_show_all (solver_dialog);
2070 }
2071 
2072 static void
clear_solutions_cb(GtkWidget * item,gpointer data)2073 clear_solutions_cb (GtkWidget *item, gpointer data)
2074 {
2075 	clear_solutions ();
2076 }
2077 
2078 static gboolean
plot_canvas_key_press_event(GtkWidget * widget,GdkEventKey * event,gpointer user_data)2079 plot_canvas_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
2080 {
2081 	switch (event->keyval) {
2082 	case GDK_KEY_Up:
2083 		lineplot_move_graph (0.0, 0.1);
2084 		break;
2085 	case GDK_KEY_Down:
2086 		lineplot_move_graph (0.0, -0.1);
2087 		break;
2088 	case GDK_KEY_Left:
2089 		lineplot_move_graph (-0.1, 0.0);
2090 
2091 		if (plot_mode == MODE_SURFACE &&
2092 		    surface_plot != NULL &&
2093 		    plot_in_progress == 0) {
2094 			plot_in_progress++;
2095 			gtk_plot3d_rotate_z (GTK_PLOT3D (surface_plot), 360-10);
2096 
2097 			gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
2098 			gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
2099 
2100 			while (gtk_events_pending ())
2101 				gtk_main_iteration ();
2102 			plot_in_progress--;
2103 		}
2104 		break;
2105 	case GDK_KEY_Right:
2106 		lineplot_move_graph (0.1, 0.0);
2107 
2108 		if (plot_mode == MODE_SURFACE &&
2109 		    surface_plot != NULL &&
2110 		    plot_in_progress == 0) {
2111 			plot_in_progress++;
2112 			gtk_plot3d_rotate_z (GTK_PLOT3D (surface_plot), 10);
2113 
2114 			gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
2115 			gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
2116 
2117 			while (gtk_events_pending ())
2118 				gtk_main_iteration ();
2119 			plot_in_progress--;
2120 		}
2121 		break;
2122 	default: break;
2123 	}
2124 
2125 	return FALSE;
2126 }
2127 
2128 static void
graph_window_destroyed(GtkWidget * w,gpointer data)2129 graph_window_destroyed (GtkWidget *w, gpointer data)
2130 {
2131 	graph_window = NULL;
2132 	if (solver_dialog != NULL)
2133 		gtk_widget_destroy (solver_dialog);
2134 }
2135 
2136 static void
ensure_window(gboolean do_window_present)2137 ensure_window (gboolean do_window_present)
2138 {
2139 	GtkWidget *menu, *menubar, *item;
2140 	static gboolean first_time = TRUE;
2141 	static GtkAccelGroup *accel_group = NULL;
2142 
2143 	if (first_time) {
2144 		accel_group = gtk_accel_group_new ();
2145 
2146 		gtk_accel_map_add_entry ("<Genius-Plot>/Zoom/Zoom out",
2147 					 GDK_KEY_minus,
2148 					 GDK_CONTROL_MASK);
2149 		gtk_accel_map_add_entry ("<Genius-Plot>/Zoom/Zoom in",
2150 					 GDK_KEY_plus,
2151 					 GDK_CONTROL_MASK);
2152 		gtk_accel_map_add_entry ("<Genius-Plot>/Zoom/Fit dependent axis",
2153 					 GDK_KEY_f,
2154 					 GDK_CONTROL_MASK);
2155 		gtk_accel_map_add_entry ("<Genius-Plot>/Zoom/Reset to original zoom",
2156 					 GDK_KEY_r,
2157 					 GDK_CONTROL_MASK);
2158 		first_time = FALSE;
2159 	}
2160 
2161 	/* ensure we don't whack things, just paranoia */
2162 	whack_window_after_plot = FALSE;
2163 
2164 	if (graph_window != NULL) {
2165 		/* present is evil in that it takes focus away,
2166 		 * only want to do it on the GUI triggered actions. */
2167 		if (do_window_present)
2168 			gtk_window_present (GTK_WINDOW (graph_window));
2169 		else
2170 			gtk_widget_show (graph_window);
2171 
2172 		gtk_widget_hide (errors_label_box);
2173 		return;
2174 	}
2175 
2176 	graph_window = gtk_dialog_new_with_buttons
2177 		(_("Plot") /* title */,
2178 		 NULL /*GTK_WINDOW (genius_window)*/ /* parent */,
2179 		 0 /* flags */,
2180 		 _("_Stop"),
2181 		 RESPONSE_STOP,
2182 		 _("_Close"),
2183 		 GTK_RESPONSE_CLOSE,
2184 		 NULL);
2185 	gtk_window_set_type_hint (GTK_WINDOW (graph_window),
2186 				  GDK_WINDOW_TYPE_HINT_NORMAL);
2187 
2188 	gtk_window_add_accel_group (GTK_WINDOW (graph_window),
2189 				    accel_group);
2190 
2191 	g_signal_connect (G_OBJECT (graph_window),
2192 			  "destroy",
2193 			  G_CALLBACK (graph_window_destroyed), NULL);
2194 	g_signal_connect (G_OBJECT (graph_window),
2195 			  "delete_event",
2196 			  G_CALLBACK (graph_window_delete_event),
2197 			  NULL);
2198 	g_signal_connect (G_OBJECT (graph_window),
2199 			  "response",
2200 			  G_CALLBACK (graph_window_response),
2201 			  NULL);
2202 
2203 	menubar = gtk_menu_bar_new ();
2204 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (graph_window))),
2205 			    GTK_WIDGET (menubar), FALSE, TRUE, 0);
2206 
2207 	/*
2208 	 * Graph menu
2209 	 */
2210 	menu = gtk_menu_new ();
2211 	gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
2212 	item = gtk_menu_item_new_with_mnemonic (_("_Graph"));
2213 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
2214 	gtk_menu_shell_append (GTK_MENU_SHELL (menubar), item);
2215 
2216 	item = gtk_menu_item_new_with_mnemonic (_("_Print..."));
2217 	g_signal_connect (G_OBJECT (item), "activate",
2218 			  G_CALLBACK (plot_print_cb), NULL);
2219 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2220 	plot_print_item = item;
2221 
2222 	item = gtk_menu_item_new_with_mnemonic (_("_Export postscript..."));
2223 	g_signal_connect (G_OBJECT (item), "activate",
2224 			  G_CALLBACK (plot_exportps_cb), NULL);
2225 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2226 	plot_exportps_item = item;
2227 
2228 	item = gtk_menu_item_new_with_mnemonic (_("E_xport encapsulated postscript..."));
2229 	g_signal_connect (G_OBJECT (item), "activate",
2230 			  G_CALLBACK (plot_exporteps_cb), NULL);
2231 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2232 	plot_exporteps_item = item;
2233 
2234 	if (ve_is_prog_in_path ("ps2pdf")) {
2235 		item = gtk_menu_item_new_with_mnemonic (_("Export P_DF..."));
2236 		g_signal_connect (G_OBJECT (item), "activate",
2237 				  G_CALLBACK (plot_exportpdf_cb), NULL);
2238 		gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2239 		plot_exportpdf_item = item;
2240 	} else {
2241 		plot_exportpdf_item = NULL;
2242 	}
2243 
2244 	item = gtk_menu_item_new_with_mnemonic (_("Export P_NG..."));
2245 	g_signal_connect (G_OBJECT (item), "activate",
2246 			  G_CALLBACK (plot_exportpng_cb), NULL);
2247 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2248 	plot_exportpng_item = item;
2249 
2250 
2251 	/*
2252 	 * Zoom menu
2253 	 */
2254 	menu = gtk_menu_new ();
2255 	gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
2256 	item = gtk_menu_item_new_with_mnemonic (_("_Zoom"));
2257 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
2258 	gtk_menu_shell_append (GTK_MENU_SHELL (menubar), item);
2259 
2260 	item = gtk_menu_item_new_with_mnemonic (_("Zoom _out"));
2261 	g_signal_connect (G_OBJECT (item), "activate",
2262 			  G_CALLBACK (plot_zoomout_cb), NULL);
2263 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2264 	gtk_menu_item_set_accel_path (GTK_MENU_ITEM (item), "<Genius-Plot>/Zoom/Zoom out");
2265 	plot_zoomout_item = item;
2266 
2267 	item = gtk_menu_item_new_with_mnemonic (_("Zoom _in"));
2268 	g_signal_connect (G_OBJECT (item), "activate",
2269 			  G_CALLBACK (plot_zoomin_cb), NULL);
2270 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2271 	gtk_menu_item_set_accel_path (GTK_MENU_ITEM (item), "<Genius-Plot>/Zoom/Zoom in");
2272 	plot_zoomin_item = item;
2273 
2274 	item = gtk_menu_item_new_with_mnemonic (_("_Fit dependent axis"));
2275 	g_signal_connect (G_OBJECT (item), "activate",
2276 			  G_CALLBACK (plot_zoomfit_cb), NULL);
2277 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2278 	gtk_menu_item_set_accel_path (GTK_MENU_ITEM (item), "<Genius-Plot>/Zoom/Fit dependent axis");
2279 	plot_zoomfit_item = item;
2280 
2281 	item = gtk_menu_item_new_with_mnemonic (_("_Reset to original zoom"));
2282 	g_signal_connect (G_OBJECT (item), "activate",
2283 			  G_CALLBACK (plot_resetzoom_cb), NULL);
2284 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2285 	gtk_menu_item_set_accel_path (GTK_MENU_ITEM (item), "<Genius-Plot>/Zoom/Reset to original zoom");
2286 	plot_resetzoom_item = item;
2287 
2288 
2289 	/*
2290 	 * View menu
2291 	 */
2292 	menu = gtk_menu_new ();
2293 	gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
2294 	item = gtk_menu_item_new_with_mnemonic (_("_View"));
2295 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
2296 	gtk_menu_shell_append (GTK_MENU_SHELL (menubar), item);
2297 	view_menu_item = item;
2298 
2299 	item = gtk_menu_item_new_with_mnemonic (_("_Reset angles"));
2300 	g_signal_connect (G_OBJECT (item), "activate",
2301 			  G_CALLBACK (reset_angles_cb), NULL);
2302 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2303 
2304 	item = gtk_menu_item_new_with_mnemonic (_("_Top view"));
2305 	g_signal_connect (G_OBJECT (item), "activate",
2306 			  G_CALLBACK (top_view_cb), NULL);
2307 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2308 
2309 	item = gtk_menu_item_new_with_mnemonic (_("R_otate axis..."));
2310 	g_signal_connect (G_OBJECT (item), "activate",
2311 			  G_CALLBACK (rotate_cb), NULL);
2312 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2313 
2314 	item = gtk_menu_item_new_with_mnemonic (_("Start rotate _animation..."));
2315 	g_signal_connect (G_OBJECT (item), "activate",
2316 			  G_CALLBACK (start_rotate_anim_cb), NULL);
2317 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2318 
2319 	item = gtk_menu_item_new_with_mnemonic (_("Stop rotate a_nimation..."));
2320 	g_signal_connect (G_OBJECT (item), "activate",
2321 			  G_CALLBACK (stop_rotate_anim_cb), NULL);
2322 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2323 
2324 	/*
2325 	 * Solver menu
2326 	 */
2327 	menu = gtk_menu_new ();
2328 	gtk_menu_set_accel_group (GTK_MENU (menu), accel_group);
2329 	item = gtk_menu_item_new_with_mnemonic (_("_Solver"));
2330 	gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
2331 	gtk_menu_shell_append (GTK_MENU_SHELL (menubar), item);
2332 	solver_menu_item = item;
2333 
2334 	item = gtk_menu_item_new_with_mnemonic (_("_Solver..."));
2335 	g_signal_connect (G_OBJECT (item), "activate",
2336 			  G_CALLBACK (solver_cb), NULL);
2337 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2338 
2339 	item = gtk_menu_item_new_with_mnemonic (_("_Clear solutions"));
2340 	g_signal_connect (G_OBJECT (item), "activate",
2341 			  G_CALLBACK (clear_solutions_cb), NULL);
2342 	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2343 
2344 	gtk_widget_show_all (menubar);
2345 
2346 	plot_canvas = gtk_plot_canvas_new (WIDTH, HEIGHT, 1.0);
2347 	g_signal_connect (G_OBJECT (plot_canvas),
2348 			  "destroy",
2349 			  G_CALLBACK (gtk_widget_destroyed),
2350 			  &plot_canvas);
2351 	GTK_PLOT_CANVAS_UNSET_FLAGS (GTK_PLOT_CANVAS (plot_canvas),
2352 				     GTK_PLOT_CANVAS_DND_FLAGS);
2353 	g_signal_connect (G_OBJECT (plot_canvas), "select_region",
2354 			  G_CALLBACK (plot_select_region),
2355 			  NULL);
2356 	g_signal_connect (G_OBJECT (plot_canvas), "key_press_event",
2357 			  G_CALLBACK (plot_canvas_key_press_event),
2358 			  NULL);
2359 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (graph_window))),
2360 			    GTK_WIDGET (plot_canvas), TRUE, TRUE, 0);
2361 	gtk_widget_show (plot_canvas);
2362 
2363 	errors_label_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
2364 	gtk_box_pack_start
2365 		(GTK_BOX (errors_label_box),
2366 		 GTK_WIDGET (gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR)),
2367 		 FALSE, FALSE, 0);
2368 	gtk_box_pack_start
2369 		(GTK_BOX (errors_label_box),
2370 		 GTK_WIDGET (gtk_label_new (_("Errors during plotting (possibly harmless), see the console."))),
2371 		 FALSE, FALSE, 0);
2372 	gtk_widget_show_all (errors_label_box);
2373 	gtk_widget_hide (errors_label_box);
2374 
2375 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (graph_window))),
2376 			    GTK_WIDGET (errors_label_box), FALSE, FALSE, GENIUS_PAD);
2377 
2378 
2379 
2380 	gtk_widget_show (graph_window);
2381 }
2382 
2383 
2384 static void
clear_graph(void)2385 clear_graph (void)
2386 {
2387 	int i;
2388 
2389 	stop_rotate_anim_cb (NULL, NULL);
2390 
2391 	gtk_widget_hide (errors_label_box);
2392 
2393 	/* to avoid the costly removes */
2394 	g_slist_free (solutions_list);
2395 	solutions_list = NULL;
2396 
2397 	if (plot_child != NULL) {
2398 		if (plot_canvas != NULL)
2399 			gtk_plot_canvas_remove_child (GTK_PLOT_CANVAS (plot_canvas),
2400 						      plot_child);
2401 		surface_plot = NULL;
2402 		line_plot = NULL;
2403 		plot_child = NULL;
2404 	}
2405 
2406 	for (i = 0; i < MAXFUNC; i++) {
2407 		line_data[i] = NULL;
2408 	}
2409 
2410 	parametric_data =  NULL;
2411 
2412 	slopefield_data =  NULL;
2413 
2414 	vectorfield_data =  NULL;
2415 
2416 	surface_data = NULL;
2417 }
2418 
2419 static void
get_ticks(double start,double end,double * tick,int * prec,int * style,int * fontheight)2420 get_ticks (double start, double end, double *tick, int *prec,
2421 	   int *style, int *fontheight)
2422 {
2423 	int incs;
2424 	double len = end-start;
2425 	int tries = 0;
2426 	int tickprec;
2427 	int extra_prec;
2428 	int maxprec;
2429 	int diff_of_prec;
2430 
2431 	/* actually maxprec is the minimum since we're taking negatives */
2432 	if (start == 0.0) {
2433 		maxprec = -floor (log10(fabs(end)));
2434 	} else if (end == 0.0) {
2435 		maxprec = -floor (log10(fabs(start)));
2436 	} else {
2437 		maxprec = MIN(-floor (log10(fabs(start))),-floor (log10(fabs(end))));
2438 	}
2439 
2440 	tickprec = -floor (log10(len));
2441 	*tick = pow (10, -tickprec);
2442 	incs = floor (len / *tick);
2443 
2444 	*fontheight = 12;
2445 
2446 	extra_prec = 0;
2447 
2448 	while (incs < 4) {
2449 		*tick /= 2.0;
2450 
2451 		extra_prec ++;
2452 
2453 		incs = floor (len / *tick);
2454 		/* sanity */
2455 		if (tries ++ > 100) {
2456 			break;
2457 		}
2458 	}
2459 
2460 	while (incs > 6) {
2461 		*tick *= 2.0;
2462 		incs = floor (len / *tick);
2463 
2464 		if (extra_prec > 0)
2465 			extra_prec --;
2466 
2467 		/* sanity */
2468 		if (tries ++ > 100) {
2469 			break;
2470 		}
2471 	}
2472 
2473 	diff_of_prec = tickprec + extra_prec - maxprec;
2474 
2475 	if (maxprec <= -6) {
2476 		*prec = MAX(diff_of_prec,1);
2477 		*style = GTK_PLOT_LABEL_EXP;
2478 	} else if (maxprec >= 6) {
2479 		*prec = MAX(diff_of_prec,1);
2480 		*style = GTK_PLOT_LABEL_EXP;
2481 	} else if (maxprec <= 0) {
2482 		*style = GTK_PLOT_LABEL_FLOAT;
2483 		*prec = MAX(tickprec + extra_prec,0);
2484 	} else {
2485 		if (diff_of_prec > 2) {
2486 			*prec = MAX(diff_of_prec,1);
2487 			*style = GTK_PLOT_LABEL_EXP;
2488 		} else {
2489 			*style = GTK_PLOT_LABEL_FLOAT;
2490 			*prec = tickprec + extra_prec;
2491 		}
2492 	}
2493 
2494 	if (*style == GTK_PLOT_LABEL_FLOAT) {
2495 		if (diff_of_prec > 8) {
2496 			*fontheight = 8;
2497 		} else if (diff_of_prec > 6) {
2498 			*fontheight = 10;
2499 		}
2500 	} else if (*style == GTK_PLOT_LABEL_EXP) {
2501 		if (*prec > 4) {
2502 			*fontheight = 8;
2503 		} else if (*prec > 2) {
2504 			*fontheight = 10;
2505 		}
2506 	}
2507 }
2508 
2509 static void
plot_setup_axis(void)2510 plot_setup_axis (void)
2511 {
2512 	int xprec, yprec, xstyle, ystyle;
2513 	double xtick, ytick;
2514 	GtkPlotAxis *axis;
2515 	GdkRGBA gray;
2516 	int xfontheight, yfontheight;
2517 
2518 	get_ticks (plotx1, plotx2, &xtick, &xprec, &xstyle, &xfontheight);
2519 	get_ticks (ploty1, ploty2, &ytick, &yprec, &ystyle, &yfontheight);
2520 
2521 	gtk_plot_freeze (GTK_PLOT (line_plot));
2522 
2523 	gtk_plot_set_range (GTK_PLOT (line_plot),
2524 			    plotx1, plotx2, ploty1, ploty2);
2525 	gtk_plot_set_ticks (GTK_PLOT (line_plot), GTK_PLOT_AXIS_X, xtick, 9);
2526 	gtk_plot_set_ticks (GTK_PLOT (line_plot), GTK_PLOT_AXIS_Y, ytick, 9);
2527 
2528 	/* this should all be configurable */
2529 	gtk_plot_x0_set_visible (GTK_PLOT (line_plot), TRUE);
2530 	gtk_plot_y0_set_visible (GTK_PLOT (line_plot), TRUE);
2531 
2532 	gtk_plot_grids_set_visible (GTK_PLOT (line_plot),
2533 				    TRUE /* vmajor */,
2534 				    FALSE /* vminor */,
2535 				    TRUE /* vmajor */,
2536 				    FALSE /* vminor */);
2537 
2538 	gdk_rgba_parse (&gray, "gray75");
2539 
2540 	gtk_plot_x0line_set_attributes (GTK_PLOT (line_plot),
2541 					GTK_PLOT_LINE_SOLID,
2542 					1 /* width */,
2543 					&gray);
2544 	gtk_plot_y0line_set_attributes (GTK_PLOT (line_plot),
2545 					GTK_PLOT_LINE_SOLID,
2546 					1 /* width */,
2547 					&gray);
2548 
2549 	gtk_plot_major_vgrid_set_attributes (GTK_PLOT (line_plot),
2550 					     GTK_PLOT_LINE_DOTTED,
2551 					     1 /* width */,
2552 					     &gray);
2553 	gtk_plot_major_hgrid_set_attributes (GTK_PLOT (line_plot),
2554 					     GTK_PLOT_LINE_DOTTED,
2555 					     1 /* width */,
2556 					     &gray);
2557 
2558 
2559 	axis = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_TOP);
2560 	/* FIXME: this is a hack */
2561 	axis->labels_attr.height = xfontheight;
2562 	gtk_plot_axis_set_labels_style (axis,
2563 					xstyle /* style */,
2564 					xprec /* precision */);
2565 	gtk_plot_axis_show_labels (axis, lineplot_draw_labels ?
2566 				      GTK_PLOT_LABEL_OUT :
2567 				      GTK_PLOT_LABEL_NONE);
2568 
2569 	axis = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_BOTTOM);
2570 	/* FIXME: this is a hack */
2571 	axis->labels_attr.height = xfontheight;
2572 	gtk_plot_axis_set_labels_style (axis,
2573 					xstyle /* style */,
2574 					xprec /* precision */);
2575 	gtk_plot_axis_show_labels (axis, lineplot_draw_labels ?
2576 				      GTK_PLOT_LABEL_OUT :
2577 				      GTK_PLOT_LABEL_NONE);
2578 
2579 	axis = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_LEFT);
2580 	/* FIXME: this is a hack */
2581 	axis->labels_attr.height = yfontheight;
2582 	gtk_plot_axis_set_labels_style (axis,
2583 					ystyle /* style */,
2584 					yprec /* precision */);
2585 	gtk_plot_axis_show_labels (axis, lineplot_draw_labels ?
2586 				      GTK_PLOT_LABEL_OUT :
2587 				      GTK_PLOT_LABEL_NONE);
2588 
2589 	axis = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_RIGHT);
2590 	/* FIXME: this is a hack */
2591 	axis->labels_attr.height = yfontheight;
2592 	gtk_plot_axis_set_labels_style (axis,
2593 					ystyle /* style */,
2594 					yprec /* precision */);
2595 	gtk_plot_axis_show_labels (axis, lineplot_draw_labels ?
2596 				      GTK_PLOT_LABEL_OUT :
2597 				      GTK_PLOT_LABEL_NONE);
2598 
2599 
2600 	/* FIXME: implement logarithmic scale
2601 	gtk_plot_set_xscale (GTK_PLOT (line_plot), GTK_PLOT_SCALE_LOG10);
2602 	gtk_plot_set_yscale (GTK_PLOT (line_plot), GTK_PLOT_SCALE_LOG10);*/
2603 
2604 	gtk_plot_thaw (GTK_PLOT (line_plot));
2605 }
2606 
2607 static void
surface_setup_axis(void)2608 surface_setup_axis (void)
2609 {
2610 	int xprec, yprec, zprec;
2611 	int xstyle, ystyle, zstyle;
2612 	double xtick, ytick, ztick;
2613 	GtkPlotAxis *x, *y, *z;
2614 	int xfontheight, yfontheight, zfontheight;
2615 
2616 	get_ticks (surfacex1, surfacex2, &xtick, &xprec, &xstyle, &xfontheight);
2617 	get_ticks (surfacey1, surfacey2, &ytick, &yprec, &ystyle, &yfontheight);
2618 	get_ticks (surfacez1, surfacez2, &ztick, &zprec, &zstyle, &zfontheight);
2619 
2620 	x = gtk_plot3d_get_axis (GTK_PLOT3D (surface_plot), GTK_PLOT_AXIS_X);
2621 	y = gtk_plot3d_get_axis (GTK_PLOT3D (surface_plot), GTK_PLOT_AXIS_Y);
2622 	z = gtk_plot3d_get_axis (GTK_PLOT3D (surface_plot), GTK_PLOT_AXIS_Z);
2623 
2624 	gtk_plot_axis_freeze (x);
2625 	gtk_plot_axis_freeze (y);
2626 	gtk_plot_axis_freeze (z);
2627 
2628 	gtk_plot3d_set_xrange (GTK_PLOT3D (surface_plot), surfacex1, surfacex2);
2629 	gtk_plot_axis_set_ticks (x, xtick, 1);
2630 	gtk_plot3d_set_yrange (GTK_PLOT3D (surface_plot), surfacey1, surfacey2);
2631 	gtk_plot_axis_set_ticks (y, ytick, 1);
2632 	gtk_plot3d_set_zrange (GTK_PLOT3D (surface_plot), surfacez1, surfacez2);
2633 	gtk_plot_axis_set_ticks (z, ztick, 1);
2634 
2635 	/*FIXME: hack*/
2636 	x->labels_attr.height = xfontheight;
2637 	y->labels_attr.height = yfontheight;
2638 	z->labels_attr.height = zfontheight;
2639 
2640 	gtk_plot_axis_set_labels_style (x,
2641 					xstyle,
2642 					xprec /* precision */);
2643 	gtk_plot_axis_set_labels_style (y,
2644 					ystyle,
2645 					yprec /* precision */);
2646 	gtk_plot_axis_set_labels_style (z,
2647 					zstyle,
2648 					zprec /* precision */);
2649 
2650 	gtk_plot_axis_thaw (x);
2651 	gtk_plot_axis_thaw (y);
2652 	gtk_plot_axis_thaw (z);
2653 }
2654 
2655 static void
surface_setup_gradient(void)2656 surface_setup_gradient (void)
2657 {
2658 	double min, max, absminmax;
2659 
2660 	if (surface_data == NULL)
2661 		return;
2662 
2663 	min = MAX(surfacez1, plot_minz);
2664 	max = MIN(surfacez2, plot_maxz);
2665 
2666 	gtk_plot_data_set_gradient (surface_data,
2667 				    min,
2668 				    max,
2669 				    10 /* nlevels */,
2670 				    0 /* nsublevels */);
2671 	absminmax = MAX(fabs(min),fabs(max));
2672 	if (absminmax < 0.0001 || absminmax > 1000000) {
2673 		gtk_plot_data_gradient_set_style (surface_data,
2674 						  GTK_PLOT_LABEL_EXP,
2675 						  3);
2676 	} else {
2677 		int powten = floor (log10(absminmax));
2678 		int prec = 0;
2679 		if (-powten+2 > 0) {
2680 			prec = -powten+2;
2681 		} else {
2682 			prec = 0;
2683 		}
2684 		gtk_plot_data_gradient_set_style (surface_data,
2685 						  GTK_PLOT_LABEL_FLOAT,
2686 						  prec);
2687 	}
2688 
2689 }
2690 
2691 static void
plot_axis(void)2692 plot_axis (void)
2693 {
2694 	plot_in_progress ++;
2695 	gel_calc_running ++;
2696 	plot_window_setup ();
2697 
2698 	if (plot_mode == MODE_LINEPLOT) {
2699 		plot_maxy = - G_MAXDOUBLE/2;
2700 		plot_miny = G_MAXDOUBLE/2;
2701 		plot_maxx = - G_MAXDOUBLE/2;
2702 		plot_minx = G_MAXDOUBLE/2;
2703 		recompute_functions (FALSE /*fitting*/);
2704 		plot_setup_axis ();
2705 	} else if (plot_mode == MODE_LINEPLOT_PARAMETRIC ||
2706 		   plot_mode == MODE_LINEPLOT_SLOPEFIELD ||
2707 		   plot_mode == MODE_LINEPLOT_VECTORFIELD) {
2708 		plot_setup_axis ();
2709 	} else if (plot_mode == MODE_SURFACE) {
2710 		plot_maxx = - G_MAXDOUBLE/2;
2711 		plot_minx = G_MAXDOUBLE/2;
2712 		plot_maxy = - G_MAXDOUBLE/2;
2713 		plot_miny = G_MAXDOUBLE/2;
2714 		plot_maxz = - G_MAXDOUBLE/2;
2715 		plot_minz = G_MAXDOUBLE/2;
2716 		recompute_surface_function (FALSE /* fit */);
2717 		surface_setup_axis ();
2718 		if (surface_data != NULL)
2719 			gtk_plot_surface_build_mesh (GTK_PLOT_SURFACE (surface_data));
2720 		surface_setup_gradient ();
2721 		/* FIXME: this doesn't work (crashes) must fix in GtkExtra, then
2722 		   we can always just autoscale stuff
2723 		   gtk_plot3d_autoscale (GTK_PLOT3D (surface_plot));
2724 		 */
2725 	}
2726 
2727 	replot_fields ();
2728 
2729 	if (plot_canvas != NULL) {
2730 		gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
2731 		gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
2732 	}
2733 
2734 	plot_in_progress --;
2735 	gel_calc_running --;
2736 	plot_window_setup ();
2737 }
2738 
2739 static double
call_func3(GelCtx * ctx,GelEFunc * func,GelETree * arg,GelETree * arg2,GelETree * arg3,gboolean * ex,GelETree ** func_ret)2740 call_func3 (GelCtx *ctx,
2741 	    GelEFunc *func,
2742 	    GelETree *arg,
2743 	    GelETree *arg2,
2744 	    GelETree *arg3,
2745 	    gboolean *ex,
2746 	    GelETree **func_ret)
2747 {
2748 	GelETree *ret;
2749 	double retd;
2750 	GelETree *args[4];
2751 
2752 	args[0] = arg;
2753 	args[1] = arg2;
2754 	args[2] = arg3;
2755 	args[3] = NULL;
2756 
2757 	ret = gel_funccall (ctx, func, args, 3);
2758 
2759 	/* FIXME: handle errors! */
2760 	if G_UNLIKELY (gel_error_num != 0)
2761 		gel_error_num = 0;
2762 
2763 	/* only do one level of indirection to avoid infinite loops */
2764 	if (ret != NULL && ret->type == GEL_FUNCTION_NODE) {
2765 		if (ret->func.func->nargs == 3) {
2766 			GelETree *ret2;
2767 			ret2 = gel_funccall (ctx, ret->func.func, args, 3);
2768 			gel_freetree (ret);
2769 			ret = ret2;
2770 			/* FIXME: handle errors! */
2771 			if G_UNLIKELY (gel_error_num != 0)
2772 				gel_error_num = 0;
2773 		} else if (func_ret != NULL) {
2774 			*func_ret = ret;
2775 #ifdef HUGE_VAL
2776 			return HUGE_VAL;
2777 #else
2778 			return 0;
2779 #endif
2780 		}
2781 
2782 	}
2783 
2784 	if (ret == NULL || ret->type != GEL_VALUE_NODE) {
2785 		*ex = TRUE;
2786 		gel_freetree (ret);
2787 #ifdef HUGE_VAL
2788 		return HUGE_VAL;
2789 #else
2790 		return 0;
2791 #endif
2792 	}
2793 
2794 	retd = mpw_get_double (ret->val.value);
2795 	if G_UNLIKELY (gel_error_num != 0) {
2796 		*ex = TRUE;
2797 		gel_error_num = 0;
2798 #ifdef HUGE_VAL
2799 		retd = HUGE_VAL;
2800 #endif
2801 	}
2802 
2803 	gel_freetree (ret);
2804 	return retd;
2805 }
2806 
2807 static double
call_func2(GelCtx * ctx,GelEFunc * func,GelETree * arg,GelETree * arg2,gboolean * ex,GelETree ** func_ret)2808 call_func2 (GelCtx *ctx,
2809 	    GelEFunc *func,
2810 	    GelETree *arg,
2811 	    GelETree *arg2,
2812 	    gboolean *ex,
2813 	    GelETree **func_ret)
2814 {
2815 	GelETree *ret;
2816 	double retd;
2817 	GelETree *args[3];
2818 
2819 	args[0] = arg;
2820 	args[1] = arg2;
2821 	args[2] = NULL;
2822 
2823 	ret = gel_funccall (ctx, func, args, 2);
2824 
2825 	/* FIXME: handle errors! */
2826 	if G_UNLIKELY (gel_error_num != 0)
2827 		gel_error_num = 0;
2828 
2829 	/* only do one level of indirection to avoid infinite loops */
2830 	if (ret != NULL && ret->type == GEL_FUNCTION_NODE) {
2831 		if (ret->func.func->nargs == 2) {
2832 			GelETree *ret2;
2833 			ret2 = gel_funccall (ctx, ret->func.func, args, 2);
2834 			gel_freetree (ret);
2835 			ret = ret2;
2836 			/* FIXME: handle errors! */
2837 			if G_UNLIKELY (gel_error_num != 0)
2838 				gel_error_num = 0;
2839 		} else if (func_ret != NULL) {
2840 			*func_ret = ret;
2841 #ifdef HUGE_VAL
2842 			return HUGE_VAL;
2843 #else
2844 			return 0;
2845 #endif
2846 		}
2847 	}
2848 
2849 	if (ret == NULL || ret->type != GEL_VALUE_NODE) {
2850 		*ex = TRUE;
2851 		gel_freetree (ret);
2852 #ifdef HUGE_VAL
2853 		return HUGE_VAL;
2854 #else
2855 		return 0;
2856 #endif
2857 	}
2858 
2859 	retd = mpw_get_double (ret->val.value);
2860 	if G_UNLIKELY (gel_error_num != 0) {
2861 		*ex = TRUE;
2862 		gel_error_num = 0;
2863 #ifdef HUGE_VAL
2864 		retd = HUGE_VAL;
2865 #endif
2866 	}
2867 
2868 	gel_freetree (ret);
2869 	return retd;
2870 }
2871 
2872 static double
call_func(GelCtx * ctx,GelEFunc * func,GelETree * arg,gboolean * ex,GelETree ** func_ret)2873 call_func (GelCtx *ctx,
2874 	   GelEFunc *func,
2875 	   GelETree *arg,
2876 	   gboolean *ex,
2877 	   GelETree **func_ret)
2878 {
2879 	GelETree *ret;
2880 	double retd;
2881 	GelETree *args[2];
2882 
2883 	args[0] = arg;
2884 	args[1] = NULL;
2885 
2886 	ret = gel_funccall (ctx, func, args, 1);
2887 
2888 	/* FIXME: handle errors! */
2889 	if G_UNLIKELY (gel_error_num != 0)
2890 		gel_error_num = 0;
2891 
2892 	/* only do one level of indirection to avoid infinite loops */
2893 	if (ret != NULL && ret->type == GEL_FUNCTION_NODE) {
2894 		if (ret->func.func->nargs == 1) {
2895 			GelETree *ret2;
2896 			ret2 = gel_funccall (ctx, ret->func.func, args, 1);
2897 			gel_freetree (ret);
2898 			ret = ret2;
2899 			/* FIXME: handle errors! */
2900 			if G_UNLIKELY (gel_error_num != 0)
2901 				gel_error_num = 0;
2902 		} else if (func_ret != NULL) {
2903 			*func_ret = ret;
2904 #ifdef HUGE_VAL
2905 			return HUGE_VAL;
2906 #else
2907 			return 0;
2908 #endif
2909 		}
2910 
2911 	}
2912 
2913 	if (ret == NULL || ret->type != GEL_VALUE_NODE) {
2914 		*ex = TRUE;
2915 		gel_freetree (ret);
2916 #ifdef HUGE_VAL
2917 		return HUGE_VAL;
2918 #else
2919 		return 0;
2920 #endif
2921 	}
2922 
2923 	retd = mpw_get_double (ret->val.value);
2924 	if G_UNLIKELY (gel_error_num != 0) {
2925 		*ex = TRUE;
2926 		gel_error_num = 0;
2927 #ifdef HUGE_VAL
2928 		retd = HUGE_VAL;
2929 #endif
2930 	}
2931 
2932 	gel_freetree (ret);
2933 	return retd;
2934 }
2935 
2936 static void
call_func_z(GelCtx * ctx,GelEFunc * func,GelETree * arg,double * retx,double * rety,gboolean * ex,GelETree ** func_ret)2937 call_func_z (GelCtx *ctx,
2938 	     GelEFunc *func,
2939 	     GelETree *arg,
2940 	     double *retx,
2941 	     double *rety,
2942 	     gboolean *ex,
2943 	     GelETree **func_ret)
2944 {
2945 	GelETree *ret;
2946 	GelETree *args[2];
2947 
2948 	args[0] = arg;
2949 	args[1] = NULL;
2950 
2951 	ret = gel_funccall (ctx, func, args, 1);
2952 
2953 	/* FIXME: handle errors! */
2954 	if G_UNLIKELY (gel_error_num != 0)
2955 		gel_error_num = 0;
2956 
2957 	/* only do one level of indirection to avoid infinite loops */
2958 	if (ret != NULL && ret->type == GEL_FUNCTION_NODE) {
2959 		if (ret->func.func->nargs == 1) {
2960 			GelETree *ret2;
2961 			ret2 = gel_funccall (ctx, ret->func.func, args, 1);
2962 			gel_freetree (ret);
2963 			ret = ret2;
2964 			/* FIXME: handle errors! */
2965 			if G_UNLIKELY (gel_error_num != 0)
2966 				gel_error_num = 0;
2967 		} else if (func_ret != NULL) {
2968 			*func_ret = ret;
2969 #ifdef HUGE_VAL
2970 			*retx = HUGE_VAL;
2971 			*rety = HUGE_VAL;
2972 #else
2973 			*retx = 0.0;
2974 			*rety = 0.0;
2975 #endif
2976 			return;
2977 		}
2978 
2979 	}
2980 
2981 	if (ret == NULL || ret->type != GEL_VALUE_NODE) {
2982 		*ex = TRUE;
2983 		gel_freetree (ret);
2984 #ifdef HUGE_VAL
2985 		*retx = HUGE_VAL;
2986 		*rety = HUGE_VAL;
2987 #else
2988 		*retx = 0.0;
2989 		*rety = 0.0;
2990 #endif
2991 		return;
2992 	}
2993 
2994 	mpw_get_complex_double (ret->val.value, retx, rety);
2995 	if G_UNLIKELY (gel_error_num != 0) {
2996 		*ex = TRUE;
2997 		gel_error_num = 0;
2998 #ifdef HUGE_VAL
2999 		*retx = HUGE_VAL;
3000 		*rety = HUGE_VAL;
3001 #else
3002 		*retx = 0.0;
3003 		*rety = 0.0;
3004 #endif
3005 	}
3006 
3007 	gel_freetree (ret);
3008 }
3009 
3010 #if 0
3011 static double
3012 plot_func_data (GtkPlot *plot, GtkPlotData *data, double x, gboolean *error)
3013 {
3014 	static int hookrun = 0;
3015 	gboolean ex = FALSE;
3016 	int i;
3017 	double y;
3018 
3019 	if (error != NULL)
3020 		*error = FALSE;
3021 
3022 	if G_UNLIKELY (gel_interrupted) {
3023 		if (error != NULL)
3024 			*error = TRUE;
3025 		return 0.0;
3026 	}
3027 
3028 	for (i = 0; i < MAXFUNC; i++) {
3029 		if (data == line_data[i])
3030 			break;
3031 	}
3032 	if G_UNLIKELY (i == MAXFUNC) {
3033 		if (error != NULL)
3034 			*error = TRUE;
3035 		return 0.0;
3036 	}
3037 
3038 	mpw_set_d (plot_arg->val.value, x);
3039 	y = call_func (plot_ctx, plot_func[i], plot_arg, &ex, NULL);
3040 
3041 	if G_UNLIKELY (ex) {
3042 		if (error != NULL)
3043 			*error = TRUE;
3044 	} else {
3045 		if G_UNLIKELY (y > plot_maxy)
3046 			plot_maxy = y;
3047 		if G_UNLIKELY (y < plot_miny)
3048 			plot_miny = y;
3049 	}
3050 
3051 	/* No need of this anymore, my hacked version handles lines going
3052 	 * way off at least in our use case
3053 	if (y > ploty2 || y < ploty1) {
3054 		if (error != NULL)
3055 			*error = TRUE;
3056 	}
3057 	*/
3058 
3059 
3060 	if G_UNLIKELY (hookrun++ >= 10) {
3061 		if (gel_evalnode_hook != NULL) {
3062 			hookrun = 0;
3063 			(*gel_evalnode_hook)();
3064 			if G_UNLIKELY (gel_interrupted) {
3065 				if (error != NULL)
3066 					*error = TRUE;
3067 				return y;
3068 			}
3069 		}
3070 	}
3071 
3072 	return y;
3073 }
3074 #endif
3075 
3076 static double
call_xy_or_z_function(GelEFunc * f,double x,double y,gboolean * ex)3077 call_xy_or_z_function (GelEFunc *f, double x, double y, gboolean *ex)
3078 {
3079 	GelETree *func_ret = NULL;
3080 	double z;
3081 
3082 	/* complex function */
3083 	if (f->nargs == 1) {
3084 		mpw_set_d_complex (plot_arg->val.value, x, y);
3085 		z = call_func (plot_ctx, f, plot_arg, ex,
3086 			       &func_ret);
3087 	} else if (f->nargs == 2) {
3088 		mpw_set_d (plot_arg->val.value, x);
3089 		mpw_set_d (plot_arg2->val.value, y);
3090 		z = call_func2 (plot_ctx, f, plot_arg, plot_arg2,
3091 				ex, &func_ret);
3092 	} else {
3093 		mpw_set_d (plot_arg->val.value, x);
3094 		mpw_set_d (plot_arg2->val.value, y);
3095 		mpw_set_d_complex (plot_arg3->val.value, x, y);
3096 		z = call_func3 (plot_ctx, f, plot_arg, plot_arg2,
3097 				plot_arg3, ex, &func_ret);
3098 	}
3099 	if (func_ret != NULL) {
3100 		/* complex function */
3101 		if (func_ret->func.func->nargs == 1) {
3102 			mpw_set_d_complex (plot_arg->val.value, x, y);
3103 			z = call_func (plot_ctx, func_ret->func.func, plot_arg, ex,
3104 				       NULL);
3105 		} else if (func_ret->func.func->nargs == 2) {
3106 			mpw_set_d (plot_arg->val.value, x);
3107 			mpw_set_d (plot_arg2->val.value, y);
3108 			z = call_func2 (plot_ctx, func_ret->func.func, plot_arg, plot_arg2,
3109 					ex, NULL);
3110 		} else {
3111 			mpw_set_d (plot_arg->val.value, x);
3112 			mpw_set_d (plot_arg2->val.value, y);
3113 			mpw_set_d_complex (plot_arg3->val.value, x, y);
3114 			z = call_func3 (plot_ctx, func_ret->func.func, plot_arg, plot_arg2,
3115 					plot_arg3, ex, NULL);
3116 		}
3117 	}
3118 
3119 	return z;
3120 }
3121 
3122 
3123 /*
3124 static double
3125 surface_func_data (GtkPlot *plot, GtkPlotData *data, double x, double y, gboolean *error)
3126 */
3127 static double
surface_func_data(double x,double y,gboolean * error)3128 surface_func_data (double x, double y, gboolean *error)
3129 {
3130 	static int hookrun = 0;
3131 	gboolean ex = FALSE;
3132 	double z;
3133 
3134 	if (error != NULL)
3135 		*error = FALSE;
3136 
3137 	if G_UNLIKELY (gel_interrupted) {
3138 		if (error != NULL)
3139 			*error = TRUE;
3140 		return 0.0;
3141 	}
3142 
3143 	z = call_xy_or_z_function (surface_func, x, y, &ex);
3144 
3145 	if G_UNLIKELY (ex) {
3146 		if (error != NULL)
3147 			*error = TRUE;
3148 	} else {
3149 		if G_UNLIKELY (z > plot_maxz)
3150 			plot_maxz = z;
3151 		if G_UNLIKELY (z < plot_minz)
3152 			plot_minz = z;
3153 	}
3154 
3155 	/*
3156 	size = surfacez1 - surfacez2;
3157 
3158 	if (z > (surfacez2+size*0.2) || z < (surfacez1-size*0.2)) {
3159 		if (error != NULL)
3160 			*error = TRUE;
3161 	}
3162 	*/
3163 
3164 
3165 	if G_UNLIKELY (hookrun++ >= 10) {
3166 		if (gel_evalnode_hook != NULL) {
3167 			hookrun = 0;
3168 			(*gel_evalnode_hook)();
3169 			if G_UNLIKELY (gel_interrupted) {
3170 				if (error != NULL)
3171 					*error = TRUE;
3172 				return z;
3173 			}
3174 		}
3175 	}
3176 
3177 	return z;
3178 }
3179 
3180 static void
get_slopefield_points(void)3181 get_slopefield_points (void)
3182 {
3183 	double x, y, vt, ht, z, dx, dy;
3184 	int i, j, k;
3185 	gboolean ex;
3186 	double pw, ph;
3187 	double xmul, ymul;
3188 	double sz;
3189 	double mt;
3190 
3191 	/* FIXME: evil, see the AAAARGH below! */
3192 
3193 	gtk_plot_get_size (GTK_PLOT (line_plot), &pw, &ph);
3194 	xmul = (pw * WIDTH) / (plotx2 - plotx1);
3195 	ymul = (ph * HEIGHT) / (ploty2 - ploty1);
3196 
3197 	ht = (plotx2 - plotx1) / (plotHtick);
3198 	vt = (ploty2 - ploty1) / (plotVtick);
3199 
3200 	mt = MIN (((pw * WIDTH) / (plotHtick)),
3201 		  ((ph * HEIGHT) / (plotVtick))) * 0.8;
3202 
3203 	g_free (plot_points_x);
3204 	g_free (plot_points_y);
3205 	g_free (plot_points_dx);
3206 	g_free (plot_points_dy);
3207 
3208 	plot_points_x = g_new (double, (plotHtick)*(plotVtick));
3209 	plot_points_y = g_new (double, (plotHtick)*(plotVtick));
3210 	plot_points_dx = g_new (double, (plotHtick)*(plotVtick));
3211 	plot_points_dy = g_new (double, (plotHtick)*(plotVtick));
3212 
3213 	k = 0;
3214 	for (i = 0; i < plotHtick; i++) {
3215 		for (j = 0; j < plotVtick; j++) {
3216 			x = plotx1 + ht*(i+0.5);
3217 			y = ploty1 + vt*(j+0.5);
3218 			ex = FALSE;
3219 			z = call_xy_or_z_function (slopefield_func,
3220 						   x, y, &ex);
3221 
3222 			if G_LIKELY ( ! ex) {
3223 				/* gtkextra fluxplot is nuts, it does the
3224 				 * dx and dy are in pixel coordinates,
3225 				 * AAAAAARGH! */
3226 
3227 				z = z*ymul;
3228 				sz = sqrt(z*z + xmul*xmul);
3229 				dx = xmul / sz;
3230 				dy = z / sz;
3231 
3232 				dx *= mt;
3233 				dy *= mt;
3234 
3235 				plot_points_x[k] = x;
3236 				plot_points_dx[k] = dx;
3237 
3238 				plot_points_y[k] = y;
3239 				plot_points_dy[k] = dy;
3240 
3241 				k++;
3242 			}
3243 		}
3244 	}
3245 
3246 	plot_points_num = k;
3247 }
3248 
3249 static void
get_vectorfield_points(void)3250 get_vectorfield_points (void)
3251 {
3252 	double x, y, vt, ht, dx, dy;
3253 	int i, j, k;
3254 	gboolean ex;
3255 	double maxsz = 0.0;
3256 	double pw, ph;
3257 	double xmul, ymul;
3258 	double mt, sz;
3259 
3260 	/* FIXME: evil, see the AAAARGH below! */
3261 
3262 	gtk_plot_get_size (GTK_PLOT (line_plot), &pw, &ph);
3263 	xmul = (pw * WIDTH) / (plotx2 - plotx1);
3264 	ymul = (ph * HEIGHT) / (ploty2 - ploty1);
3265 
3266 	ht = (plotx2 - plotx1) / (plotHtick);
3267 	vt = (ploty2 - ploty1) / (plotVtick);
3268 
3269 	mt = MIN (((pw * WIDTH) / (plotHtick)),
3270 		  ((ph * HEIGHT) / (plotVtick))) * 0.95;
3271 
3272 	g_free (plot_points_x);
3273 	g_free (plot_points_y);
3274 	g_free (plot_points_dx);
3275 	g_free (plot_points_dy);
3276 
3277 	plot_points_x = g_new (double, (plotHtick)*(plotVtick));
3278 	plot_points_y = g_new (double, (plotHtick)*(plotVtick));
3279 	plot_points_dx = g_new (double, (plotHtick)*(plotVtick));
3280 	plot_points_dy = g_new (double, (plotHtick)*(plotVtick));
3281 
3282 	k = 0;
3283 	for (i = 0; i < plotHtick; i++) {
3284 		for (j = 0; j < plotVtick; j++) {
3285 			x = plotx1 + ht*(i+0.5);
3286 			y = ploty1 + vt*(j+0.5);
3287 			ex = FALSE;
3288 			dx = call_xy_or_z_function (vectorfield_func_x,
3289 						    x, y, &ex);
3290 			dy = call_xy_or_z_function (vectorfield_func_y,
3291 						    x, y, &ex);
3292 
3293 			if G_LIKELY ( ! ex) {
3294 				/* gtkextra fluxplot is nuts, it does the
3295 				 * dx and dy are in pixel coordinates,
3296 				 * AAAAAARGH! */
3297 
3298 				dx = dx*xmul;
3299 				dy = dy*ymul;
3300 
3301 				sz = sqrt(dx*dx + dy*dy);
3302 
3303 				if (vectorfield_normalize_arrow_length) {
3304 					if (sz > 0) {
3305 						dx /= sz;
3306 						dy /= sz;
3307 					}
3308 				} else {
3309 					if (sz > maxsz)
3310 						maxsz = sz;
3311 				}
3312 
3313 				plot_points_x[k] = x;
3314 				plot_points_dx[k] = dx * mt;
3315 
3316 				plot_points_y[k] = y;
3317 				plot_points_dy[k] = dy * mt;
3318 
3319 
3320 				k++;
3321 			}
3322 		}
3323 	}
3324 
3325 	plot_points_num = k;
3326 
3327 	if ( ! vectorfield_normalize_arrow_length) {
3328 		if (maxsz > 0) {
3329 			for (k = 0; k < plot_points_num; k++) {
3330 				plot_points_dx[k] /= maxsz;
3331 				plot_points_dy[k] /= maxsz;
3332 			}
3333 		}
3334 	}
3335 }
3336 
3337 static char *
label_func(int i,GelEFunc * func,const char * var,const char * name)3338 label_func (int i, GelEFunc *func, const char *var, const char *name)
3339 {
3340 	char *text = NULL;
3341 
3342 	if (name != NULL) {
3343 		return g_strdup (name);
3344 	} else if (func->id != NULL) {
3345 		text = g_strdup_printf ("%s(%s)", func->id->token, var);
3346 	} else if (func->type == GEL_USER_FUNC) {
3347 		int old_style, len;
3348 		GelOutput *out = gel_output_new ();
3349 		D_ENSURE_USER_BODY (func);
3350 		gel_output_setup_string (out, 0, NULL);
3351 
3352 		/* FIXME: the push/pop of style is UGLY */
3353 		old_style = gel_calcstate.output_style;
3354 		gel_calcstate.output_style = GEL_OUTPUT_NORMAL;
3355 		gel_print_etree (out, func->data.user, TRUE /* toplevel */);
3356 		gel_calcstate.output_style = old_style;
3357 
3358 		text = gel_output_snarf_string (out);
3359 		gel_output_unref (out);
3360 
3361 		len = strlen (text);
3362 
3363 		if (len > 2 &&
3364 		    text[0] == '(' &&
3365 		    text[len-1] == ')') {
3366 			char *s;
3367 			text[len-1] = '\0';
3368 			s = g_strdup (&text[1]);
3369 			g_free (text);
3370 			text = s;
3371 			len-=2;
3372 		}
3373 
3374 		/* only print bodies of short functions */
3375 		if (len > 64) {
3376 			g_free (text);
3377 			text = NULL;
3378 		}
3379 	}
3380 
3381 	if (text == NULL) {
3382 		if (i < 0)
3383 			text = g_strdup_printf (_("Function"));
3384 		else
3385 			text = g_strdup_printf (_("Function #%d"), i+1);
3386 	}
3387 
3388 	return text;
3389 }
3390 
3391 #define GET_DOUBLE(var,argnum,func) \
3392 	{ \
3393 	if (a[argnum]->type != GEL_VALUE_NODE) { \
3394 		gel_errorout (_("%s: argument number %d not a number"), func, argnum+1); \
3395 		return NULL; \
3396 	} \
3397 	var = mpw_get_double (a[argnum]->val.value); \
3398 	}
3399 
3400 static gboolean
get_limits_from_matrix(GelETree * m,double * x1,double * x2,double * y1,double * y2)3401 get_limits_from_matrix (GelETree *m, double *x1, double *x2, double *y1, double *y2)
3402 {
3403 	GelETree *t;
3404 
3405 	if (m->type != GEL_MATRIX_NODE ||
3406 	    gel_matrixw_elements (m->mat.matrix) != 4) {
3407 		gel_errorout (_("Graph limits not given as a 4-vector"));
3408 		return FALSE;
3409 	}
3410 
3411 	t = gel_matrixw_vindex (m->mat.matrix, 0);
3412 	if (t->type != GEL_VALUE_NODE) {
3413 		gel_errorout (_("Graph limits not given as numbers"));
3414 		return FALSE;
3415 	}
3416 	*x1 = mpw_get_double (t->val.value);
3417 	if G_UNLIKELY (gel_error_num != 0) {
3418 		gel_error_num = 0;
3419 		return FALSE;
3420 	}
3421 
3422 	t = gel_matrixw_vindex (m->mat.matrix, 1);
3423 	if (t->type != GEL_VALUE_NODE) {
3424 		gel_errorout (_("Graph limits not given as numbers"));
3425 		return FALSE;
3426 	}
3427 	*x2 = mpw_get_double (t->val.value);
3428 	if G_UNLIKELY (gel_error_num != 0) {
3429 		gel_error_num = 0;
3430 		return FALSE;
3431 	}
3432 
3433 	t = gel_matrixw_vindex (m->mat.matrix, 2);
3434 	if (t->type != GEL_VALUE_NODE) {
3435 		gel_errorout (_("Graph limits not given as numbers"));
3436 		return FALSE;
3437 	}
3438 	*y1 = mpw_get_double (t->val.value);
3439 	if G_UNLIKELY (gel_error_num != 0) {
3440 		gel_error_num = 0;
3441 		return FALSE;
3442 	}
3443 
3444 	t = gel_matrixw_vindex (m->mat.matrix, 3);
3445 	if (t->type != GEL_VALUE_NODE) {
3446 		gel_errorout (_("Graph limits not given as numbers"));
3447 		return FALSE;
3448 	}
3449 	*y2 = mpw_get_double (t->val.value);
3450 	if G_UNLIKELY (gel_error_num != 0) {
3451 		gel_error_num = 0;
3452 		return FALSE;
3453 	}
3454 
3455 	if (*x1 > *x2) {
3456 		double s = *x1;
3457 		*x1 = *x2;
3458 		*x2 = s;
3459 	}
3460 
3461 	if (*y1 > *y2) {
3462 		double s = *y1;
3463 		*y1 = *y2;
3464 		*y2 = s;
3465 	}
3466 
3467 	/* sanity */
3468 	if (*x2 - *x1 < MINPLOT)
3469 		*x2 = *x1 + MINPLOT;
3470 	if (*y2 - *y1 < MINPLOT)
3471 		*y2 = *y1 + MINPLOT;
3472 
3473 	return TRUE;
3474 }
3475 
3476 static gboolean
get_limits_from_matrix_xonly(GelETree * m,double * x1,double * x2)3477 get_limits_from_matrix_xonly (GelETree *m, double *x1, double *x2)
3478 {
3479 	GelETree *t;
3480 
3481 	if (m->type != GEL_MATRIX_NODE ||
3482 	    gel_matrixw_elements (m->mat.matrix) != 2) {
3483 		gel_errorout (_("Graph limits not given as a 2-vector"));
3484 		return FALSE;
3485 	}
3486 
3487 	t = gel_matrixw_vindex (m->mat.matrix, 0);
3488 	if (t->type != GEL_VALUE_NODE) {
3489 		gel_errorout (_("Graph limits not given as numbers"));
3490 		return FALSE;
3491 	}
3492 	*x1 = mpw_get_double (t->val.value);
3493 	if G_UNLIKELY (gel_error_num != 0) {
3494 		gel_error_num = 0;
3495 		return FALSE;
3496 	}
3497 
3498 	t = gel_matrixw_vindex (m->mat.matrix, 1);
3499 	if (t->type != GEL_VALUE_NODE) {
3500 		gel_errorout (_("Graph limits not given as numbers"));
3501 		return FALSE;
3502 	}
3503 	*x2 = mpw_get_double (t->val.value);
3504 	if G_UNLIKELY (gel_error_num != 0) {
3505 		gel_error_num = 0;
3506 		return FALSE;
3507 	}
3508 
3509 	if (*x1 > *x2) {
3510 		double s = *x1;
3511 		*x1 = *x2;
3512 		*x2 = s;
3513 	}
3514 
3515 	/* sanity */
3516 	if (*x2 - *x1 < MINPLOT)
3517 		*x2 = *x1 + MINPLOT;
3518 
3519 	return TRUE;
3520 }
3521 
3522 static GelETree *
make_matrix_from_limits(void)3523 make_matrix_from_limits (void)
3524 {
3525 	GelETree *n;
3526 	GelMatrixW *m;
3527 	/*make us a new empty node*/
3528 	GEL_GET_NEW_NODE (n);
3529 	n->type = GEL_MATRIX_NODE;
3530 	m = n->mat.matrix = gel_matrixw_new ();
3531 	n->mat.quoted = FALSE;
3532 	gel_matrixw_set_size (m, 4, 1);
3533 
3534 	gel_matrixw_set_indexii (m, 0) = gel_makenum_d (defx1);
3535 	gel_matrixw_set_index (m, 1, 0) = gel_makenum_d (defx2);
3536 	gel_matrixw_set_index (m, 2, 0) = gel_makenum_d (defy1);
3537 	gel_matrixw_set_index (m, 3, 0) = gel_makenum_d (defy2);
3538 
3539 	return n;
3540 }
3541 
3542 static GelETree *
make_matrix_from_lp_varnames(void)3543 make_matrix_from_lp_varnames (void)
3544 {
3545 	GelETree *n;
3546 	GelMatrixW *m;
3547 	/*make us a new empty node*/
3548 	GEL_GET_NEW_NODE (n);
3549 	n->type = GEL_MATRIX_NODE;
3550 	m = n->mat.matrix = gel_matrixw_new ();
3551 	n->mat.quoted = FALSE;
3552 	gel_matrixw_set_size (m, 4, 1);
3553 
3554 	init_var_names ();
3555 
3556 	gel_matrixw_set_indexii (m, 0) = gel_makenum_string (lp_x_name);
3557 	gel_matrixw_set_index (m, 1, 0) = gel_makenum_string (lp_y_name);
3558 	gel_matrixw_set_index (m, 2, 0) = gel_makenum_string (lp_z_name);
3559 	gel_matrixw_set_index (m, 3, 0) = gel_makenum_string (lp_t_name);
3560 
3561 	return n;
3562 }
3563 
3564 static GelETree *
make_matrix_from_sp_varnames(void)3565 make_matrix_from_sp_varnames (void)
3566 {
3567 	GelETree *n;
3568 	GelMatrixW *m;
3569 	/*make us a new empty node*/
3570 	GEL_GET_NEW_NODE (n);
3571 	n->type = GEL_MATRIX_NODE;
3572 	m = n->mat.matrix = gel_matrixw_new ();
3573 	n->mat.quoted = FALSE;
3574 	gel_matrixw_set_size (m, 3, 1);
3575 
3576 	init_var_names ();
3577 
3578 	gel_matrixw_set_indexii (m, 0) = gel_makenum_string (sp_x_name);
3579 	gel_matrixw_set_index (m, 1, 0) = gel_makenum_string (sp_y_name);
3580 	gel_matrixw_set_index (m, 2, 0) = gel_makenum_string (sp_z_name);
3581 
3582 	return n;
3583 }
3584 
3585 static gboolean
get_limits_from_matrix_surf(GelETree * m,double * x1,double * x2,double * y1,double * y2,double * z1,double * z2)3586 get_limits_from_matrix_surf (GelETree *m, double *x1, double *x2, double *y1, double *y2, double *z1, double *z2)
3587 {
3588 	GelETree *t;
3589 
3590 	if (m->type != GEL_MATRIX_NODE ||
3591 	    gel_matrixw_elements (m->mat.matrix) != 6) {
3592 		gel_errorout (_("Graph limits not given as a 6-vector"));
3593 		return FALSE;
3594 	}
3595 
3596 	t = gel_matrixw_vindex (m->mat.matrix, 0);
3597 	if (t->type != GEL_VALUE_NODE) {
3598 		gel_errorout (_("Graph limits not given as numbers"));
3599 		return FALSE;
3600 	}
3601 	*x1 = mpw_get_double (t->val.value);
3602 	if G_UNLIKELY (gel_error_num != 0) {
3603 		gel_error_num = 0;
3604 		return FALSE;
3605 	}
3606 
3607 	t = gel_matrixw_vindex (m->mat.matrix, 1);
3608 	if (t->type != GEL_VALUE_NODE) {
3609 		gel_errorout (_("Graph limits not given as numbers"));
3610 		return FALSE;
3611 	}
3612 	*x2 = mpw_get_double (t->val.value);
3613 	if G_UNLIKELY (gel_error_num != 0) {
3614 		gel_error_num = 0;
3615 		return FALSE;
3616 	}
3617 
3618 	t = gel_matrixw_vindex (m->mat.matrix, 2);
3619 	if (t->type != GEL_VALUE_NODE) {
3620 		gel_errorout (_("Graph limits not given as numbers"));
3621 		return FALSE;
3622 	}
3623 	*y1 = mpw_get_double (t->val.value);
3624 	if G_UNLIKELY (gel_error_num != 0) {
3625 		gel_error_num = 0;
3626 		return FALSE;
3627 	}
3628 
3629 	t = gel_matrixw_vindex (m->mat.matrix, 3);
3630 	if (t->type != GEL_VALUE_NODE) {
3631 		gel_errorout (_("Graph limits not given as numbers"));
3632 		return FALSE;
3633 	}
3634 	*y2 = mpw_get_double (t->val.value);
3635 	if G_UNLIKELY (gel_error_num != 0) {
3636 		gel_error_num = 0;
3637 		return FALSE;
3638 	}
3639 
3640 	t = gel_matrixw_vindex (m->mat.matrix, 4);
3641 	if (t->type != GEL_VALUE_NODE) {
3642 		gel_errorout (_("Graph limits not given as numbers"));
3643 		return FALSE;
3644 	}
3645 	*z1 = mpw_get_double (t->val.value);
3646 	if G_UNLIKELY (gel_error_num != 0) {
3647 		gel_error_num = 0;
3648 		return FALSE;
3649 	}
3650 
3651 	t = gel_matrixw_vindex (m->mat.matrix, 5);
3652 	if (t->type != GEL_VALUE_NODE) {
3653 		gel_errorout (_("Graph limits not given as numbers"));
3654 		return FALSE;
3655 	}
3656 	*z2 = mpw_get_double (t->val.value);
3657 	if G_UNLIKELY (gel_error_num != 0) {
3658 		gel_error_num = 0;
3659 		return FALSE;
3660 	}
3661 
3662 	if (*x1 > *x2) {
3663 		double s = *x1;
3664 		*x1 = *x2;
3665 		*x2 = s;
3666 	}
3667 
3668 	if (*y1 > *y2) {
3669 		double s = *y1;
3670 		*y1 = *y2;
3671 		*y2 = s;
3672 	}
3673 
3674 	if (*z1 > *z2) {
3675 		double s = *z1;
3676 		*z1 = *z2;
3677 		*z2 = s;
3678 	}
3679 
3680 
3681 	/* sanity */
3682 	if (*x2 - *x1 < MINPLOT)
3683 		*x2 = *x1 + MINPLOT;
3684 	if (*y2 - *y1 < MINPLOT)
3685 		*y2 = *y1 + MINPLOT;
3686 	if (*z2 - *z1 < MINPLOT)
3687 		*z2 = *z1 + MINPLOT;
3688 
3689 	return TRUE;
3690 }
3691 
3692 static GelETree *
make_matrix_from_limits_surf(void)3693 make_matrix_from_limits_surf (void)
3694 {
3695 	GelETree *n;
3696 	GelMatrixW *m;
3697 	/*make us a new empty node*/
3698 	GEL_GET_NEW_NODE (n);
3699 	n->type = GEL_MATRIX_NODE;
3700 	m = n->mat.matrix = gel_matrixw_new ();
3701 	n->mat.quoted = FALSE;
3702 	gel_matrixw_set_size (m, 6, 1);
3703 
3704 	gel_matrixw_set_indexii (m, 0) = gel_makenum_d (surf_defx1);
3705 	gel_matrixw_set_index (m, 1, 0) = gel_makenum_d (surf_defx2);
3706 	gel_matrixw_set_index (m, 2, 0) = gel_makenum_d (surf_defy1);
3707 	gel_matrixw_set_index (m, 3, 0) = gel_makenum_d (surf_defy2);
3708 	gel_matrixw_set_index (m, 4, 0) = gel_makenum_d (surf_defz1);
3709 	gel_matrixw_set_index (m, 5, 0) = gel_makenum_d (surf_defz2);
3710 
3711 	return n;
3712 }
3713 
3714 static gboolean
get_ticks_from_matrix(GelETree * m,int * v,int * h)3715 get_ticks_from_matrix (GelETree *m, int *v, int *h)
3716 {
3717 	GelETree *t;
3718 
3719 	if (m->type == GEL_VALUE_NODE) {
3720 		long n = mpw_get_long (m->val.value);
3721 		if G_UNLIKELY (gel_error_num != 0) {
3722 			gel_error_num = 0;
3723 			return FALSE;
3724 		}
3725 		if (n <= 1 || n > 200) {
3726 			gel_errorout (_("Ticks must be between 2 and 200"));
3727 			return FALSE;
3728 		}
3729 		*v = *h = n;
3730 		return TRUE;
3731 	} else if (m->type == GEL_MATRIX_NODE &&
3732 	    gel_matrixw_elements (m->mat.matrix) == 2) {
3733 		t = gel_matrixw_vindex (m->mat.matrix, 0);
3734 		if (t->type != GEL_VALUE_NODE) {
3735 			gel_errorout (_("Ticks not given as numbers"));
3736 			return FALSE;
3737 		}
3738 		*v = mpw_get_long (t->val.value);
3739 		if G_UNLIKELY (gel_error_num != 0) {
3740 			gel_error_num = 0;
3741 			return FALSE;
3742 		}
3743 		if (*v <= 1 || *v > 200) {
3744 			gel_errorout (_("Ticks must be between 2 and 200"));
3745 			return FALSE;
3746 		}
3747 		t = gel_matrixw_vindex (m->mat.matrix, 1);
3748 		if (t->type != GEL_VALUE_NODE) {
3749 			gel_errorout (_("Ticks not given as numbers"));
3750 			return FALSE;
3751 		}
3752 		*h = mpw_get_long (t->val.value);
3753 		if G_UNLIKELY (gel_error_num != 0) {
3754 			gel_error_num = 0;
3755 			return FALSE;
3756 		}
3757 		if (*h <= 1 || *h > 200) {
3758 			gel_errorout (_("Ticks must be between 2 and 200"));
3759 			return FALSE;
3760 		}
3761 		return TRUE;
3762 	} else {
3763 		gel_errorout (_("Ticks not given as a number or a 2-vector"));
3764 		return FALSE;
3765 	}
3766 }
3767 
3768 static GelETree *
make_matrix_from_ticks(int v,int h)3769 make_matrix_from_ticks (int v, int h)
3770 {
3771 	GelETree *n;
3772 	GelMatrixW *m;
3773 	/*make us a new empty node*/
3774 	GEL_GET_NEW_NODE (n);
3775 	n->type = GEL_MATRIX_NODE;
3776 	m = n->mat.matrix = gel_matrixw_new ();
3777 	n->mat.quoted = FALSE;
3778 	gel_matrixw_set_size (m, 2, 1);
3779 
3780 	gel_matrixw_set_indexii (m, 0) = gel_makenum_si (v);
3781 	gel_matrixw_set_index (m, 1, 0) = gel_makenum_si (h);
3782 
3783 	return n;
3784 }
3785 
3786 static gboolean
parametric_get_value(double * x,double * y,double t)3787 parametric_get_value (double *x, double *y, double t)
3788 {
3789 	static int hookrun = 0;
3790 	gboolean ex = FALSE;
3791 
3792 	mpw_set_d (plot_arg->val.value, t);
3793 	if (parametric_func_z != NULL) {
3794 		call_func_z (plot_ctx, parametric_func_z, plot_arg, x, y, &ex, NULL);
3795 	} else {
3796 		*x = call_func (plot_ctx, parametric_func_x, plot_arg, &ex, NULL);
3797 		if G_LIKELY ( ! ex)
3798 			*y = call_func (plot_ctx, parametric_func_y, plot_arg, &ex, NULL);
3799 	}
3800 
3801 	if G_UNLIKELY (ex) {
3802 		*x = 0.0;
3803 		*y = 0.0;
3804 		return FALSE;
3805 	} else {
3806 		if G_UNLIKELY (*y > plot_maxy)
3807 			plot_maxy = *y;
3808 		if G_UNLIKELY (*y < plot_miny)
3809 			plot_miny = *y;
3810 		if G_UNLIKELY (*x > plot_maxx)
3811 			plot_maxx = *x;
3812 		if G_UNLIKELY (*x < plot_minx)
3813 			plot_minx = *x;
3814 	}
3815 	/* FIXME: sanity on x/y ??? */
3816 
3817 	if G_UNLIKELY (hookrun++ >= 10) {
3818 		if (gel_evalnode_hook != NULL) {
3819 			hookrun = 0;
3820 			(*gel_evalnode_hook)();
3821 		}
3822 	}
3823 
3824 	return TRUE;
3825 }
3826 
3827 static GtkPlotData *
draw_line(double * x,double * y,int len,int thickness,GdkRGBA * color,char * legend,gboolean filled)3828 draw_line (double *x, double *y, int len, int thickness, GdkRGBA *color,
3829 	   char *legend, gboolean filled)
3830 {
3831 	GtkPlotData *data;
3832 
3833 	data = GTK_PLOT_DATA (gtk_plot_data_new ());
3834 	gtk_plot_data_set_points (data, x, y, NULL, NULL, len);
3835 	g_object_set_data_full (G_OBJECT (data),
3836 				"x", x, (GDestroyNotify)g_free);
3837 	g_object_set_data_full (G_OBJECT (data),
3838 				"y", y, (GDestroyNotify)g_free);
3839 	gtk_plot_add_data (GTK_PLOT (line_plot), data);
3840 	if (legend == NULL)
3841 		gtk_plot_data_hide_legend (data);
3842 	else
3843 		gtk_plot_data_set_legend (data,
3844 					  legend);
3845 
3846 	gtk_plot_data_set_line_attributes (data,
3847 					   GTK_PLOT_LINE_SOLID,
3848 					   CAIRO_LINE_CAP_ROUND,
3849 					   CAIRO_LINE_JOIN_ROUND,
3850 					   thickness, color);
3851 
3852 	gtk_plot_data_fill_area (data, filled);
3853 
3854 	gtk_widget_show (GTK_WIDGET (data));
3855 
3856 
3857 	gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
3858 	gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
3859 
3860 	return data;
3861 }
3862 
3863 static GtkPlotData *
draw_points(double * x,double * y,int len,int thickness,GdkRGBA * color,char * legend)3864 draw_points (double *x, double *y, int len, int thickness, GdkRGBA *color,
3865 	     char *legend)
3866 {
3867 	GtkPlotData *data;
3868 
3869 	data = GTK_PLOT_DATA (gtk_plot_data_new ());
3870 	gtk_plot_data_set_points (data, x, y, NULL, NULL, len);
3871 	g_object_set_data_full (G_OBJECT (data),
3872 				"x", x, (GDestroyNotify)g_free);
3873 	g_object_set_data_full (G_OBJECT (data),
3874 				"y", y, (GDestroyNotify)g_free);
3875 	gtk_plot_add_data (GTK_PLOT (line_plot), data);
3876 	if (legend == NULL)
3877 		gtk_plot_data_hide_legend (data);
3878 	else
3879 		gtk_plot_data_set_legend (data,
3880 					  legend);
3881 
3882 	gtk_plot_data_set_line_attributes (data,
3883 					   GTK_PLOT_LINE_SOLID,
3884 					   CAIRO_LINE_CAP_ROUND,
3885 					   CAIRO_LINE_JOIN_ROUND,
3886 					   thickness, color);
3887 
3888 	gtk_plot_data_set_connector (data, GTK_PLOT_CONNECT_NONE);
3889 
3890 	gtk_widget_show (GTK_WIDGET (data));
3891 
3892 	gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
3893 	gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
3894 
3895 	return data;
3896 }
3897 
3898 static GtkPlotData *
draw_surface_line(double * x,double * y,double * z,int len,int thickness,GdkRGBA * color,char * legend)3899 draw_surface_line (double *x, double *y, double *z,
3900 		   int len, int thickness, GdkRGBA *color, char *legend)
3901 {
3902 	GtkPlotData *data;
3903 
3904 	data = GTK_PLOT_DATA (gtk_plot_data_new ());
3905 	gtk_plot_data_set_x (data, x);
3906 	gtk_plot_data_set_y (data, y);
3907 	gtk_plot_data_set_z (data, z);
3908 	gtk_plot_data_set_numpoints (data, len);
3909 	g_object_set_data_full (G_OBJECT (data),
3910 				"x", x, (GDestroyNotify)g_free);
3911 	g_object_set_data_full (G_OBJECT (data),
3912 				"y", y, (GDestroyNotify)g_free);
3913 	g_object_set_data_full (G_OBJECT (data),
3914 				"z", z, (GDestroyNotify)g_free);
3915 	gtk_plot_add_data (GTK_PLOT (surface_plot), data);
3916 	if (legend == NULL)
3917 		gtk_plot_data_hide_legend (data);
3918 	else
3919 		gtk_plot_data_set_legend (data,
3920 					  legend);
3921 
3922 	gtk_plot_data_set_line_attributes (data,
3923 					   GTK_PLOT_LINE_SOLID,
3924 					   CAIRO_LINE_CAP_ROUND,
3925 					   CAIRO_LINE_JOIN_ROUND,
3926 					   thickness, color);
3927 
3928 	gtk_widget_show (GTK_WIDGET (data));
3929 
3930 	gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
3931 	gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
3932 
3933 	return data;
3934 }
3935 
3936 static GtkPlotData *
draw_surface_points(double * x,double * y,double * z,int len,int thickness,GdkRGBA * color,char * legend)3937 draw_surface_points (double *x, double *y, double *z,
3938 		     int len, int thickness, GdkRGBA *color, char *legend)
3939 {
3940 	GtkPlotData *data;
3941 
3942 	data = GTK_PLOT_DATA (gtk_plot_data_new ());
3943 	gtk_plot_data_set_x (data, x);
3944 	gtk_plot_data_set_y (data, y);
3945 	gtk_plot_data_set_z (data, z);
3946 	gtk_plot_data_set_numpoints (data, len);
3947 	g_object_set_data_full (G_OBJECT (data),
3948 				"x", x, (GDestroyNotify)g_free);
3949 	g_object_set_data_full (G_OBJECT (data),
3950 				"y", y, (GDestroyNotify)g_free);
3951 	g_object_set_data_full (G_OBJECT (data),
3952 				"z", z, (GDestroyNotify)g_free);
3953 	gtk_plot_add_data (GTK_PLOT (surface_plot), data);
3954 	if (legend == NULL)
3955 		gtk_plot_data_hide_legend (data);
3956 	else
3957 		gtk_plot_data_set_legend (data,
3958 					  legend);
3959 
3960 	gtk_plot_data_set_line_attributes (data,
3961 					   GTK_PLOT_LINE_SOLID,
3962 					   CAIRO_LINE_CAP_ROUND,
3963 					   CAIRO_LINE_JOIN_ROUND,
3964 					   thickness, color);
3965 
3966 	gtk_plot_data_set_connector (data, GTK_PLOT_CONNECT_NONE);
3967 
3968 	gtk_widget_show (GTK_WIDGET (data));
3969 
3970 	gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
3971 	gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
3972 
3973 	return data;
3974 }
3975 
3976 #if 0
3977 static void
3978 clip_line_ends (double xx[], double yy[], int len)
3979 {
3980 	if G_UNLIKELY (len < 2)
3981 		return;
3982 
3983 	if (xx[0] < plotx1) {
3984 		double slope = (yy[1]-yy[0])/(xx[1]-xx[0]);
3985 		xx[0] = plotx1;
3986 		yy[0] = yy[1] - (xx[1]-plotx1) * slope;
3987 	}
3988 
3989 	if (yy[0] < ploty1) {
3990 		if G_LIKELY (/* sanity */(xx[1]-xx[0]) > 0) {
3991 			double slope = (yy[1]-yy[0])/(xx[1]-xx[0]);
3992 			xx[0] = (ploty1 -yy[1] + slope*xx[1]) / slope;
3993 			yy[0] = ploty1;
3994 		}
3995 	} else if (yy[0] > ploty2) {
3996 		if G_LIKELY (/* sanity */(xx[1]-xx[0]) > 0) {
3997 			double slope = (yy[1]-yy[0])/(xx[1]-xx[0]);
3998 			xx[0] = (ploty2 -yy[1] + slope*xx[1]) / slope;
3999 			yy[0] = ploty2;
4000 		}
4001 	}
4002 
4003 	if (xx[len-1] > plotx2) {
4004 		double slope = (yy[len-1]-yy[len-2])
4005 			/ (xx[len-1]-xx[len-2]);
4006 		xx[len-1] = plotx2;
4007 		yy[len-1] = yy[len-2] + (plotx2-xx[len-2]) * slope;
4008 	}
4009 
4010 	if (yy[len-1] < ploty1) {
4011 		if G_LIKELY (/* sanity */(xx[len-1]-xx[len-2]) > 0) {
4012 			double slope = (yy[len-1]-yy[len-2])/(xx[len-1]-xx[len-2]);
4013 			xx[len-1] = (ploty1 - yy[len-2] + slope*xx[len-2]) / slope;
4014 			yy[len-1] = ploty1;
4015 		}
4016 	} else if (yy[len-1] > ploty2) {
4017 		if G_LIKELY (/* sanity */(xx[len-1]-xx[len-2]) > 0) {
4018 			double slope = (yy[len-1]-yy[len-2])/(xx[len-1]-xx[len-2]);
4019 			xx[len-1] = (ploty2 - yy[len-2] + slope*xx[len-2]) / slope;
4020 			yy[len-1] = ploty2;
4021 		}
4022 	}
4023 }
4024 #endif
4025 
4026 static void
solution_destroyed(GtkWidget * plotdata,gpointer data)4027 solution_destroyed (GtkWidget *plotdata, gpointer data)
4028 {
4029 	solutions_list = g_slist_remove (solutions_list, plotdata);
4030 }
4031 
4032 static void
slopefield_draw_solution(double x,double y,double dx,gboolean is_gui)4033 slopefield_draw_solution (double x, double y, double dx, gboolean is_gui)
4034 {
4035 	double *xx, *yy;
4036 	double cx, cy;
4037 	int len1, len2, len;
4038 	int i;
4039 	GdkRGBA color;
4040 	GQueue points1 = G_QUEUE_INIT;
4041 	GSList *points2 = NULL;
4042 	GList *li;
4043 	GSList *sli;
4044 	GtkPlotData *data;
4045 	double fudgey;
4046 
4047 	if (slopefield_func == NULL)
4048 		return;
4049 
4050 	plot_in_progress ++;
4051 	gel_calc_running ++;
4052 	plot_window_setup ();
4053 
4054 	gdk_rgba_parse (&color, "red");
4055 
4056 	fudgey = (ploty2-ploty1)/100;
4057 
4058 	len1 = 0;
4059 	cx = x;
4060 	cy = y;
4061 	while (cx < plotx2 && cy > ploty1-fudgey && cy < ploty2+fudgey) {
4062 		double *pt;
4063 		gboolean ex = FALSE;
4064 		double k1, k2, k3, k4, sl;
4065 
4066 		/* standard Runge-Kutta */
4067 		k1 = call_xy_or_z_function (slopefield_func,
4068 					    cx, cy, &ex);
4069 		if G_UNLIKELY (ex) break;
4070 		k2 = call_xy_or_z_function (slopefield_func,
4071 					    cx+(dx/2), cy+(dx/2)*k1, &ex);
4072 		if G_UNLIKELY (ex) break;
4073 		k3 = call_xy_or_z_function (slopefield_func,
4074 					    cx+(dx/2), cy+(dx/2)*k2, &ex);
4075 		if G_UNLIKELY (ex) break;
4076 		k4 = call_xy_or_z_function (slopefield_func,
4077 					    cx+dx, cy+dx*k3, &ex);
4078 		if G_UNLIKELY (ex) break;
4079 
4080 		sl = (k1+2*k2+2*k3+k4)/6.0;
4081 
4082 		cy += sl * dx;
4083 		cx += dx;
4084 
4085 		len1 ++;
4086 
4087 		pt = g_new (double, 2);
4088 		pt[0] = cx;
4089 		pt[1] = cy;
4090 
4091 		g_queue_push_tail (&points1, pt);
4092 	}
4093 
4094 	len2 = 0;
4095 	cx = x;
4096 	cy = y;
4097 	while (cx > plotx1 && cy > ploty1-fudgey && cy < ploty2+fudgey) {
4098 		double *pt;
4099 		gboolean ex = FALSE;
4100 		double k1, k2, k3, k4, sl;
4101 
4102 		/* standard Runge-Kutta */
4103 		k1 = call_xy_or_z_function (slopefield_func,
4104 					    cx, cy, &ex);
4105 		if G_UNLIKELY (ex) break;
4106 		k2 = call_xy_or_z_function (slopefield_func,
4107 					    cx-(dx/2), cy-(dx/2)*k1, &ex);
4108 		if G_UNLIKELY (ex) break;
4109 		k3 = call_xy_or_z_function (slopefield_func,
4110 					    cx-(dx/2), cy-(dx/2)*k2, &ex);
4111 		if G_UNLIKELY (ex) break;
4112 		k4 = call_xy_or_z_function (slopefield_func,
4113 					    cx-dx, cy-dx*k3, &ex);
4114 		if G_UNLIKELY (ex) break;
4115 
4116 		sl = (k1+2*k2+2*k3+k4)/6.0;
4117 
4118 		cy -= sl* dx;
4119 		cx -= dx;
4120 
4121 		len2 ++;
4122 
4123 		pt = g_new (double, 2);
4124 		pt[0] = cx;
4125 		pt[1] = cy;
4126 
4127 		points2 = g_slist_prepend (points2, pt);
4128 	}
4129 
4130 	len = len1 + 1 + len2;
4131 	xx = g_new0 (double, len);
4132 	yy = g_new0 (double, len);
4133 
4134 	i = 0;
4135 	for (sli = points2; sli != NULL; sli = sli->next) {
4136 		double *pt = sli->data;
4137 		sli->data = NULL;
4138 
4139 		xx[i] = pt[0];
4140 		yy[i] = pt[1];
4141 
4142 		g_free (pt);
4143 
4144 		i++;
4145 	}
4146 
4147 	xx[i] = x;
4148 	yy[i] = y;
4149 
4150 	i++;
4151 
4152 	for (li = points1.head; li != NULL; li = li->next) {
4153 		double *pt = li->data;
4154 		li->data = NULL;
4155 
4156 		xx[i] = pt[0];
4157 		yy[i] = pt[1];
4158 
4159 		g_free (pt);
4160 
4161 		i++;
4162 	}
4163 
4164 	g_queue_clear (&points1);
4165 	g_slist_free (points2);
4166 
4167 	/* Adjust ends */
4168 	/*clip_line_ends (xx, yy, len);*/
4169 
4170 	data = draw_line (xx, yy, len,
4171 			  2 /* thickness */,
4172 			  &color,
4173 			  NULL /* legend */,
4174 			  FALSE /* filled */);
4175 	solutions_list = g_slist_prepend (solutions_list,
4176 					  data);
4177 	g_signal_connect (G_OBJECT (data), "destroy",
4178 			  G_CALLBACK (solution_destroyed), NULL);
4179 
4180 	if (is_gui && gel_interrupted)
4181 		gel_interrupted = FALSE;
4182 
4183 	plot_in_progress --;
4184 	gel_calc_running --;
4185 	plot_window_setup ();
4186 }
4187 
4188 static void
vectorfield_draw_solution(double x,double y,double dt,double tlen,gboolean is_gui)4189 vectorfield_draw_solution (double x, double y, double dt, double tlen, gboolean is_gui)
4190 {
4191 	double *xx, *yy;
4192 	double cx, cy, t;
4193 	int len;
4194 	int i;
4195 	GdkRGBA color;
4196 	GtkPlotData *data;
4197 	gboolean ex;
4198 
4199 	if (vectorfield_func_x == NULL ||
4200 	    vectorfield_func_y == NULL ||
4201 	    dt <= 0.0 ||
4202 	    tlen <= 0.0)
4203 		return;
4204 
4205 	plot_in_progress ++;
4206 	gel_calc_running ++;
4207 	plot_window_setup ();
4208 
4209 	gdk_rgba_parse (&color, "red");
4210 
4211 	len = (int)(tlen / dt) + 2;
4212 	xx = g_new0 (double, len);
4213 	yy = g_new0 (double, len);
4214 
4215 	i = 1;
4216 	xx[0] = x;
4217 	yy[0] = y;
4218 	cx = x;
4219 	cy = y;
4220 	t = 0.0;
4221 	while (t < tlen && i < len) {
4222 		double xk1, xk2, xk3, xk4, xsl;
4223 		double yk1, yk2, yk3, yk4, ysl;
4224 
4225 		ex = FALSE;
4226 		/* standard Runge-Kutta */
4227 		xk1 = call_xy_or_z_function (vectorfield_func_x,
4228 					     cx, cy, &ex);
4229 		if G_UNLIKELY (ex) break;
4230 		yk1 = call_xy_or_z_function (vectorfield_func_y,
4231 					     cx, cy, &ex);
4232 		if G_UNLIKELY (ex) break;
4233 
4234 		xk2 = call_xy_or_z_function (vectorfield_func_x,
4235 					     cx+(dt/2)*xk1, cy+(dt/2)*yk1, &ex);
4236 		if G_UNLIKELY (ex) break;
4237 		yk2 = call_xy_or_z_function (vectorfield_func_y,
4238 					     cx+(dt/2)*xk1, cy+(dt/2)*yk1, &ex);
4239 		if G_UNLIKELY (ex) break;
4240 
4241 		xk3 = call_xy_or_z_function (vectorfield_func_x,
4242 					     cx+(dt/2)*xk2, cy+(dt/2)*yk2, &ex);
4243 		if G_UNLIKELY (ex) break;
4244 		yk3 = call_xy_or_z_function (vectorfield_func_y,
4245 					     cx+(dt/2)*xk2, cy+(dt/2)*yk2, &ex);
4246 		if G_UNLIKELY (ex) break;
4247 
4248 		xk4 = call_xy_or_z_function (vectorfield_func_x,
4249 					     cx+dt*xk3, cy+dt*yk3, &ex);
4250 		if G_UNLIKELY (ex) break;
4251 		yk4 = call_xy_or_z_function (vectorfield_func_y,
4252 					     cx+dt*xk3, cy+dt*yk3, &ex);
4253 		if G_UNLIKELY (ex) break;
4254 
4255 		xsl = (xk1+2*xk2+2*xk3+xk4)/6.0;
4256 		ysl = (yk1+2*yk2+2*yk3+yk4)/6.0;
4257 
4258 		cx += xsl * dt;
4259 		cy += ysl * dt;
4260 
4261 		xx[i] = cx;
4262 		yy[i] = cy;
4263 
4264 		i ++;
4265 		t += dt;
4266 	}
4267 
4268 	len = i;
4269 
4270 	data = draw_line (xx, yy, len,
4271 			  2 /* thickness */,
4272 			  &color,
4273 			  NULL /* legend */,
4274 			  FALSE /* filled */);
4275 	solutions_list = g_slist_prepend (solutions_list,
4276 					  data);
4277 	g_signal_connect (G_OBJECT (data), "destroy",
4278 			  G_CALLBACK (solution_destroyed), NULL);
4279 
4280 	if (is_gui && gel_interrupted)
4281 		gel_interrupted = FALSE;
4282 
4283 	plot_in_progress --;
4284 	gel_calc_running--;
4285 	plot_window_setup ();
4286 }
4287 
4288 
4289 static void
replot_fields(void)4290 replot_fields (void)
4291 {
4292 	init_var_names ();
4293 
4294 	if (slopefield_func != NULL) {
4295 		get_slopefield_points ();
4296 		if (plot_points_num > 0) {
4297 			GdkRGBA color;
4298 
4299 			if (slopefield_data == NULL) {
4300 				char *label, *tmp;
4301 
4302 				slopefield_data = GTK_PLOT_DATA(gtk_plot_flux_new());
4303 				gtk_plot_add_data (GTK_PLOT (line_plot),
4304 						   slopefield_data);
4305 				gdk_rgba_parse (&color, "blue");
4306 				gtk_plot_data_set_line_attributes
4307 					(slopefield_data,
4308 					 GTK_PLOT_LINE_NONE,
4309 					 CAIRO_LINE_CAP_ROUND,
4310 					 CAIRO_LINE_JOIN_ROUND,
4311 					 1 /* thickness */,
4312 					 &color);
4313 				gtk_plot_data_set_symbol (slopefield_data,
4314 							  GTK_PLOT_SYMBOL_NONE /* symbol type? */,
4315 							  GTK_PLOT_SYMBOL_EMPTY /* symbol style */,
4316 							  1 /* size? */,
4317 							  (plotHtick > 15 || plotVtick > 15) ? 1 : 2
4318 							    /* line_width */,
4319 							  &color /* color */,
4320 							  &color /* border_color? */);
4321 
4322 
4323 				gtk_plot_flux_set_arrow (GTK_PLOT_FLUX (slopefield_data),
4324 							 0, 0, GTK_PLOT_SYMBOL_EMPTY);
4325 
4326 				gtk_plot_flux_show_scale (GTK_PLOT_FLUX (slopefield_data), FALSE);
4327 
4328 				tmp = g_strdup_printf ("%s,%s",
4329 						       lp_x_name,
4330 						       lp_y_name);
4331 				label = label_func (-1, slopefield_func, tmp, slopefield_name);
4332 				g_free (tmp);
4333 				/* FIXME: gtkextra is broken (adding the "  ")
4334 				 * and I don't feel like fixing it */
4335 				/* add dy/dx = */
4336 				tmp = g_strconcat ("d",
4337 						   lp_y_name,
4338 						   "/d",
4339 						   lp_x_name,
4340 						   " = ", label, "  ", NULL);
4341 				g_free (label);
4342 				gtk_plot_data_set_legend (slopefield_data, tmp);
4343 				g_free (tmp);
4344 
4345 				gtk_widget_show (GTK_WIDGET (slopefield_data));
4346 			}
4347 			gtk_plot_data_set_points (slopefield_data,
4348 						  plot_points_x,
4349 						  plot_points_y,
4350 						  plot_points_dx,
4351 						  plot_points_dy,
4352 						  plot_points_num);
4353 		}
4354 
4355 		/* sanity */
4356 		g_return_if_fail (vectorfield_func_x == NULL && vectorfield_func_x == NULL);
4357 	}
4358 
4359 	if (vectorfield_func_x != NULL && vectorfield_func_y != NULL) {
4360 		get_vectorfield_points ();
4361 		if (plot_points_num > 0) {
4362 			GdkRGBA color;
4363 
4364 			if (vectorfield_data == NULL) {
4365 				char *l1, *l2, *tmp;
4366 
4367 				vectorfield_data = GTK_PLOT_DATA(gtk_plot_flux_new());
4368 				gtk_plot_add_data (GTK_PLOT (line_plot),
4369 						   vectorfield_data);
4370 				gdk_rgba_parse (&color, "blue");
4371 				gtk_plot_data_set_line_attributes
4372 					(vectorfield_data,
4373 					 GTK_PLOT_LINE_NONE,
4374 					 CAIRO_LINE_CAP_ROUND,
4375 					 CAIRO_LINE_JOIN_ROUND,
4376 					 1 /* thickess */,
4377 					 &color);
4378 				gtk_plot_data_set_symbol (vectorfield_data,
4379 							  GTK_PLOT_SYMBOL_NONE /* symbol type? */,
4380 							  GTK_PLOT_SYMBOL_EMPTY /* symbol style */,
4381 							  1 /* size? */,
4382 							  (plotHtick > 15 || plotVtick > 15) ? 1 : 2
4383 							    /* line_width */,
4384 							  &color /* color */,
4385 							  &color /* border_color? */);
4386 
4387 
4388 				gtk_plot_flux_set_arrow (GTK_PLOT_FLUX (vectorfield_data),
4389 							 6, 6, GTK_PLOT_SYMBOL_EMPTY);
4390 
4391 				gtk_plot_flux_show_scale (GTK_PLOT_FLUX (vectorfield_data), FALSE);
4392 
4393 				tmp = g_strdup_printf ("%s,%s",
4394 						       lp_x_name,
4395 						       lp_y_name);
4396 				l1 = label_func (-1, vectorfield_func_x, tmp, vectorfield_name_x);
4397 				l2 = label_func (-1, vectorfield_func_y, tmp, vectorfield_name_y);
4398 				g_free (tmp);
4399 				/* FIXME: gtkextra is broken (adding the "  ")
4400 				 * and I don't feel like fixing it */
4401 				/*tmp = g_strconcat ("dx/dt = ", l1, ",  dy/dt = ", l2, "  ", NULL);*/
4402 				tmp = g_strconcat ("d",
4403 						   lp_x_name,
4404 						   "/d",
4405 						   lp_t_name,
4406 						   " = ", l1, ",  d",
4407 						   lp_y_name,
4408 						   "/d",
4409 						   lp_t_name,
4410 						   " = ", l2,
4411 						   "  ", NULL);
4412 				g_free (l1);
4413 				g_free (l2);
4414 				gtk_plot_data_set_legend (vectorfield_data, tmp);
4415 				g_free (tmp);
4416 
4417 				gtk_widget_show (GTK_WIDGET (vectorfield_data));
4418 			}
4419 			gtk_plot_data_set_points (vectorfield_data,
4420 						  plot_points_x,
4421 						  plot_points_y,
4422 						  plot_points_dx,
4423 						  plot_points_dy,
4424 						  plot_points_num);
4425 		}
4426 	}
4427 
4428 }
4429 
4430 static void
init_plot_ctx(void)4431 init_plot_ctx (void)
4432 {
4433 	if G_UNLIKELY (plot_ctx == NULL) {
4434 		mpw_t xx;
4435 
4436 		plot_ctx = gel_eval_get_context ();
4437 
4438 		mpw_init (xx);
4439 		plot_arg = gel_makenum_use (xx);
4440 
4441 		mpw_init (xx);
4442 		plot_arg2 = gel_makenum_use (xx);
4443 
4444 		mpw_init (xx);
4445 		plot_arg3 = gel_makenum_use (xx);
4446 	}
4447 }
4448 
4449 typedef struct {
4450 	double x;
4451 	double y;
4452 } Point;
4453 
4454 #ifdef NAN
4455 # define BADPTVAL NAN
4456 #else
4457 #ifdef INFINITY
4458 # define BADPTVAL INFINITY
4459 #else
4460 # define BADPTVAL (1.0/0.0)
4461 #endif
4462 #endif
4463 
4464 static Point *
function_get_us_a_point(int funci,double x)4465 function_get_us_a_point (int funci, double x)
4466 {
4467 	gboolean ex = FALSE;
4468 	double rety;
4469 	Point *pt = g_new0 (Point, 1);
4470 	mpw_set_d (plot_arg->val.value, x);
4471 	rety = call_func (plot_ctx, plot_func[funci], plot_arg, &ex, NULL);
4472 
4473 	if G_UNLIKELY (ex) {
4474 		pt->x = x;
4475 		pt->y = BADPTVAL;
4476 	} else {
4477 		pt->x = x;
4478 		pt->y = rety;
4479 
4480 		if G_UNLIKELY (rety > plot_maxy)
4481 			plot_maxy = rety;
4482 		if G_UNLIKELY (rety < plot_miny)
4483 			plot_miny = rety;
4484 	}
4485 
4486 	return pt;
4487 }
4488 
4489 
4490 static double
approx_arcsin(double x)4491 approx_arcsin(double x)
4492 {
4493 	/* Pade quotient approximation, see
4494 	 * http://www.ecse.rpi.edu/~wrf/Research/Short_Notes/arcsin/onlyelem.html*/
4495 	return ((-17.0/60.0)*x*x*x+x)/(1.0-(9.0/20.0)*x*x);
4496 }
4497 
4498 
4499 static void
bisect_points(int funci,GQueue * points,GList * li,double sizex,double sizey,int level,int * count)4500 bisect_points (int funci, GQueue *points, GList *li, double sizex, double sizey, int level, int *count)
4501 {
4502 	Point *pt;
4503 	Point *nextpt;
4504 	Point *prevpt;
4505 	Point *newpt;
4506 	double xdiffscaled;
4507 	double ydiffscaled;
4508 	double xprevdiffscaled;
4509 	double yprevdiffscaled;
4510 	double seglensq;
4511 	gboolean do_bisect = FALSE;
4512 	gboolean bisect_prev = FALSE;
4513 	gboolean bisect_next = FALSE;
4514 
4515 	pt = li->data;
4516 	nextpt = li->next->data;
4517 
4518 	if ( ! isfinite (nextpt->y) || ! isfinite(pt->y))
4519 		return;
4520 
4521 	if (li->prev) {
4522 		prevpt = li->prev->data;
4523 		if ( ! isfinite (prevpt->y))
4524 			prevpt = NULL;
4525 	} else {
4526 		prevpt = NULL;
4527 	}
4528 
4529 	xdiffscaled = (nextpt->x-pt->x)/sizex;
4530 	ydiffscaled = (nextpt->y-pt->y)/sizey;
4531 
4532 	seglensq = xdiffscaled*xdiffscaled+ydiffscaled*ydiffscaled;
4533 
4534 	/* lines of size 1% are fine */
4535 	if (seglensq >= 0.01*0.01) {
4536 		do_bisect = TRUE;
4537 		bisect_next = TRUE;
4538 	} else if (prevpt != NULL) {
4539 		double seglen1sq;
4540 		xprevdiffscaled = (pt->x-prevpt->x)/sizex;
4541 		yprevdiffscaled = (pt->y-prevpt->y)/sizey;
4542 
4543 		seglen1sq = xprevdiffscaled*xprevdiffscaled+yprevdiffscaled*yprevdiffscaled;
4544 
4545 		/* difference of angles is bigger than approx 0.1 radians */
4546 		if (fabs (approx_arcsin (yprevdiffscaled/sqrt(seglen1sq)) - approx_arcsin (ydiffscaled/sqrt(seglensq))) > 0.1) {
4547 			do_bisect = TRUE;
4548 			bisect_prev = TRUE;
4549 		}
4550 	}
4551 
4552 
4553 	if (do_bisect) {
4554 		(*count)++;
4555 		newpt = function_get_us_a_point (funci, pt->x + (nextpt->x-pt->x)/2.0);
4556 		g_queue_insert_after (points, li, newpt);
4557 
4558 		if (level < 3) {
4559 			GList *linext = li->next;
4560 			if (bisect_prev)
4561 				bisect_points (funci, points, li->prev, sizex, sizey, level+1, count);
4562 			bisect_points (funci, points, li, sizex, sizey, level+1, count);
4563 			if (bisect_next)
4564 				bisect_points (funci, points, linext, sizex, sizey, level+1, count);
4565 		}
4566 	}
4567 
4568 
4569 }
4570 
4571 static void
recompute_function(int funci,double ** x,double ** y,int * len,gboolean fitting)4572 recompute_function (int funci, double **x, double **y, int *len, gboolean fitting)
4573 {
4574 	int i, count, lentried;
4575 	double maxfuzz;
4576 	double fuzz[16];
4577 	GQueue *points = g_queue_new ();
4578 	GList *li;
4579 	double sizex, sizey;
4580 	double tmpploty1, tmpploty2;
4581 
4582 	lentried = WIDTH/2;/* FIXME: perhaps settable */
4583 
4584 	/* up to 1% of the interval is fuzzed */
4585 	maxfuzz = 0.01*(plotx2-plotx1)/(lentried-1);
4586 	for (i = 0; i < 16; i++) {
4587 		fuzz[i] = g_random_double_range (-maxfuzz, maxfuzz);
4588 	}
4589 
4590 	count = 0;
4591 	for (i = 0; i < lentried; i++) {
4592 		static int hookrun = 0;
4593 		double thex;
4594 		Point *pt;
4595 
4596 		if G_UNLIKELY (gel_interrupted) {
4597 			break;
4598 		}
4599 
4600 		thex = plotx1 + ((plotx2-plotx1)*(double)i)/(lentried-1) +
4601 			fuzz[i&0xf];
4602 		/* don't fuzz beyond the domain */
4603 		if (thex < plotx1)
4604 			thex = plotx1;
4605 		else if (thex > plotx2)
4606 			thex = plotx2;
4607 
4608 		count++;
4609 		pt = function_get_us_a_point (funci, thex);
4610 		g_queue_push_tail (points, pt);
4611 
4612 		if G_UNLIKELY (hookrun++ >= 10) {
4613 			if (gel_evalnode_hook != NULL) {
4614 				hookrun = 0;
4615 				(*gel_evalnode_hook)();
4616 				if G_UNLIKELY (gel_interrupted) {
4617 					break;
4618 				}
4619 			}
4620 		}
4621 	}
4622 
4623 	if (fitting) {
4624 		sizey = plot_maxy - plot_miny;
4625 		if (sizey <= 0.0)
4626 			sizey = 0.01;
4627 		tmpploty1 = plot_miny - 0.05*sizey;
4628 		tmpploty2 = plot_maxy + 0.05*sizey;
4629 
4630 		sizey *= 1.05 * sizey;
4631 	} else {
4632 		sizey = ploty2 - ploty1;
4633 		tmpploty1 = ploty1;
4634 		tmpploty2 = ploty2;
4635 	}
4636 	sizex = plotx2 - plotx1;
4637 
4638 	/* sanity */
4639 	if (sizey <= 0.0)
4640 		sizey = 0.01;
4641 	if (sizex <= 0.0)
4642 		sizex = 0.01;
4643 
4644 
4645 	/* adaptively bisect intervals */
4646 	li = g_queue_peek_head_link (points);
4647 	while (li != NULL && li->next != NULL) {
4648 		Point *pt = li->data;
4649 		GList *orignext = li->next;
4650 		Point *nextpt = orignext->data;
4651 
4652 		if ((pt->y < tmpploty1-0.5*sizey &&
4653 		     nextpt->y < tmpploty1-0.5*sizey) ||
4654 		    (pt->y > tmpploty2+0.5*sizey &&
4655 		     nextpt->y > tmpploty2+0.5*sizey)) {
4656 			li = orignext;
4657 			continue;
4658 		}
4659 
4660 		bisect_points (funci, points, li, sizex, sizey, 1, &count);
4661 		li = orignext;
4662 	}
4663 
4664 	/* find "steep jumps" and insert invalid points */
4665 	li = g_queue_peek_head_link (points);
4666 	while (li != NULL && li->next != NULL) {
4667 		Point *pt = li->data;
4668 		Point *nextpt = li->next->data;
4669 		GList *orignext = li->next;
4670 		double xdiffscaled;
4671 		double ydiffscaled;
4672 
4673 		if ( ! isfinite (nextpt->y) || ! isfinite(pt->y)) {
4674 			li = orignext;
4675 			continue;
4676 		}
4677 
4678 		xdiffscaled = (nextpt->x-pt->x)/sizex;
4679 		ydiffscaled = (nextpt->y-pt->y)/sizey;
4680 
4681 		/* derivative at least 100 after scaling, length bigger than 1% */
4682 		if (100.0*fabs(xdiffscaled) < fabs(ydiffscaled) &&
4683 		    xdiffscaled*xdiffscaled+ydiffscaled*ydiffscaled > 0.01*0.01 &&
4684 		    li->next->next != NULL && li->prev != NULL) {
4685 			Point *prevpt = li->prev->data;
4686 			Point *nextnextpt = li->next->next->data;
4687 			double xnextdiffscaled;
4688 			double ynextdiffscaled;
4689 			double xprevdiffscaled;
4690 			double yprevdiffscaled;
4691 
4692 			xnextdiffscaled = (nextnextpt->x-nextpt->x)/sizex;
4693 			ynextdiffscaled = (nextnextpt->y-nextpt->y)/sizey;
4694 
4695 			xprevdiffscaled = (pt->x-prevpt->x)/sizex;
4696 			yprevdiffscaled = (pt->y-prevpt->y)/sizey;
4697 
4698 
4699 			/* too steep! and steeper than surrounding which is derivative at most 10,
4700 			 * or if the prev and next derivatives are of different sign */
4701 			if ( (10.0*fabs(xprevdiffscaled) >= fabs(yprevdiffscaled) ||
4702 			      (ydiffscaled > 0.0 && yprevdiffscaled < 0.0) ||
4703 			      (ydiffscaled < 0.0 && yprevdiffscaled > 0.0) )
4704 			     &&
4705 			     (10.0*fabs(xnextdiffscaled) >= fabs(ynextdiffscaled) ||
4706 			      (ydiffscaled > 0.0 && ynextdiffscaled < 0.0) ||
4707 			      (ydiffscaled < 0.0 && ynextdiffscaled > 0.0) )
4708 			     ) {
4709 				Point *newpt;
4710 				newpt = g_new0 (Point, 1);
4711 				newpt->x = BADPTVAL;
4712 				newpt->y = BADPTVAL;
4713 
4714 				g_queue_insert_after (points, li, newpt);
4715 				count++;
4716 			}
4717 		};
4718 		li = orignext;
4719 	}
4720 
4721 	*len = count;
4722 	*x = g_new0 (double, count);
4723 	*y = g_new0 (double, count);
4724 	i = 0;
4725 	for (li = g_queue_peek_head_link (points); li != NULL; li = li->next) {
4726 		Point *pt = li->data;
4727 		li->data = NULL;
4728 
4729 		(*x)[i] = pt->x;
4730 		(*y)[i] = pt->y;
4731 		i++;
4732 
4733 		g_free (pt);
4734 	}
4735 
4736 	g_queue_free (points);
4737 }
4738 
4739 #if 0
4740 static double
4741 get_maxes(double **y, int len, int place)
4742 {
4743 	double max = 0.0;
4744 	int i;
4745 
4746 	for (i = MAX(place-5,1); i < place; i++) {
4747 		double diff = fabs(y[i]-y[i-1]);
4748 		if (diff > max)
4749 			max = diff;
4750 	}
4751 
4752 	for (i = place+1; i <= MIN(place+5,len-2); i++) {
4753 		double diff = fabs(y[i]-y[i+1]);
4754 		if (diff > max)
4755 			max = diff;
4756 	}
4757 	return max;
4758 }
4759 
4760 /* insert invalid points and places where things jump way too much,
4761  * FIXME: we should make the step smaller adaptively I think */
4762 static void
4763 cutup_function (double **x, double **y, int *len)
4764 {
4765 	double *oldx = *x;
4766 	double *oldy = *y;
4767 	int oldlen = *len;
4768 
4769 	*x = g_new0 (double, *len);
4770 	*y = g_new0 (double, *len);
4771 
4772 	for (i
4773 }
4774 #endif
4775 
4776 
4777 static void
recompute_functions(gboolean fitting)4778 recompute_functions (gboolean fitting)
4779 {
4780 	int i;
4781 	for (i = 0; i < MAXFUNC && plot_func[i] != NULL; i++) {
4782 		double *x, *y;
4783 		int len;
4784 		recompute_function (i, &x, &y, &len, fitting);
4785 
4786 		gtk_plot_data_set_points (line_data[i], x, y, NULL, NULL, len);
4787 		g_object_set_data_full (G_OBJECT (line_data[i]),
4788 					"x", x, (GDestroyNotify)g_free);
4789 		g_object_set_data_full (G_OBJECT (line_data[i]),
4790 					"y", y, (GDestroyNotify)g_free);
4791 	}
4792 }
4793 
4794 static void
recompute_surface_function(gboolean fitting)4795 recompute_surface_function (gboolean fitting)
4796 {
4797 	gboolean error = FALSE;
4798 	double x, y;
4799 	int i, j, n;
4800 
4801 	/* only if plotting a function do we need to reset the min/max */
4802 	plot_maxz = - G_MAXDOUBLE/2;
4803 	plot_minz = G_MAXDOUBLE/2;
4804 
4805 	if (surface_data_x != NULL) g_free (surface_data_x);
4806 	if (surface_data_y != NULL) g_free (surface_data_y);
4807 	if (surface_data_z != NULL) g_free (surface_data_z);
4808 	surface_data_x = (double *)g_malloc((31*31 + 1) * sizeof(double));
4809 	surface_data_y = (double *)g_malloc((31*31 + 1) * sizeof(double));
4810 	surface_data_z = (double *)g_malloc((31*31 + 1) * sizeof(double));
4811 
4812 	/* FIXME: 30 should be configurable! */
4813 	n = 0;
4814 	for (j = 0; j <= 30; j++) {
4815 		if (j < 30)
4816 			y = surfacey1 + (j*(surfacey2-surfacey1))/30.0;
4817 		else
4818 			y = surfacey2;
4819 		for (i = 0; i <= 30; i++) {
4820 			if (i < 30)
4821 				x = surfacex1 + (i*(surfacex2-surfacex1))/30.0;
4822 			else
4823 				x = surfacex2;
4824 
4825 			surface_data_x[n] = x;
4826 			surface_data_y[n] = y;
4827 			surface_data_z[n] = surface_func_data (x, y, &error);
4828 			n++;
4829 		}
4830 	}
4831 
4832 	surface_data_len = n;
4833 
4834 	if (fitting) {
4835 		double size = plot_maxz - plot_minz;
4836 		if (size <= 0)
4837 			size = 1.0;
4838 		surfacez1 = plot_minz - size * 0.05;
4839 		surfacez2 = plot_maxz + size * 0.05;
4840 
4841 		/* sanity */
4842 		if (surfacez2 <= surfacez1)
4843 			surfacez2 = surfacez1 + 0.1;
4844 
4845 		/* sanity */
4846 		if (surfacez1 < -(G_MAXDOUBLE/2))
4847 			surfacez1 = -(G_MAXDOUBLE/2);
4848 		if (surfacez2 > (G_MAXDOUBLE/2))
4849 			surfacez2 = (G_MAXDOUBLE/2);
4850 	}
4851 
4852 	if (surface_data != NULL) {
4853 		gtk_plot_data_set_x (GTK_PLOT_DATA (surface_data), surface_data_x);
4854 		gtk_plot_data_set_y (GTK_PLOT_DATA (surface_data), surface_data_y);
4855 		gtk_plot_data_set_z (GTK_PLOT_DATA (surface_data), surface_data_z);
4856 		gtk_plot_data_set_numpoints (GTK_PLOT_DATA (surface_data), surface_data_len);
4857 	}
4858 }
4859 
4860 static void
plot_functions(gboolean do_window_present,gboolean from_gui,gboolean fit)4861 plot_functions (gboolean do_window_present,
4862 		gboolean from_gui,
4863 		gboolean fit)
4864 {
4865 	const char *colors[] = {
4866 		"darkblue",
4867 		"darkgreen",
4868 		"darkred",
4869 		"magenta",
4870 		"black",
4871 		"darkorange",
4872 		"blue",
4873 		"green",
4874 		"red",
4875 		"brown",
4876 		"yellow", /* should never get here, but just for sanity */
4877 		"orange",
4878 		NULL };
4879 	int i;
4880 	int color_i;
4881 
4882 	init_var_names ();
4883 
4884 	ensure_window (do_window_present);
4885 
4886 	if (plot_canvas != NULL /* sanity */)
4887 		gtk_plot_canvas_freeze (GTK_PLOT_CANVAS (plot_canvas));
4888 
4889 	clear_graph ();
4890 
4891 	add_line_plot ();
4892 
4893 	plot_in_progress ++;
4894 	gel_calc_running ++;
4895 	plot_window_setup ();
4896 	gtk_plot_freeze (GTK_PLOT (line_plot));
4897 
4898 	/* sanity */
4899 	if (plotx2 < plotx1) {
4900 		double t = plotx2;
4901 		plotx2 = plotx1;
4902 		plotx1 = t;
4903 	}
4904 	if (ploty2 < ploty1) {
4905 		double t = ploty2;
4906 		ploty2 = ploty1;
4907 		ploty1 = t;
4908 	}
4909 
4910 	/* sanity */
4911 	if (plotx2 - plotx1 < MINPLOT)
4912 		plotx2 = plotx1 + MINPLOT;
4913 	/* sanity */
4914 	if (ploty2 - ploty1  < MINPLOT)
4915 		ploty2 = ploty1 + MINPLOT;
4916 
4917 	plot_maxy = - G_MAXDOUBLE/2;
4918 	plot_miny = G_MAXDOUBLE/2;
4919 	plot_maxx = - G_MAXDOUBLE/2;
4920 	plot_minx = G_MAXDOUBLE/2;
4921 
4922 	init_plot_ctx ();
4923 
4924 	if (gel_evalnode_hook != NULL)
4925 		(*gel_evalnode_hook)();
4926 
4927 	color_i = 0;
4928 
4929 	for (i = 0; i < MAXFUNC && plot_func[i] != NULL; i++) {
4930 		GdkRGBA color;
4931 		char *label;
4932 
4933 		line_data[i] = GTK_PLOT_DATA (gtk_plot_data_new ());
4934 		gtk_plot_add_data (GTK_PLOT (line_plot),
4935 				   line_data[i]);
4936 
4937 		gtk_widget_show (GTK_WIDGET (line_data[i]));
4938 
4939 		gdk_rgba_parse (&color, colors[color_i++]);
4940 		gtk_plot_data_set_line_attributes (line_data[i],
4941 						   GTK_PLOT_LINE_SOLID,
4942 						   CAIRO_LINE_CAP_ROUND,
4943 						   CAIRO_LINE_JOIN_ROUND,
4944 						   2, &color);
4945 
4946 		label = label_func (i, plot_func[i],
4947 				    lp_x_name,
4948 				    plot_func_name[i]);
4949 		gtk_plot_data_set_legend (line_data[i], label);
4950 		g_free (label);
4951 	}
4952 
4953 	recompute_functions (fit);
4954 
4955 	if (plot_func[0] != NULL && fit) {
4956 		double size = plot_maxy - plot_miny;
4957 		if (size <= 0)
4958 			size = 1.0;
4959 		ploty1 = plot_miny - size * 0.05;
4960 		ploty2 = plot_maxy + size * 0.05;
4961 
4962 		/* sanity */
4963 		if (ploty2 <= ploty1)
4964 			ploty2 = ploty1 + 0.1;
4965 
4966 		/* sanity */
4967 		if (ploty1 < -(G_MAXDOUBLE/2))
4968 			ploty1 = -(G_MAXDOUBLE/2);
4969 		if (ploty2 > (G_MAXDOUBLE/2))
4970 			ploty2 = (G_MAXDOUBLE/2);
4971 	}
4972 
4973 
4974 	if ((parametric_func_x != NULL && parametric_func_y != NULL) ||
4975 	    (parametric_func_z != NULL)) {
4976 		GdkRGBA color;
4977 		char *label;
4978 		int len;
4979 		double *x, *y;
4980 		double t;
4981 
4982 		parametric_data = GTK_PLOT_DATA (gtk_plot_data_new ());
4983 
4984 		/* could be one off, will adjust later */
4985 		len = MAX(ceil (((plott2 - plott1) / plottinc)) + 2,1);
4986 		x = g_new0 (double, len);
4987 		y = g_new0 (double, len);
4988 
4989 		t = plott1;
4990 		for (i = 0; i < len; i++) {
4991 			parametric_get_value (&(x[i]), &(y[i]), t);
4992 
4993 			if G_UNLIKELY (gel_interrupted) {
4994 				break;
4995 			}
4996 
4997 			t = t + plottinc;
4998 			if (t >= plott2) {
4999 				i++;
5000 				parametric_get_value (&(x[i]), &(y[i]), plott2);
5001 				i++;
5002 				break;
5003 			}
5004 		}
5005 		/* how many actually went */
5006 		len = MAX(1,i);
5007 
5008 		gtk_plot_data_set_points (parametric_data, x, y, NULL, NULL, len);
5009 		g_object_set_data_full (G_OBJECT (parametric_data),
5010 					"x", x, (GDestroyNotify)g_free);
5011 		g_object_set_data_full (G_OBJECT (parametric_data),
5012 					"y", y, (GDestroyNotify)g_free);
5013 		gtk_plot_add_data (GTK_PLOT (line_plot), parametric_data);
5014 
5015 		gtk_widget_show (GTK_WIDGET (parametric_data));
5016 
5017 		gdk_rgba_parse (&color, colors[color_i++]);
5018 		gtk_plot_data_set_line_attributes (parametric_data,
5019 						   GTK_PLOT_LINE_SOLID,
5020 						   CAIRO_LINE_CAP_ROUND,
5021 						   CAIRO_LINE_JOIN_ROUND,
5022 						   2, &color);
5023 
5024 		if (parametric_name != NULL) {
5025 			label = g_strdup (parametric_name);
5026 		} else if (parametric_func_z) {
5027 			label = label_func (-1, parametric_func_z, "t", NULL);
5028 		} else {
5029 			char *l1, *l2;
5030 			l1 = label_func (-1, parametric_func_x, "t", NULL);
5031 			l2 = label_func (-1, parametric_func_y, "t", NULL);
5032 			label = g_strconcat (l1, ", ", l2, NULL);
5033 			g_free (l1);
5034 			g_free (l2);
5035 		}
5036 		gtk_plot_data_set_legend (parametric_data, label);
5037 		g_free (label);
5038 
5039 		if (fit) {
5040 			double sizex = plot_maxx - plot_minx;
5041 			double sizey = plot_maxy - plot_miny;
5042 			if (sizex <= 0)
5043 				sizex = 1.0;
5044 			if (sizey <= 0)
5045 				sizey = 1.0;
5046 			plotx1 = plot_minx - sizex * 0.05;
5047 			plotx2 = plot_maxx + sizex * 0.05;
5048 			ploty1 = plot_miny - sizey * 0.05;
5049 			ploty2 = plot_maxy + sizey * 0.05;
5050 
5051 			/* sanity */
5052 			if (plotx2 <= plotx1)
5053 				plotx2 = plotx1 + 0.1;
5054 			if (ploty2 <= ploty1)
5055 				ploty2 = ploty1 + 0.1;
5056 
5057 			/* sanity */
5058 			if (plotx1 < -(G_MAXDOUBLE/2))
5059 				plotx1 = -(G_MAXDOUBLE/2);
5060 			if (plotx2 > (G_MAXDOUBLE/2))
5061 				plotx2 = (G_MAXDOUBLE/2);
5062 			if (ploty1 < -(G_MAXDOUBLE/2))
5063 				ploty1 = -(G_MAXDOUBLE/2);
5064 			if (ploty2 > (G_MAXDOUBLE/2))
5065 				ploty2 = (G_MAXDOUBLE/2);
5066 		}
5067 	}
5068 
5069 	plot_setup_axis ();
5070 
5071 	replot_fields ();
5072 
5073 	line_plot_move_about ();
5074 
5075 	/* could be whacked by closing the window or some such */
5076 	if (plot_canvas != NULL) {
5077 		gtk_plot_canvas_thaw (GTK_PLOT_CANVAS (plot_canvas));
5078 		gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
5079 		gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
5080 	}
5081 
5082 	gtk_plot_thaw (GTK_PLOT (line_plot));
5083 	plot_in_progress --;
5084 	gel_calc_running --;
5085 	plot_window_setup ();
5086 
5087 	if (gel_evalnode_hook != NULL)
5088 		(*gel_evalnode_hook)();
5089 }
5090 
5091 static void
plot_surface_functions(gboolean do_window_present,gboolean fit_function)5092 plot_surface_functions (gboolean do_window_present, gboolean fit_function)
5093 {
5094 	init_var_names ();
5095 
5096 	ensure_window (do_window_present);
5097 
5098 	if (plot_canvas != NULL /* sanity */)
5099 		gtk_plot_canvas_freeze (GTK_PLOT_CANVAS (plot_canvas));
5100 
5101 	clear_graph ();
5102 
5103 	add_surface_plot ();
5104 
5105 	plot_in_progress ++;
5106 	gel_calc_running ++;
5107 	plot_window_setup ();
5108 
5109 
5110 	/* sanity */
5111 	if (surfacex2 == surfacex1)
5112 		surfacex2 = surfacex1 + MINPLOT;
5113 	if (surfacey2 == surfacey1)
5114 		surfacey2 = surfacey1 + MINPLOT;
5115 	if (surfacez2 == surfacez1)
5116 		surfacez2 = surfacez1 + MINPLOT;
5117 
5118 
5119 	gtk_plot3d_reset_angles (GTK_PLOT3D (surface_plot));
5120 	gtk_plot3d_rotate_x (GTK_PLOT3D (surface_plot), 60.0);
5121 	gtk_plot3d_rotate_z (GTK_PLOT3D (surface_plot), 30.0);
5122 
5123 	init_plot_ctx ();
5124 
5125 	if (gel_evalnode_hook != NULL)
5126 		(*gel_evalnode_hook)();
5127 
5128 	if (surface_func != NULL) {
5129 		recompute_surface_function (fit_function);
5130 	}
5131 
5132 	if (surface_data_x != NULL &&
5133 	    surface_data_y != NULL &&
5134 	    surface_data_z != NULL) {
5135 		surface_data = GTK_PLOT_DATA (gtk_plot_surface_new ());
5136 		gtk_plot_surface_use_amplitud (GTK_PLOT_SURFACE (surface_data), FALSE);
5137 		gtk_plot_surface_use_height_gradient (GTK_PLOT_SURFACE (surface_data), TRUE);
5138 		gtk_plot_surface_set_mesh_visible (GTK_PLOT_SURFACE (surface_data), TRUE);
5139 		if (surfaceplot_draw_legends) {
5140 			gtk_plot_data_gradient_set_visible (GTK_PLOT_DATA (surface_data), TRUE);
5141 			gtk_plot_data_show_legend (GTK_PLOT_DATA (surface_data));
5142 		} else {
5143 			gtk_plot_data_gradient_set_visible (GTK_PLOT_DATA (surface_data), FALSE);
5144 			gtk_plot_data_hide_legend (GTK_PLOT_DATA (surface_data));
5145 		}
5146 		gtk_plot_data_move_gradient (GTK_PLOT_DATA (surface_data),
5147 					     0.93, 0.15);
5148 		gtk_plot_axis_hide_title (GTK_PLOT_DATA (surface_data)->gradient);
5149 
5150 		gtk_plot_data_set_x (GTK_PLOT_DATA (surface_data), surface_data_x);
5151 		gtk_plot_data_set_y (GTK_PLOT_DATA (surface_data), surface_data_y);
5152 		gtk_plot_data_set_z (GTK_PLOT_DATA (surface_data), surface_data_z);
5153 		gtk_plot_data_set_numpoints (GTK_PLOT_DATA (surface_data), surface_data_len);
5154 
5155 		gtk_plot_add_data (GTK_PLOT (surface_plot),
5156 				   surface_data);
5157 
5158 		surface_setup_gradient ();
5159 
5160 		gtk_widget_show (GTK_WIDGET (surface_data));
5161 	}
5162 
5163 	if (surface_data != NULL) {
5164 		if (surface_func != NULL) {
5165 			char *label = label_func (-1, surface_func, /* FIXME: correct variable */ "...", surface_func_name);
5166 			gtk_plot_data_set_legend (surface_data, label);
5167 			g_free (label);
5168 		} else if (surface_func_name) {
5169 			gtk_plot_data_set_legend (surface_data, surface_func_name);
5170 		} else
5171 			gtk_plot_data_set_legend (surface_data, "");
5172 	}
5173 
5174 	surface_setup_axis ();
5175 
5176 	/* FIXME: this doesn't work (crashes) must fix in GtkExtra
5177 	gtk_plot3d_autoscale (GTK_PLOT3D (surface_plot));
5178 	*/
5179 
5180 	if (surface_data != NULL) {
5181 		gtk_plot_surface_build_mesh (GTK_PLOT_SURFACE (surface_data));
5182 	}
5183 
5184 
5185 	/* could be whacked by closing the window or some such */
5186 	if (surface_plot != NULL) {
5187 		if (surfaceplot_draw_legends)
5188 			gtk_plot_show_legends (GTK_PLOT (surface_plot));
5189 		else
5190 			gtk_plot_hide_legends (GTK_PLOT (surface_plot));
5191 	}
5192 
5193 	/* could be whacked by closing the window or some such */
5194 	if (plot_canvas != NULL) {
5195 		gtk_plot_canvas_thaw (GTK_PLOT_CANVAS (plot_canvas));
5196 		gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
5197 		gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
5198 
5199 		if (gel_evalnode_hook != NULL)
5200 			(*gel_evalnode_hook)();
5201 	}
5202 
5203 	plot_in_progress --;
5204 	gel_calc_running --;
5205 	plot_window_setup ();
5206 }
5207 
5208 /*exact answer callback*/
5209 static void
int_spin_cb(GtkAdjustment * adj,int * data)5210 int_spin_cb (GtkAdjustment *adj, int *data)
5211 {
5212 	*data = (int)(gtk_adjustment_get_value (adj));
5213 }
5214 
5215 static void
entry_activate(void)5216 entry_activate (void)
5217 {
5218 	if (plot_dialog != NULL)
5219 		gtk_dialog_response (GTK_DIALOG (plot_dialog),
5220 				     RESPONSE_PLOT);
5221 }
5222 
5223 static GtkWidget *
create_range_boxes(const char * title,GtkWidget ** titlew,const char * def1,GtkWidget ** w1,const char * totitle,GtkWidget ** totitlew,const char * def2,GtkWidget ** w2,const char * bytitle,const char * defby,GtkWidget ** wb,GCallback activate_callback)5224 create_range_boxes (const char *title, GtkWidget **titlew,
5225 		    const char *def1, GtkWidget **w1,
5226 		    const char *totitle, GtkWidget **totitlew,
5227 		    const char *def2, GtkWidget **w2,
5228 		    const char *bytitle,
5229 		    const char *defby, GtkWidget **wb,
5230 		    GCallback activate_callback)
5231 {
5232 	GtkWidget *b, *w;
5233 
5234 	b = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
5235 	w = gtk_label_new(title);
5236 	if (titlew != NULL) {
5237 		*titlew = w;
5238 		g_signal_connect (G_OBJECT (w),
5239 				  "destroy",
5240 				  G_CALLBACK (gtk_widget_destroyed),
5241 				  titlew);
5242 	}
5243 	gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
5244 	w = gtk_entry_new ();
5245 	if (w1 != NULL) {
5246 		*w1 = w;
5247 		g_signal_connect (G_OBJECT (w),
5248 				  "destroy",
5249 				  G_CALLBACK (gtk_widget_destroyed),
5250 				  w1);
5251 	}
5252 	gtk_entry_set_text (GTK_ENTRY (w), def1);
5253 	g_signal_connect (G_OBJECT (w), "activate",
5254 			  G_CALLBACK (activate_callback), NULL);
5255 	gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
5256 
5257 	if (totitle != NULL) {
5258 		w = gtk_label_new (totitle);
5259 		if (totitlew != NULL) {
5260 			*totitlew = w;
5261 			g_signal_connect (G_OBJECT (w),
5262 					  "destroy",
5263 					  G_CALLBACK (gtk_widget_destroyed),
5264 					  totitlew);
5265 		}
5266 		gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
5267 		w = gtk_entry_new ();
5268 		if (w2 != NULL) {
5269 			*w2 = w;
5270 			g_signal_connect (G_OBJECT (w),
5271 					  "destroy",
5272 					  G_CALLBACK (gtk_widget_destroyed),
5273 					  w2);
5274 		}
5275 		gtk_entry_set_text (GTK_ENTRY (w), def2);
5276 		g_signal_connect (G_OBJECT (w), "activate",
5277 				  G_CALLBACK (activate_callback), NULL);
5278 		gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
5279 	}
5280 
5281 	if (bytitle != NULL) {
5282 		w = gtk_label_new (bytitle);
5283 		gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
5284 		w = gtk_entry_new ();
5285 		if (wb != NULL) {
5286 			*wb = w;
5287 			g_signal_connect (G_OBJECT (w),
5288 					  "destroy",
5289 					  G_CALLBACK (gtk_widget_destroyed),
5290 					  wb);
5291 		}
5292 		gtk_entry_set_text (GTK_ENTRY (w), defby);
5293 		g_signal_connect (G_OBJECT (w), "activate",
5294 				  G_CALLBACK (activate_callback), NULL);
5295 		gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
5296 	}
5297 
5298 	return b;
5299 }
5300 
5301 static GtkWidget *
create_int_spinbox(const char * title,int * val,int min,int max)5302 create_int_spinbox (const char *title, int *val, int min, int max)
5303 {
5304 	GtkWidget *b, *w;
5305 	GtkAdjustment *adj;
5306 
5307 	b = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
5308 	w = gtk_label_new(title);
5309 	gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
5310 	adj = (GtkAdjustment *)gtk_adjustment_new (*val,
5311 						   min,
5312 						   max,
5313 						   1,
5314 						   10,
5315 						   0);
5316 	w = gtk_spin_button_new (adj, 1.0, 0);
5317 	g_signal_connect (G_OBJECT (w), "activate",
5318 			  G_CALLBACK (gtk_spin_button_update), NULL);
5319 	g_signal_connect (G_OBJECT (w), "activate",
5320 			  G_CALLBACK (entry_activate), NULL);
5321 	gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (w), TRUE);
5322 	gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (w), GTK_UPDATE_ALWAYS);
5323 	gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (w), TRUE);
5324 	gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
5325 	g_signal_connect (G_OBJECT (adj), "value_changed",
5326 			  G_CALLBACK (int_spin_cb), val);
5327 
5328 	return b;
5329 }
5330 
5331 static GtkWidget *
create_expression_box(const char * label,GtkWidget ** labelw,GtkWidget ** entry,GtkWidget ** status)5332 create_expression_box (const char *label,
5333 		       GtkWidget **labelw,
5334 		       GtkWidget **entry,
5335 		       GtkWidget **status)
5336 {
5337 	GtkWidget *b;
5338 	GtkWidget *l;
5339 
5340 	b = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
5341 
5342 	l = gtk_label_new (label);
5343 	if (labelw != NULL) {
5344 		*labelw = l;
5345 		g_signal_connect (G_OBJECT (l),
5346 				  "destroy",
5347 				  G_CALLBACK (gtk_widget_destroyed),
5348 				  labelw);
5349 	}
5350 
5351 	gtk_box_pack_start (GTK_BOX (b), l, FALSE, FALSE, 0);
5352 
5353 	*entry = gtk_entry_new ();
5354 	g_signal_connect (G_OBJECT (*entry), "activate",
5355 			  G_CALLBACK (entry_activate), NULL);
5356 	gtk_box_pack_start (GTK_BOX (b), *entry, TRUE, TRUE, 0);
5357 
5358 	*status = gtk_image_new ();
5359 	gtk_box_pack_start (GTK_BOX (b), *status, FALSE, FALSE, 0);
5360 	return b;
5361 }
5362 
5363 static GtkWidget *
create_simple_expression_box(const char * label,GtkWidget ** labelw,GtkWidget ** entry)5364 create_simple_expression_box (const char *label,
5365 			      GtkWidget **labelw,
5366 			      GtkWidget **entry)
5367 {
5368 	GtkWidget *b;
5369 	GtkWidget *l;
5370 
5371 	b = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
5372 
5373 	l = gtk_label_new (label);
5374 	if (labelw != NULL)
5375 		*labelw = l;
5376 
5377 	gtk_box_pack_start (GTK_BOX (b), l, FALSE, FALSE, 0);
5378 
5379 	*entry = gtk_entry_new ();
5380 	gtk_box_pack_start (GTK_BOX (b), *entry, TRUE, TRUE, 0);
5381 
5382 	return b;
5383 }
5384 
5385 /*option callback*/
5386 static void
optioncb(GtkWidget * widget,int * data)5387 optioncb (GtkWidget * widget, int *data)
5388 {
5389 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
5390 		*data = TRUE;
5391 	else
5392 		*data = FALSE;
5393 }
5394 
5395 static void
set_lineplot_labels(void)5396 set_lineplot_labels (void)
5397 {
5398 	char *s;
5399 	int i;
5400 
5401 	if (plot_dialog == NULL)
5402 		return;
5403 
5404 	s = g_strdup_printf (_("Type in function name or expression involving "
5405 			       "the %s and %s variables (or the %s variable which will be %s=%s+i%s) "
5406 			       "that gives the slope "
5407 			       "at the point (%s,%s)."),
5408 			     lp_x_name,
5409 			     lp_y_name,
5410 			     lp_z_name,
5411 			     lp_z_name,
5412 			     lp_x_name,
5413 			     lp_y_name,
5414 			     lp_x_name,
5415 			     lp_y_name);
5416 	gtk_label_set_text (GTK_LABEL (slopefield_info_label), s);
5417 	g_free (s);
5418 
5419 	s = g_strdup_printf ("d%s/d%s=",
5420 			     lp_y_name,
5421 			     lp_x_name);
5422 	gtk_label_set_text (GTK_LABEL (slopefield_der_label), s);
5423 	g_free (s);
5424 
5425 	s = g_strdup_printf (_("%s from:"),
5426 			     lp_x_name);
5427 	gtk_label_set_text (GTK_LABEL (lineplot_x_range_label), s);
5428 	g_free (s);
5429 
5430 	s = g_strdup_printf (_("%s from:"),
5431 			     lp_y_name);
5432 	gtk_label_set_text (GTK_LABEL (lineplot_y_range_label), s);
5433 	g_free (s);
5434 
5435 	s = g_strdup_printf (_("Type in function names or expressions involving "
5436 			       "the %s and %s variables (or the %s variable which will be %s=%s+i%s) "
5437 			       "that give the d%s/d%s and d%s/d%s of the autonomous system to be plotted "
5438 			       "at the point (%s,%s)."),
5439 			     lp_x_name,
5440 			     lp_y_name,
5441 			     lp_z_name,
5442 			     lp_z_name,
5443 			     lp_x_name,
5444 			     lp_y_name,
5445 			     lp_x_name,
5446 			     lp_t_name,
5447 			     lp_y_name,
5448 			     lp_t_name,
5449 			     lp_x_name,
5450 			     lp_y_name);
5451 	gtk_label_set_text (GTK_LABEL (vectorfield_info_label), s);
5452 	g_free (s);
5453 
5454 	s = g_strdup_printf ("d%s/d%s=",
5455 			     lp_x_name,
5456 			     lp_t_name);
5457 	gtk_label_set_text (GTK_LABEL (vectorfield_xder_label), s);
5458 	g_free (s);
5459 
5460 	s = g_strdup_printf ("d%s/d%s=",
5461 			     lp_y_name,
5462 			     lp_t_name);
5463 	gtk_label_set_text (GTK_LABEL (vectorfield_yder_label), s);
5464 	g_free (s);
5465 
5466 	s = g_strdup_printf (_("Type in function names or expressions involving "
5467 			       "the %s variable in the boxes below to graph "
5468 			       "them"), lp_x_name);
5469 	gtk_label_set_text (GTK_LABEL (lineplot_info_label), s);
5470 	g_free (s);
5471 
5472 	s = g_strdup_printf ("%s=", lp_y_name);
5473 	for (i = 0; i < MAXFUNC; i++) {
5474 		gtk_label_set_text (GTK_LABEL (plot_y_labels[i]), s);
5475 	}
5476 	g_free (s);
5477 
5478 	s = g_strdup_printf (_("Type in function names or expressions involving "
5479 			       "the %s variable in the boxes below to graph "
5480 			       "them.  Either fill in both boxes with %s= and %s= "
5481 			       "in front of them giving the %s and %s coordinates "
5482 			       "separately, or alternatively fill in the %s= box "
5483 			       "giving %s and %s as the real and imaginary part of "
5484 			       "a complex number."),
5485 			     lp_t_name,
5486 			     lp_x_name,
5487 			     lp_y_name,
5488 			     lp_x_name,
5489 			     lp_y_name,
5490 			     lp_z_name,
5491 			     lp_x_name,
5492 			     lp_y_name);
5493 	gtk_label_set_text (GTK_LABEL (parametric_info_label), s);
5494 	g_free (s);
5495 
5496 	s = g_strdup_printf ("%s=",
5497 			     lp_x_name);
5498 	gtk_label_set_text (GTK_LABEL (parametric_x_label), s);
5499 	g_free (s);
5500 
5501 	s = g_strdup_printf ("%s=",
5502 			     lp_y_name);
5503 	gtk_label_set_text (GTK_LABEL (parametric_y_label), s);
5504 	g_free (s);
5505 
5506 	s = g_strdup_printf ("%s=",
5507 			     lp_z_name);
5508 	gtk_label_set_text (GTK_LABEL (parametric_z_label), s);
5509 	g_free (s);
5510 
5511 	s = g_strdup_printf (_("Parameter %s from:"),
5512 			     lp_t_name);
5513 	gtk_label_set_text (GTK_LABEL (parametric_trange_label), s);
5514 	g_free (s);
5515 }
5516 
5517 static void
set_solver_labels(void)5518 set_solver_labels (void)
5519 {
5520 	char *s;
5521 
5522 	if (solver_dialog == NULL)
5523 		return;
5524 
5525 	if (solver_xinc_label != NULL) {
5526 		s = g_strdup_printf (_("%s increment:"),
5527 				     lp_x_name);
5528 		gtk_label_set_text (GTK_LABEL (solver_xinc_label), s);
5529 		g_free (s);
5530 	}
5531 
5532 	if (solver_tinc_label != NULL) {
5533 		s = g_strdup_printf (_("%s increment:"),
5534 				     lp_t_name);
5535 		gtk_label_set_text (GTK_LABEL (solver_tinc_label), s);
5536 		g_free (s);
5537 	}
5538 
5539 	if (solver_tlen_label != NULL) {
5540 		s = g_strdup_printf (_("%s interval length:"),
5541 				     lp_t_name);
5542 		gtk_label_set_text (GTK_LABEL (solver_tlen_label), s);
5543 		g_free (s);
5544 	}
5545 
5546 	if (solver_x_pt_label != NULL) {
5547 		s = g_strdup_printf (_("Point %s:"),
5548 				     lp_x_name);
5549 		gtk_label_set_text (GTK_LABEL (solver_x_pt_label), s);
5550 		g_free (s);
5551 	}
5552 
5553 	if (solver_y_pt_label != NULL) {
5554 		s = g_strdup_printf ("%s:",
5555 				     lp_y_name);
5556 		gtk_label_set_text (GTK_LABEL (solver_y_pt_label), s);
5557 		g_free (s);
5558 	}
5559 }
5560 
5561 static void
set_surface_labels(void)5562 set_surface_labels (void)
5563 {
5564 	char *s;
5565 
5566 	if (plot_dialog == NULL)
5567 		return;
5568 
5569 	s = g_strdup_printf
5570 		(_("Type a function name or an expression involving "
5571 		   "the %s and %s variables (or the %s variable which will be %s=%s+i%s) "
5572 		   "in the boxes below to graph them.  Functions with one argument only "
5573 		   "will be passed a complex number."),
5574 		 sp_x_name,
5575 		 sp_y_name,
5576 		 sp_z_name,
5577 		 sp_z_name,
5578 		 sp_x_name,
5579 		 sp_y_name);
5580 	gtk_label_set_text (GTK_LABEL (surface_info_label), s);
5581 	g_free (s);
5582 
5583 	s = g_strdup_printf (_("%s from:"),
5584 			     sp_x_name);
5585 	gtk_label_set_text (GTK_LABEL (surface_x_range_label), s);
5586 	g_free (s);
5587 
5588 	s = g_strdup_printf (_("%s from:"),
5589 			     sp_y_name);
5590 	gtk_label_set_text (GTK_LABEL (surface_y_range_label), s);
5591 	g_free (s);
5592 }
5593 
5594 static char *
get_varname_from_entry(GtkEntry * e,const char * def,gboolean * ex)5595 get_varname_from_entry (GtkEntry *e, const char *def, gboolean *ex)
5596 {
5597 	char *str = g_strdup (gtk_entry_get_text (GTK_ENTRY (e)));
5598 	if (ve_string_empty (str)) {
5599 		g_free (str);
5600 		*ex = TRUE;
5601 		return g_strdup (def);
5602 	}
5603 
5604 	if ( ! is_identifier (str))
5605 		*ex = TRUE;
5606 
5607 	str = g_strcanon (str,
5608 			  G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "_",
5609 			  '_');
5610 	if (str[0] >= '0' && str[0] <= '9') {
5611 		*ex = TRUE;
5612 		str[0] = '_';
5613 	}
5614 
5615 	return str;
5616 }
5617 
5618 static void
change_lineplot_varnames(GtkWidget * button,gpointer data)5619 change_lineplot_varnames (GtkWidget *button, gpointer data)
5620 {
5621 	GtkWidget *req = NULL;
5622 	GtkWidget *b, *l;
5623 	GtkWidget *xe;
5624 	GtkWidget *ye;
5625 	GtkWidget *ze;
5626 	GtkWidget *te;
5627 	GtkWidget *errlabel;
5628         GtkSizeGroup *sg;
5629 
5630 	req = gtk_dialog_new_with_buttons
5631 		(_("Change variable names") /* title */,
5632 		 GTK_WINDOW (graph_window) /* parent */,
5633 		 GTK_DIALOG_MODAL /* flags */,
5634 		 _("_OK"),
5635 		 GTK_RESPONSE_OK,
5636 		 _("_Cancel"),
5637 		 GTK_RESPONSE_CANCEL,
5638 		 NULL);
5639 
5640 	gtk_dialog_set_default_response (GTK_DIALOG (req),
5641 					 GTK_RESPONSE_OK);
5642 
5643 	sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
5644 
5645 	errlabel = gtk_label_new (_("Some values were illegal"));
5646 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
5647 			    errlabel, FALSE, FALSE, 0);
5648 
5649 	b = create_simple_expression_box (_("independent variable (x):"),
5650 					  &l, &xe);
5651 	gtk_label_set_xalign (GTK_LABEL (l), 0.0);
5652 	gtk_size_group_add_widget (sg, l);
5653 	gtk_entry_set_text (GTK_ENTRY (xe), lp_x_name);
5654 	g_signal_connect (G_OBJECT (xe), "activate",
5655 			  G_CALLBACK (ok_dialog_entry_activate), req);
5656 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
5657 			    b, FALSE, FALSE, 0);
5658 
5659 	b = create_simple_expression_box (_("dependent variable (y):"),
5660 					  &l, &ye);
5661 	gtk_label_set_xalign (GTK_LABEL (l), 0.0);
5662 	gtk_size_group_add_widget (sg, l);
5663 	gtk_entry_set_text (GTK_ENTRY (ye), lp_y_name);
5664 	g_signal_connect (G_OBJECT (ye), "activate",
5665 			  G_CALLBACK (ok_dialog_entry_activate), req);
5666 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
5667 			    b, FALSE, FALSE, 0);
5668 
5669 	b = create_simple_expression_box (_("complex variable (z = x+iy):"),
5670 					  &l, &ze);
5671 	gtk_label_set_xalign (GTK_LABEL (l), 0.0);
5672 	gtk_size_group_add_widget (sg, l);
5673 	gtk_entry_set_text (GTK_ENTRY (ze), lp_z_name);
5674 	g_signal_connect (G_OBJECT (ze), "activate",
5675 			  G_CALLBACK (ok_dialog_entry_activate), req);
5676 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
5677 			    b, FALSE, FALSE, 0);
5678 
5679 	b = create_simple_expression_box (_("parameter variable (t):"),
5680 					  &l, &te);
5681 	gtk_label_set_xalign (GTK_LABEL (l), 0.0);
5682 	gtk_size_group_add_widget (sg, l);
5683 	gtk_entry_set_text (GTK_ENTRY (te), lp_t_name);
5684 	g_signal_connect (G_OBJECT (te), "activate",
5685 			  G_CALLBACK (ok_dialog_entry_activate), req);
5686 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
5687 			    b, FALSE, FALSE, 0);
5688 
5689 	gtk_widget_show_all (req);
5690 	gtk_widget_hide (errlabel);
5691 
5692 run_dialog_again:
5693 
5694 	if (gtk_dialog_run (GTK_DIALOG (req)) == GTK_RESPONSE_OK) {
5695 		gboolean ex = FALSE;
5696 		char *xn, *yn, *zn, *tn;
5697 
5698 		xn = get_varname_from_entry (GTK_ENTRY (xe), "x", &ex);
5699 		yn = get_varname_from_entry (GTK_ENTRY (ye), "y", &ex);
5700 		zn = get_varname_from_entry (GTK_ENTRY (ze), "z", &ex);
5701 		tn = get_varname_from_entry (GTK_ENTRY (te), "t", &ex);
5702 		if (strcmp (xn, yn) == 0 ||
5703 		    strcmp (xn, zn) == 0 ||
5704 		    strcmp (xn, tn) == 0 ||
5705 		    strcmp (yn, zn) == 0 ||
5706 		    strcmp (yn, tn) == 0 ||
5707 		    strcmp (zn, tn) == 0)
5708 			ex = TRUE;
5709 		if (ex) {
5710 			gtk_entry_set_text (GTK_ENTRY (xe), xn);
5711 			gtk_entry_set_text (GTK_ENTRY (ye), yn);
5712 			gtk_entry_set_text (GTK_ENTRY (ze), zn);
5713 			gtk_entry_set_text (GTK_ENTRY (te), tn);
5714 			g_free (xn);
5715 			g_free (yn);
5716 			g_free (zn);
5717 			g_free (tn);
5718 			gtk_widget_show (errlabel);
5719 			goto run_dialog_again;
5720 		}
5721 
5722 		g_free (lp_x_name);
5723 		g_free (lp_y_name);
5724 		g_free (lp_z_name);
5725 		g_free (lp_t_name);
5726 		lp_x_name = xn;
5727 		lp_y_name = yn;
5728 		lp_z_name = zn;
5729 		lp_t_name = tn;
5730 	}
5731 	gtk_widget_destroy (req);
5732 
5733 	set_lineplot_labels ();
5734 	set_solver_labels ();
5735 }
5736 
5737 static void
change_surface_varnames(GtkWidget * button,gpointer data)5738 change_surface_varnames (GtkWidget *button, gpointer data)
5739 {
5740 	GtkWidget *req = NULL;
5741 	GtkWidget *b, *l;
5742 	GtkWidget *xe;
5743 	GtkWidget *ye;
5744 	GtkWidget *ze;
5745 	GtkWidget *errlabel;
5746         GtkSizeGroup *sg;
5747 
5748 	req = gtk_dialog_new_with_buttons
5749 		(_("Change variable names") /* title */,
5750 		 GTK_WINDOW (graph_window) /* parent */,
5751 		 GTK_DIALOG_MODAL /* flags */,
5752 		 _("_OK"),
5753 		 GTK_RESPONSE_OK,
5754 		 _("_Cancel"),
5755 		 GTK_RESPONSE_CANCEL,
5756 		 NULL);
5757 
5758 	gtk_dialog_set_default_response (GTK_DIALOG (req),
5759 					 GTK_RESPONSE_OK);
5760 
5761 	sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
5762 
5763 	errlabel = gtk_label_new (_("Some values were illegal"));
5764 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
5765 			    errlabel, FALSE, FALSE, 0);
5766 
5767 	b = create_simple_expression_box (_("independent variable (x):"),
5768 					  &l, &xe);
5769 	gtk_label_set_xalign (GTK_LABEL (l), 0.0);
5770 	gtk_size_group_add_widget (sg, l);
5771 	gtk_entry_set_text (GTK_ENTRY (xe), sp_x_name);
5772 	g_signal_connect (G_OBJECT (xe), "activate",
5773 			  G_CALLBACK (ok_dialog_entry_activate), req);
5774 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
5775 			    b, FALSE, FALSE, 0);
5776 
5777 	b = create_simple_expression_box (_("independent variable (y):"),
5778 					  &l, &ye);
5779 	gtk_label_set_xalign (GTK_LABEL (l), 0.0);
5780 	gtk_size_group_add_widget (sg, l);
5781 	gtk_entry_set_text (GTK_ENTRY (ye), sp_y_name);
5782 	g_signal_connect (G_OBJECT (ye), "activate",
5783 			  G_CALLBACK (ok_dialog_entry_activate), req);
5784 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
5785 			    b, FALSE, FALSE, 0);
5786 
5787 	b = create_simple_expression_box (_("independent complex variable (z = x+iy):"),
5788 					  &l, &ze);
5789 	gtk_label_set_xalign (GTK_LABEL (l), 0.0);
5790 	gtk_size_group_add_widget (sg, l);
5791 	gtk_entry_set_text (GTK_ENTRY (ze), sp_z_name);
5792 	g_signal_connect (G_OBJECT (ze), "activate",
5793 			  G_CALLBACK (ok_dialog_entry_activate), req);
5794 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (req))),
5795 			    b, FALSE, FALSE, 0);
5796 
5797 	gtk_widget_show_all (req);
5798 	gtk_widget_hide (errlabel);
5799 
5800 run_dialog_again:
5801 
5802 	if (gtk_dialog_run (GTK_DIALOG (req)) == GTK_RESPONSE_OK) {
5803 		gboolean ex = FALSE;
5804 		char *xn, *yn, *zn;
5805 
5806 		xn = get_varname_from_entry (GTK_ENTRY (xe), "x", &ex);
5807 		yn = get_varname_from_entry (GTK_ENTRY (ye), "y", &ex);
5808 		zn = get_varname_from_entry (GTK_ENTRY (ze), "z", &ex);
5809 		if (strcmp (xn, yn) == 0 ||
5810 		    strcmp (xn, zn) == 0 ||
5811 		    strcmp (yn, zn) == 0)
5812 			ex = TRUE;
5813 		if (ex) {
5814 			gtk_entry_set_text (GTK_ENTRY (xe), xn);
5815 			gtk_entry_set_text (GTK_ENTRY (ye), yn);
5816 			gtk_entry_set_text (GTK_ENTRY (ze), zn);
5817 			g_free (xn);
5818 			g_free (yn);
5819 			g_free (zn);
5820 			gtk_widget_show (errlabel);
5821 			goto run_dialog_again;
5822 		}
5823 
5824 		g_free (sp_x_name);
5825 		g_free (sp_y_name);
5826 		g_free (sp_z_name);
5827 		sp_x_name = xn;
5828 		sp_y_name = yn;
5829 		sp_z_name = zn;
5830 	}
5831 	gtk_widget_destroy (req);
5832 
5833 	set_surface_labels ();
5834 }
5835 
5836 static void
setup_page(int page)5837 setup_page (int page)
5838 {
5839 	if (page == 0 /* functions */) {
5840 		gtk_widget_set_sensitive (lineplot_fit_dep_axis_checkbox, TRUE);
5841 
5842 		if (lineplot_fit_dependent_axis_cb) {
5843 			gtk_widget_set_sensitive (lineplot_dep_axis_buttons, FALSE);
5844 		} else {
5845 			gtk_widget_set_sensitive (lineplot_dep_axis_buttons, TRUE);
5846 		}
5847 		gtk_widget_set_sensitive (lineplot_depx_axis_buttons, TRUE);
5848 	} else if (page == 1 /* parametric */) {
5849 		gtk_widget_set_sensitive (lineplot_fit_dep_axis_checkbox, TRUE);
5850 
5851 		if (lineplot_fit_dependent_axis_cb) {
5852 			gtk_widget_set_sensitive (lineplot_dep_axis_buttons, FALSE);
5853 			gtk_widget_set_sensitive (lineplot_depx_axis_buttons, FALSE);
5854 		} else {
5855 			gtk_widget_set_sensitive (lineplot_dep_axis_buttons, TRUE);
5856 			gtk_widget_set_sensitive (lineplot_depx_axis_buttons, TRUE);
5857 		}
5858 	} else {
5859 		gtk_widget_set_sensitive (lineplot_fit_dep_axis_checkbox, FALSE);
5860 
5861 		gtk_widget_set_sensitive (lineplot_dep_axis_buttons, TRUE);
5862 		gtk_widget_set_sensitive (lineplot_depx_axis_buttons, TRUE);
5863 	}
5864 }
5865 
5866 static void
lineplot_switch_page_cb(GtkNotebook * notebook,GtkWidget * page,guint page_num,gpointer data)5867 lineplot_switch_page_cb (GtkNotebook *notebook, GtkWidget *page, guint page_num,
5868 			 gpointer data)
5869 {
5870 	setup_page (page_num);
5871 }
5872 
5873 
5874 /*option callback*/
5875 static void
lineplot_fit_cb_cb(GtkWidget * widget)5876 lineplot_fit_cb_cb (GtkWidget * widget)
5877 {
5878 	int function_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (function_notebook));
5879 
5880 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
5881 		lineplot_fit_dependent_axis_cb = TRUE;
5882 	} else {
5883 		lineplot_fit_dependent_axis_cb = FALSE;
5884 	}
5885 
5886 	setup_page (function_page);
5887 
5888 }
5889 
5890 
5891 static GtkWidget *
create_lineplot_box(void)5892 create_lineplot_box (void)
5893 {
5894 	GtkWidget *mainbox, *frame;
5895 	GtkWidget *box, *hbox, *b, *fb, *w;
5896 	GdkMonitor *monitor;
5897 	GdkRectangle geom;
5898 	int i;
5899 
5900 	init_var_names ();
5901 
5902 	mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
5903 	gtk_container_set_border_width (GTK_CONTAINER (mainbox), GENIUS_PAD);
5904 
5905 	function_notebook = gtk_notebook_new ();
5906 	gtk_box_pack_start (GTK_BOX (mainbox), function_notebook, FALSE, FALSE, 0);
5907 
5908 	/*
5909 	 * Line plot entries
5910 	 */
5911 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
5912 	gtk_container_set_border_width (GTK_CONTAINER (box), GENIUS_PAD);
5913 	lineplot_info_label = gtk_label_new ("");
5914 	gtk_label_set_xalign (GTK_LABEL (lineplot_info_label), 0.0);
5915 	gtk_label_set_line_wrap (GTK_LABEL (lineplot_info_label), TRUE);
5916 	gtk_label_set_max_width_chars (GTK_LABEL (lineplot_info_label), 30);
5917 	gtk_widget_set_size_request (lineplot_info_label, 610, -1);
5918 	gtk_box_pack_start (GTK_BOX (box), lineplot_info_label, FALSE, FALSE, 0);
5919 	g_signal_connect (G_OBJECT (lineplot_info_label),
5920 			  "destroy",
5921 			  G_CALLBACK (gtk_widget_destroyed),
5922 			  &lineplot_info_label);
5923 
5924 	fb = box;
5925 
5926 	monitor = gdk_display_get_primary_monitor (gdk_display_get_default ());
5927 	gdk_monitor_get_geometry (monitor, &geom);
5928 	if (geom.height < 800) {
5929 		w = gtk_scrolled_window_new (NULL, NULL);
5930 		gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w),
5931 						GTK_POLICY_NEVER,
5932 						GTK_POLICY_ALWAYS);
5933 		gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0);
5934 
5935 		b = gtk_viewport_new (NULL, NULL);
5936 		gtk_container_add (GTK_CONTAINER (w), b);
5937 
5938 		fb = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
5939 		gtk_container_set_border_width (GTK_CONTAINER (fb), GENIUS_PAD);
5940 
5941 		gtk_container_add (GTK_CONTAINER (b), fb);
5942 	}
5943 
5944 
5945 
5946 	for (i = 0; i < MAXFUNC; i++) {
5947 		b = create_expression_box ("y=",
5948 					   &(plot_y_labels[i]),
5949 					   &(plot_entries[i]),
5950 					   &(plot_entries_status[i]));
5951 		gtk_box_pack_start (GTK_BOX (fb), b, FALSE, FALSE, 0);
5952 	}
5953 
5954 	gtk_notebook_append_page (GTK_NOTEBOOK (function_notebook),
5955 				  box,
5956 				  gtk_label_new_with_mnemonic (_("_Functions / Expressions")));
5957 
5958 	/*
5959 	 * Parametric plot entries
5960 	 */
5961 
5962 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
5963 	gtk_container_set_border_width (GTK_CONTAINER (box), GENIUS_PAD);
5964 	parametric_info_label = gtk_label_new ("");
5965 	gtk_label_set_xalign (GTK_LABEL (parametric_info_label), 0.0);
5966 	gtk_label_set_line_wrap (GTK_LABEL (parametric_info_label), TRUE);
5967 	gtk_label_set_max_width_chars (GTK_LABEL (parametric_info_label), 30);
5968 	gtk_widget_set_size_request (parametric_info_label, 610, -1);
5969 	gtk_box_pack_start (GTK_BOX (box), parametric_info_label, FALSE, FALSE, 0);
5970 	g_signal_connect (G_OBJECT (parametric_info_label),
5971 			  "destroy",
5972 			  G_CALLBACK (gtk_widget_destroyed),
5973 			  &parametric_info_label);
5974 
5975 	/* x */
5976 	b = create_expression_box ("x=",
5977 				   &parametric_x_label,
5978 				   &parametric_entry_x,
5979 				   &parametric_status_x);
5980 	gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
5981 
5982 	/* y */
5983 	b = create_expression_box ("y=",
5984 				   &parametric_y_label,
5985 				   &parametric_entry_y,
5986 				   &parametric_status_y);
5987 	gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
5988 
5989 	w = gtk_label_new (_("or"));
5990 	gtk_label_set_xalign (GTK_LABEL (w), 0.0);
5991 	gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
5992 
5993 	/* z */
5994 	b = create_expression_box ("z=",
5995 				   &parametric_z_label,
5996 				   &parametric_entry_z,
5997 				   &parametric_status_z);
5998 	gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
5999 
6000 	/* just spacing */
6001 	gtk_box_pack_start (GTK_BOX (box), gtk_label_new (""), FALSE, FALSE, 0);
6002 
6003 	/* t range */
6004 	b = create_range_boxes (_("Parameter t from:"), &parametric_trange_label,
6005 				spint1_default, &spint1_entry,
6006 				_("to:"), NULL,
6007 				spint2_default, &spint2_entry,
6008 				_("by:"),
6009 				spintinc_default, &spintinc_entry,
6010 				entry_activate);
6011 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6012 
6013 	gtk_notebook_append_page (GTK_NOTEBOOK (function_notebook),
6014 				  box,
6015 				  gtk_label_new_with_mnemonic (_("Pa_rametric")));
6016 
6017 	/*
6018 	 * Slopefield
6019 	 */
6020 
6021 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
6022 	gtk_container_set_border_width (GTK_CONTAINER (box), GENIUS_PAD);
6023 
6024 	slopefield_info_label = gtk_label_new ("");
6025 	gtk_label_set_xalign (GTK_LABEL (slopefield_info_label), 0.0);
6026 	gtk_label_set_line_wrap (GTK_LABEL (slopefield_info_label), TRUE);
6027 	gtk_label_set_max_width_chars (GTK_LABEL (slopefield_info_label), 30);
6028 	gtk_widget_set_size_request (slopefield_info_label, 610, -1);
6029 	gtk_box_pack_start (GTK_BOX (box), slopefield_info_label, FALSE, FALSE, 0);
6030 	g_signal_connect (G_OBJECT (slopefield_info_label),
6031 			  "destroy",
6032 			  G_CALLBACK (gtk_widget_destroyed),
6033 			  &slopefield_info_label);
6034 
6035 	/* dy/dx */
6036 	b = create_expression_box ("dy/dx=",
6037 				   &slopefield_der_label,
6038 				   &slopefield_entry,
6039 				   &slopefield_status);
6040 	gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
6041 
6042 	/* # of ticks */
6043 	b = create_int_spinbox (_("Vertical ticks:"), &spinSVtick, 2, 50);
6044 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6045 
6046 	/* # of ticks */
6047 	b = create_int_spinbox (_("Horizontal ticks:"), &spinSHtick, 2, 50);
6048 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6049 
6050 	gtk_notebook_append_page (GTK_NOTEBOOK (function_notebook),
6051 				  box,
6052 				  gtk_label_new_with_mnemonic (_("Sl_ope field")));
6053 
6054 	/*
6055 	 * Vectorfield
6056 	 */
6057 
6058 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
6059 	gtk_container_set_border_width (GTK_CONTAINER (box), GENIUS_PAD);
6060 	vectorfield_info_label = gtk_label_new ("");
6061 
6062 	gtk_label_set_xalign (GTK_LABEL (vectorfield_info_label), 0.0);
6063 	gtk_label_set_line_wrap (GTK_LABEL (vectorfield_info_label), TRUE);
6064 	gtk_label_set_max_width_chars (GTK_LABEL (vectorfield_info_label), 30);
6065 	gtk_widget_set_size_request (vectorfield_info_label, 610, -1);
6066 	gtk_box_pack_start (GTK_BOX (box), vectorfield_info_label, FALSE, FALSE, 0);
6067 	g_signal_connect (G_OBJECT (vectorfield_info_label),
6068 			  "destroy",
6069 			  G_CALLBACK (gtk_widget_destroyed),
6070 			  &vectorfield_info_label);
6071 
6072 	/* dx/dt */
6073 	b = create_expression_box ("dx/dt=",
6074 				   &vectorfield_xder_label,
6075 				   &vectorfield_entry_x,
6076 				   &vectorfield_status_x);
6077 	gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
6078 
6079 	/* dy/dt */
6080 	b = create_expression_box ("dy/dt=",
6081 				   &vectorfield_yder_label,
6082 				   &vectorfield_entry_y,
6083 				   &vectorfield_status_y);
6084 	gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
6085 
6086 	/* Normalize the arrow length? */
6087 	w = gtk_check_button_new_with_mnemonic (_("_Normalize arrow length (do not show size)"));
6088 	gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
6089 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
6090 				      vectorfield_normalize_arrow_length_cb);
6091 	g_signal_connect (G_OBJECT (w), "toggled",
6092 			  G_CALLBACK (optioncb),
6093 			  (gpointer)&vectorfield_normalize_arrow_length_cb);
6094 
6095 	/* # of ticks */
6096 	b = create_int_spinbox (_("Vertical ticks:"), &spinVVtick, 2, 50);
6097 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6098 
6099 	/* # of ticks */
6100 	b = create_int_spinbox (_("Horizontal ticks:"), &spinVHtick, 2, 50);
6101 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6102 
6103 	gtk_notebook_append_page (GTK_NOTEBOOK (function_notebook),
6104 				  box,
6105 				  gtk_label_new_with_mnemonic (_("_Vector field")));
6106 
6107 	/*
6108 	 * Below notebook
6109 	 */
6110 
6111 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
6112 	gtk_box_pack_start (GTK_BOX (mainbox), hbox, FALSE, FALSE, 0);
6113 
6114 	/* draw legend? */
6115 	w = gtk_check_button_new_with_mnemonic (_("_Draw legend"));
6116 	gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
6117 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
6118 				      lineplot_draw_legends_cb);
6119 	g_signal_connect (G_OBJECT (w), "toggled",
6120 			  G_CALLBACK (optioncb),
6121 			  (gpointer)&lineplot_draw_legends_cb);
6122 
6123 	/* draw axis labels? */
6124 	w = gtk_check_button_new_with_mnemonic (_("Draw axis labels"));
6125 	gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
6126 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
6127 				      lineplot_draw_labels_cb);
6128 	g_signal_connect (G_OBJECT (w), "toggled",
6129 			  G_CALLBACK (optioncb),
6130 			  (gpointer)&lineplot_draw_labels_cb);
6131 
6132 	/* change varnames */
6133 	b = gtk_button_new_with_label (_("Change variable names..."));
6134 	gtk_box_pack_end (GTK_BOX (hbox), b, FALSE, FALSE, 0);
6135 	g_signal_connect (G_OBJECT (b), "clicked",
6136 			  G_CALLBACK (change_lineplot_varnames), NULL);
6137 
6138 
6139 
6140 	/* plot window */
6141 	frame = gtk_frame_new (_("Plot Window"));
6142 	gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
6143 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
6144 	gtk_container_set_border_width (GTK_CONTAINER (box), GENIUS_PAD);
6145 	gtk_container_add (GTK_CONTAINER (frame), box);
6146 
6147 	/*
6148 	 * X range
6149 	 */
6150 	b = create_range_boxes (_("X from:"), &lineplot_x_range_label,
6151 				spinx1_default, &spinx1_entry,
6152 				_("to:"), NULL,
6153 				spinx2_default, &spinx2_entry,
6154 				NULL, NULL, NULL,
6155 				entry_activate);
6156 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6157 	lineplot_depx_axis_buttons = b;
6158 
6159 	/*
6160 	 * Y range
6161 	 */
6162 	b = create_range_boxes (_("Y from:"), &lineplot_y_range_label,
6163 				spiny1_default, &spiny1_entry,
6164 				_("to:"), NULL,
6165 				spiny2_default, &spiny2_entry,
6166 				NULL, NULL, NULL,
6167 				entry_activate);
6168 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6169 	lineplot_dep_axis_buttons = b;
6170 
6171 	/* fit dependent axis? */
6172 	w = gtk_check_button_new_with_label (_("Fit dependent axis"));
6173 	lineplot_fit_dep_axis_checkbox = w;
6174 	gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
6175 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
6176 				      lineplot_fit_dependent_axis_cb);
6177 	g_signal_connect (G_OBJECT (w), "toggled",
6178 			  G_CALLBACK (lineplot_fit_cb_cb), NULL);
6179 	lineplot_fit_cb_cb (w);
6180 
6181 
6182 
6183 	/* set labels correctly */
6184 	set_lineplot_labels ();
6185 
6186 	g_signal_connect (G_OBJECT (function_notebook), "switch_page",
6187 			  G_CALLBACK (lineplot_switch_page_cb), NULL);
6188 
6189 	return mainbox;
6190 }
6191 
6192 /*option callback*/
6193 static void
surface_fit_cb_cb(GtkWidget * widget)6194 surface_fit_cb_cb (GtkWidget * widget)
6195 {
6196 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
6197 		surfaceplot_fit_dependent_axis_cb = TRUE;
6198 		gtk_widget_set_sensitive (surfaceplot_dep_axis_buttons, FALSE);
6199 	} else {
6200 		surfaceplot_fit_dependent_axis_cb = FALSE;
6201 		gtk_widget_set_sensitive (surfaceplot_dep_axis_buttons, TRUE);
6202 	}
6203 }
6204 
6205 static GtkWidget *
create_surface_box(void)6206 create_surface_box (void)
6207 {
6208 	GtkWidget *mainbox, *frame;
6209 	GtkWidget *hbox, *box, *b, *w;
6210 
6211 	init_var_names ();
6212 
6213 	mainbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
6214 	gtk_container_set_border_width (GTK_CONTAINER (mainbox), GENIUS_PAD);
6215 
6216 	frame = gtk_frame_new (_("Function / Expression"));
6217 	gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
6218 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
6219 	gtk_container_set_border_width (GTK_CONTAINER (box), GENIUS_PAD);
6220 	gtk_container_add (GTK_CONTAINER (frame), box);
6221 	surface_info_label = gtk_label_new ("");
6222 	gtk_label_set_xalign (GTK_LABEL (surface_info_label), 0.0);
6223 	gtk_label_set_line_wrap (GTK_LABEL (surface_info_label), TRUE);
6224 	gtk_label_set_max_width_chars (GTK_LABEL (surface_info_label), 30);
6225 	gtk_widget_set_size_request (surface_info_label, 610, -1);
6226 	gtk_label_set_line_wrap (GTK_LABEL (surface_info_label), TRUE);
6227 
6228 	gtk_box_pack_start (GTK_BOX (box), surface_info_label, FALSE, FALSE, 0);
6229 
6230 	b = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
6231 	gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
6232 
6233 	surface_entry = gtk_entry_new ();
6234 	g_signal_connect (G_OBJECT (surface_entry), "activate",
6235 			  G_CALLBACK (entry_activate), NULL);
6236 	gtk_box_pack_start (GTK_BOX (b), surface_entry, TRUE, TRUE, 0);
6237 
6238 	surface_entry_status = gtk_image_new ();
6239 	gtk_box_pack_start (GTK_BOX (b), surface_entry_status, FALSE, FALSE, 0);
6240 
6241 	hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, GENIUS_PAD);
6242 	gtk_box_pack_start (GTK_BOX (mainbox), hbox, FALSE, FALSE, 0);
6243 
6244 	/* draw legend? */
6245 	w = gtk_check_button_new_with_mnemonic (_("_Draw legend"));
6246 	gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
6247 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
6248 				      surfaceplot_draw_legends_cb);
6249 	g_signal_connect (G_OBJECT (w), "toggled",
6250 			  G_CALLBACK (optioncb),
6251 			  (gpointer)&surfaceplot_draw_legends_cb);
6252 
6253 	/* change varnames */
6254 
6255 	b = gtk_button_new_with_label (_("Change variable names..."));
6256 	gtk_box_pack_end (GTK_BOX (hbox), b, FALSE, FALSE, 0);
6257 	g_signal_connect (G_OBJECT (b), "clicked",
6258 			  G_CALLBACK (change_surface_varnames), NULL);
6259 
6260 	/*
6261 	 * Plot window frame
6262 	 */
6263 
6264 	frame = gtk_frame_new (_("Plot Window"));
6265 	gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
6266 	box = gtk_box_new (GTK_ORIENTATION_VERTICAL, GENIUS_PAD);
6267 	gtk_container_set_border_width (GTK_CONTAINER (box), GENIUS_PAD);
6268 	gtk_container_add (GTK_CONTAINER (frame), box);
6269 
6270 	/*
6271 	 * X range
6272 	 */
6273 	b = create_range_boxes (_("X from:"), &surface_x_range_label,
6274 				surf_spinx1_default, &surf_spinx1_entry,
6275 				_("to:"), NULL,
6276 				surf_spinx2_default, &surf_spinx2_entry,
6277 				NULL, NULL, NULL,
6278 				entry_activate);
6279 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6280 
6281 	/*
6282 	 * Y range
6283 	 */
6284 	b = create_range_boxes (_("Y from:"), &surface_y_range_label,
6285 				surf_spiny1_default, &surf_spiny1_entry,
6286 				_("to:"), NULL,
6287 				surf_spiny2_default, &surf_spiny2_entry,
6288 				NULL, NULL, NULL,
6289 				entry_activate);
6290 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6291 
6292 	/*
6293 	 * Z range
6294 	 */
6295 	b = create_range_boxes (_("Dependent axis from:"), NULL,
6296 				surf_spinz1_default, &surf_spinz1_entry,
6297 				_("to:"), NULL,
6298 				surf_spinz2_default, &surf_spinz2_entry,
6299 				NULL, NULL, NULL,
6300 				entry_activate);
6301 	gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
6302 	surfaceplot_dep_axis_buttons = b;
6303 
6304 	/* fit dependent axis? */
6305 	w = gtk_check_button_new_with_label (_("Fit dependent axis"));
6306 	gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
6307 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
6308 				      surfaceplot_fit_dependent_axis_cb);
6309 	g_signal_connect (G_OBJECT (w), "toggled",
6310 			  G_CALLBACK (surface_fit_cb_cb), NULL);
6311 	surface_fit_cb_cb (w);
6312 
6313 	/* set labels correctly */
6314 	set_surface_labels ();
6315 
6316 	return mainbox;
6317 }
6318 
6319 static GtkWidget *
create_plot_dialog(void)6320 create_plot_dialog (void)
6321 {
6322 	plot_notebook = gtk_notebook_new ();
6323 
6324 	gtk_notebook_append_page (GTK_NOTEBOOK (plot_notebook),
6325 				  create_lineplot_box (),
6326 				  gtk_label_new_with_mnemonic (_("Function _line plot")));
6327 
6328 	gtk_notebook_append_page (GTK_NOTEBOOK (plot_notebook),
6329 				  create_surface_box (),
6330 				  gtk_label_new_with_mnemonic (_("_Surface plot")));
6331 
6332 	return plot_notebook;
6333 }
6334 
6335 static GelEFunc *
function_from_expression(const char * e,const char * var,gboolean * ex)6336 function_from_expression (const char *e, const char *var, gboolean *ex)
6337 {
6338 	GelEFunc *f = NULL;
6339 	GelETree *value;
6340 	char *ce;
6341 
6342 	if (ve_string_empty (e))
6343 		return NULL;
6344 
6345 	ce = g_strstrip (g_strdup (e));
6346 	if (is_identifier (ce) && strcmp (ce, var) != 0) {
6347 		f = d_lookup_global (d_intern (ce));
6348 		g_free (ce);
6349 		if (f != NULL) {
6350 			f = d_copyfunc (f);
6351 			f->context = -1;
6352 		} else {
6353 			*ex = TRUE;
6354 		}
6355 		return f;
6356 	}
6357 
6358 	value = gel_parseexp (ce,
6359 			      NULL /* infile */,
6360 			      FALSE /* exec_commands */,
6361 			      FALSE /* testparse */,
6362 			      NULL /* finished */,
6363 			      NULL /* dirprefix */);
6364 	g_free (ce);
6365 
6366 	/* Have to reset the error here, else we may die */
6367 	gel_error_num = GEL_NO_ERROR;
6368 	gel_got_eof = FALSE;
6369 
6370 	/* FIXME: if "x" (var) not used try to evaluate and if it returns a function use that */
6371 
6372 	if (value != NULL) {
6373 		f = d_makeufunc (NULL /* id */,
6374 				 value,
6375 				 g_slist_append (NULL, d_intern (var)),
6376 				 1,
6377 				 NULL /* extra_dict */);
6378 	}
6379 
6380 	if (f == NULL)
6381 		*ex = TRUE;
6382 
6383 	return f;
6384 }
6385 
6386 static GelEFunc *
function_from_expression2(const char * e,const char * xname,const char * yname,const char * zname,gboolean * ex)6387 function_from_expression2 (const char *e,
6388 			   const char *xname,
6389 			   const char *yname,
6390 			   const char *zname,
6391 			   gboolean *ex)
6392 {
6393 	GelEFunc *f = NULL;
6394 	GelETree *value;
6395 	char *ce;
6396 	gboolean got_x, got_y, got_z;
6397 
6398 	if (ve_string_empty (e))
6399 		return NULL;
6400 
6401 	ce = g_strstrip (g_strdup (e));
6402 	if (is_identifier (ce) &&
6403 	    strcmp (ce, xname) != 0 &&
6404 	    strcmp (ce, yname) != 0 &&
6405 	    strcmp (ce, zname) != 0) {
6406 		f = d_lookup_global (d_intern (ce));
6407 		g_free (ce);
6408 		if (f != NULL) {
6409 			f = d_copyfunc (f);
6410 			f->context = -1;
6411 		} else {
6412 			*ex = TRUE;
6413 		}
6414 		return f;
6415 	}
6416 
6417 	value = gel_parseexp (ce,
6418 			      NULL /* infile */,
6419 			      FALSE /* exec_commands */,
6420 			      FALSE /* testparse */,
6421 			      NULL /* finished */,
6422 			      NULL /* dirprefix */);
6423 	g_free (ce);
6424 
6425 	/* Have to reset the error here, else we may die */
6426 	gel_error_num = GEL_NO_ERROR;
6427 	gel_got_eof = FALSE;
6428 
6429 	/* FIXME: funcbody?  I think it must be done. */
6430 	got_x = gel_eval_find_identifier (value, d_intern (xname), TRUE /*funcbody*/);
6431 	got_y = gel_eval_find_identifier (value, d_intern (yname), TRUE /*funcbody*/);
6432 	got_z = gel_eval_find_identifier (value, d_intern (zname), TRUE /*funcbody*/);
6433 
6434 	/* FIXME: if "x" or "y" or "z" not used try to evaluate and if it returns a function use that */
6435 	if (value != NULL) {
6436 		if ( ! got_x && ! got_y && got_z) {
6437 			f = d_makeufunc (NULL /* id */,
6438 					 value,
6439 					 g_slist_append (NULL, d_intern (zname)),
6440 					 1,
6441 					 NULL /* extra_dict */);
6442 		} else if ( ! got_z) {
6443 			GSList *l = g_slist_append (NULL, d_intern (xname));
6444 			l = g_slist_append (l, d_intern (yname));
6445 			f = d_makeufunc (NULL /* id */,
6446 					 value,
6447 					 l,
6448 					 2,
6449 					 NULL /* extra_dict */);
6450 		} else {
6451 			GSList *l = g_slist_append (NULL, d_intern (xname));
6452 			l = g_slist_append (l, d_intern (yname));
6453 			l = g_slist_append (l, d_intern (zname));
6454 			f = d_makeufunc (NULL /* id */,
6455 					 value,
6456 					 l,
6457 					 3,
6458 					 NULL /* extra_dict */);
6459 		}
6460 	}
6461 
6462 	if (f == NULL)
6463 		*ex = TRUE;
6464 
6465 	return f;
6466 }
6467 
6468 static gboolean
get_number_from_entry(GtkWidget * entry,GtkWidget * win,double * num)6469 get_number_from_entry (GtkWidget *entry, GtkWidget *win, double *num)
6470 {
6471 	GelETree *t;
6472 	double d;
6473 	char *str = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
6474 	if (ve_string_empty (str)) {
6475 		genius_display_error (win, _("Empty value as range value"));
6476 		if (str != NULL) g_free (str);
6477 		return FALSE;
6478 	}
6479 
6480 	if (gel_calc_running) {
6481 		genius_display_error (win, _("Genius is executing something already"));
6482 		g_free (str);
6483 		return FALSE;
6484 	}
6485 
6486 	gel_calc_running++;
6487 	gel_execinit ();
6488 	t = gel_parseexp (str, NULL, FALSE, FALSE, NULL, NULL);
6489 	g_free (str);
6490 	if (t == NULL) {
6491 		gel_calc_running--;
6492 		genius_display_error (win, _("Cannot parse range value"));
6493 		return FALSE;
6494 	}
6495 
6496 	t = gel_runexp (t);
6497 	gel_calc_running--;
6498 
6499 	/* FIXME: handle errors? ! */
6500 	if G_UNLIKELY (gel_error_num != 0)
6501 		gel_error_num = 0;
6502 
6503 	if (t == NULL) {
6504 		genius_display_error (win, _("Cannot execute range value"));
6505 		return FALSE;
6506 	}
6507 
6508 	if (t->type != GEL_VALUE_NODE) {
6509 		genius_display_error (win, _("Range value not a value"));
6510 		gel_freetree(t);
6511 		return FALSE;
6512 	}
6513 
6514 	if (t->type != GEL_VALUE_NODE) {
6515 		genius_display_error (win, _("Range value not a value"));
6516 		gel_freetree(t);
6517 		return FALSE;
6518 	}
6519 
6520 	d = mpw_get_double (t->val.value);
6521 	gel_freetree (t);
6522 	if G_UNLIKELY (gel_error_num != 0) {
6523 		genius_display_error (win, _("Cannot convert range value to a reasonable real number"));
6524 		gel_error_num = 0;
6525 		return FALSE;
6526 	}
6527 
6528 	*num = d;
6529 
6530 	return TRUE;
6531 }
6532 
6533 
6534 static GelEFunc *
get_func_from_entry(GtkWidget * entry,GtkWidget * status,const char * var,gboolean * ex)6535 get_func_from_entry (GtkWidget *entry, GtkWidget *status,
6536 		     const char *var, gboolean *ex)
6537 {
6538 	GelEFunc *f;
6539 	const char *str = gtk_entry_get_text (GTK_ENTRY (entry));
6540 	f = function_from_expression (str, var, ex);
6541 	if (f != NULL) {
6542 		gtk_image_set_from_icon_name
6543 			(GTK_IMAGE (status),
6544 			 "gtk-yes",
6545 			 GTK_ICON_SIZE_MENU);
6546 	} else if (*ex) {
6547 		gtk_image_set_from_icon_name
6548 			(GTK_IMAGE (status),
6549 			 "dialog-warning",
6550 			 GTK_ICON_SIZE_MENU);
6551 		f = NULL;
6552 	} else {
6553 		gtk_image_set_from_pixbuf
6554 			(GTK_IMAGE (status),
6555 			 NULL);
6556 		f = NULL;
6557 	}
6558 	return f;
6559 }
6560 
6561 static GelEFunc *
get_func_from_entry2(GtkWidget * entry,GtkWidget * status,const char * xname,const char * yname,const char * zname,gboolean * ex)6562 get_func_from_entry2 (GtkWidget *entry, GtkWidget *status,
6563 		      const char *xname,
6564 		      const char *yname,
6565 		      const char *zname,
6566 		      gboolean *ex)
6567 {
6568 	GelEFunc *f;
6569 	const char *str = gtk_entry_get_text (GTK_ENTRY (entry));
6570 	f = function_from_expression2 (str, xname, yname, zname, ex);
6571 	if (f != NULL) {
6572 		gtk_image_set_from_icon_name
6573 			(GTK_IMAGE (status),
6574 			 "gtk-yes",
6575 			 GTK_ICON_SIZE_MENU);
6576 	} else if (*ex) {
6577 		gtk_image_set_from_icon_name
6578 			(GTK_IMAGE (status),
6579 			 "dialog-warning",
6580 			 GTK_ICON_SIZE_MENU);
6581 		f = NULL;
6582 	} else {
6583 		gtk_image_set_from_pixbuf
6584 			(GTK_IMAGE (status),
6585 			 NULL);
6586 		f = NULL;
6587 	}
6588 	return f;
6589 }
6590 
6591 static void
surface_from_dialog(void)6592 surface_from_dialog (void)
6593 {
6594 	GelEFunc *func = { NULL };
6595 	double x1, x2, y1, y2, z1, z2;
6596 	long last_errnum;
6597 	char *error_to_print = NULL;
6598 	gboolean ex;
6599 
6600 	plot_mode = MODE_SURFACE;
6601 
6602 	last_errnum = total_errors;
6603 
6604 	ex = FALSE;
6605 	func = get_func_from_entry2 (surface_entry, surface_entry_status,
6606 				     sp_x_name, sp_y_name, sp_z_name, &ex);
6607 
6608 	if (func == NULL) {
6609 		error_to_print = g_strdup (_("No functions to plot or no functions "
6610 					     "could be parsed"));
6611 		goto whack_copied_funcs;
6612 	}
6613 
6614 	surfaceplot_draw_legends = surfaceplot_draw_legends_cb;
6615 
6616 	if ( ! get_number_from_entry (surf_spinx1_entry, plot_dialog, &x1) ||
6617 	     ! get_number_from_entry (surf_spinx2_entry, plot_dialog, &x2) ||
6618 	     ! get_number_from_entry (surf_spiny1_entry, plot_dialog, &y1) ||
6619 	     ! get_number_from_entry (surf_spiny2_entry, plot_dialog, &y2) ||
6620 	     ! get_number_from_entry (surf_spinz1_entry, plot_dialog, &z1) ||
6621 	     ! get_number_from_entry (surf_spinz2_entry, plot_dialog, &z2) ) {
6622 		goto whack_copied_funcs;
6623 	}
6624 
6625 	if (x1 > x2) {
6626 		double s = x1;
6627 		x1 = x2;
6628 		x2 = s;
6629 	}
6630 
6631 	if (y1 > y2) {
6632 		double s = y1;
6633 		y1 = y2;
6634 		y2 = s;
6635 	}
6636 
6637 	if (z1 > z2) {
6638 		double s = z1;
6639 		z1 = z2;
6640 		z2 = s;
6641 	}
6642 
6643 	if (x1 == x2) {
6644 		error_to_print = g_strdup_printf (_("Invalid %s range"),
6645 						  sp_x_name);
6646 		goto whack_copied_funcs;
6647 	}
6648 
6649 	if (y1 == y2) {
6650 		error_to_print = g_strdup_printf (_("Invalid %s range"),
6651 						  sp_y_name);
6652 		goto whack_copied_funcs;
6653 	}
6654 
6655 	if (z1 == z2) {
6656 		error_to_print = g_strdup (_("Invalid dependent range"));
6657 		goto whack_copied_funcs;
6658 	}
6659 
6660 	reset_surfacex1 = surfacex1 = x1;
6661 	reset_surfacex2 = surfacex2 = x2;
6662 	reset_surfacey1 = surfacey1 = y1;
6663 	reset_surfacey2 = surfacey2 = y2;
6664 	reset_surfacez1 = surfacez1 = z1;
6665 	reset_surfacez2 = surfacez2 = z2;
6666 
6667 	if (surface_func != NULL) {
6668 		d_freefunc (surface_func);
6669 		surface_func = NULL;
6670 	}
6671 	g_free (surface_func_name);
6672 	surface_func_name = NULL;
6673 
6674 	surface_func = func;
6675 	func = NULL;
6676 
6677 	/* don't plot from data */
6678 	if (surface_data_x != NULL) {
6679 		g_free (surface_data_x);
6680 		surface_data_x = NULL;
6681 	}
6682 	if (surface_data_y != NULL) {
6683 		g_free (surface_data_y);
6684 		surface_data_y = NULL;
6685 	}
6686 	if (surface_data_z != NULL) {
6687 		g_free (surface_data_z);
6688 		surface_data_z = NULL;
6689 	}
6690 	/* just in case */
6691 	if (surface_data != NULL) {
6692 		gtk_plot_data_set_x (GTK_PLOT_DATA (surface_data), NULL);
6693 		gtk_plot_data_set_y (GTK_PLOT_DATA (surface_data), NULL);
6694 		gtk_plot_data_set_z (GTK_PLOT_DATA (surface_data), NULL);
6695 	}
6696 
6697 	/* setup name when the functions don't have their own name */
6698 	if (surface_func->id == NULL)
6699 		surface_func_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (surface_entry)));
6700 
6701 	plot_mode = MODE_SURFACE;
6702 	plot_surface_functions (TRUE /* do_window_present */,
6703 				surfaceplot_fit_dependent_axis_cb /*fit*/);
6704 
6705 	if (surfaceplot_fit_dependent_axis_cb) {
6706 		reset_surfacez1 = surfacez1;
6707 		reset_surfacez2 = surfacez2;
6708 	}
6709 
6710 	if (gel_interrupted)
6711 		gel_interrupted = FALSE;
6712 
6713 	gel_printout_infos_parent (graph_window);
6714 	if (last_errnum != total_errors &&
6715 	    ! genius_setup.error_box) {
6716 		gtk_widget_show (errors_label_box);
6717 	}
6718 
6719 	return;
6720 
6721 whack_copied_funcs:
6722 	if (func != NULL) {
6723 		d_freefunc (func);
6724 		func = NULL;
6725 	}
6726 
6727 	gel_printout_infos_parent (graph_window);
6728 	if (last_errnum != total_errors &&
6729 	    ! genius_setup.error_box) {
6730 		gtk_widget_show (errors_label_box);
6731 	}
6732 
6733 	if (error_to_print != NULL) {
6734 		genius_display_error (plot_dialog, error_to_print);
6735 		g_free (error_to_print);
6736 	}
6737 }
6738 
6739 static void
line_plot_clear_funcs(void)6740 line_plot_clear_funcs (void)
6741 {
6742 	int i;
6743 
6744 	for (i = 0; i < MAXFUNC && plot_func[i] != NULL; i++) {
6745 		d_freefunc (plot_func[i]);
6746 		plot_func[i] = NULL;
6747 		g_free (plot_func_name[i]);
6748 		plot_func_name[i] = NULL;
6749 	}
6750 
6751 	d_freefunc (parametric_func_x);
6752 	parametric_func_x = NULL;
6753 	d_freefunc (parametric_func_y);
6754 	parametric_func_y = NULL;
6755 	d_freefunc (parametric_func_z);
6756 	parametric_func_z = NULL;
6757 	g_free (parametric_name);
6758 	parametric_name = NULL;
6759 
6760 	d_freefunc (vectorfield_func_x);
6761 	vectorfield_func_x = NULL;
6762 	d_freefunc (vectorfield_func_y);
6763 	vectorfield_func_y = NULL;
6764 	g_free (vectorfield_name_x);
6765 	vectorfield_name_x = NULL;
6766 	g_free (vectorfield_name_y);
6767 	vectorfield_name_y = NULL;
6768 
6769 	d_freefunc (slopefield_func);
6770 	slopefield_func = NULL;
6771 	g_free (slopefield_name);
6772 	slopefield_name = NULL;
6773 }
6774 
6775 static void
plot_from_dialog_lineplot(void)6776 plot_from_dialog_lineplot (void)
6777 {
6778 	int funcs = 0;
6779 	GelEFunc *func[MAXFUNC] = { NULL };
6780 	double x1, x2, y1, y2;
6781 	int i, j;
6782 	char *error_to_print = NULL;
6783 	long last_errnum;
6784 
6785 	plot_mode = MODE_LINEPLOT;
6786 
6787 	last_errnum = total_errors;
6788 
6789 	for (i = 0; i < MAXFUNC; i++) {
6790 		GelEFunc *f;
6791 		gboolean ex = FALSE;
6792 		f = get_func_from_entry (plot_entries[i],
6793 					 plot_entries_status[i],
6794 					 lp_x_name,
6795 					 &ex);
6796 		if (f != NULL) {
6797 			func[i] = f;
6798 			funcs++;
6799 		}
6800 	}
6801 
6802 	if (funcs == 0) {
6803 		error_to_print = g_strdup (_("No functions to plot or no functions "
6804 					     "could be parsed"));
6805 		goto whack_copied_funcs;
6806 	}
6807 
6808 	if ( ! get_number_from_entry (spinx1_entry, plot_dialog, &x1) ||
6809 	     ! get_number_from_entry (spinx2_entry, plot_dialog, &x2) ||
6810 	     ! get_number_from_entry (spiny1_entry, plot_dialog, &y1) ||
6811 	     ! get_number_from_entry (spiny2_entry, plot_dialog, &y2) ) {
6812 		goto whack_copied_funcs;
6813 	}
6814 
6815 	if (x1 > x2) {
6816 		double s = x1;
6817 		x1 = x2;
6818 		x2 = s;
6819 	}
6820 
6821 	if (y1 > y2) {
6822 		double s = y1;
6823 		y1 = y2;
6824 		y2 = s;
6825 	}
6826 
6827 	if (x1 == x2) {
6828 		error_to_print = g_strdup_printf (_("Invalid %s range"),
6829 						  lp_x_name);
6830 		goto whack_copied_funcs;
6831 	}
6832 
6833 	if (y1 == y2) {
6834 		error_to_print = g_strdup_printf (_("Invalid %s range"),
6835 						  lp_y_name);
6836 		goto whack_copied_funcs;
6837 	}
6838 
6839 	reset_plotx1 = plotx1 = x1;
6840 	reset_plotx2 = plotx2 = x2;
6841 	reset_ploty1 = ploty1 = y1;
6842 	reset_ploty2 = ploty2 = y2;
6843 
6844 	line_plot_clear_funcs ();
6845 
6846 	j = 0;
6847 	for (i = 0; i < MAXFUNC; i++) {
6848 		if (func[i] != NULL) {
6849 			plot_func[j] = func[i];
6850 			func[i] = NULL;
6851 			/* setup name when the functions don't have their own name */
6852 			if (plot_func[j]->id == NULL)
6853 				plot_func_name[j] = g_strdup (gtk_entry_get_text (GTK_ENTRY (plot_entries[i])));
6854 			j++;
6855 		}
6856 	}
6857 
6858 	plot_functions (TRUE /* do_window_present */,
6859 			TRUE /* from_gui */,
6860 			lineplot_fit_dependent_axis_cb /*fit*/);
6861 
6862 	if (lineplot_fit_dependent_axis_cb) {
6863 		reset_ploty1 = ploty1;
6864 		reset_ploty2 = ploty2;
6865 	}
6866 
6867 	if (gel_interrupted)
6868 		gel_interrupted = FALSE;
6869 
6870 	gel_printout_infos_parent (graph_window);
6871 	if (last_errnum != total_errors &&
6872 	    ! genius_setup.error_box) {
6873 		gtk_widget_show (errors_label_box);
6874 	}
6875 
6876 	return;
6877 
6878 whack_copied_funcs:
6879 	for (i = 0; i < MAXFUNC && func[i] != NULL; i++) {
6880 		d_freefunc (func[i]);
6881 		func[i] = NULL;
6882 	}
6883 
6884 	gel_printout_infos_parent (graph_window);
6885 	if (last_errnum != total_errors &&
6886 	    ! genius_setup.error_box) {
6887 		gtk_widget_show (errors_label_box);
6888 	}
6889 
6890 	if (error_to_print != NULL) {
6891 		genius_display_error (plot_dialog, error_to_print);
6892 		g_free (error_to_print);
6893 	}
6894 }
6895 
6896 static void
plot_from_dialog_parametric(void)6897 plot_from_dialog_parametric (void)
6898 {
6899 	GelEFunc *funcpx = NULL;
6900 	GelEFunc *funcpy = NULL;
6901 	GelEFunc *funcpz = NULL;
6902 	double x1, x2, y1, y2;
6903 	char *error_to_print = NULL;
6904 	gboolean exx = FALSE;
6905 	gboolean exy = FALSE;
6906 	gboolean exz = FALSE;
6907 	long last_errnum;
6908 
6909 	plot_mode = MODE_LINEPLOT_PARAMETRIC;
6910 
6911 	last_errnum = total_errors;
6912 
6913 	funcpx = get_func_from_entry (parametric_entry_x,
6914 				      parametric_status_x,
6915 				      lp_t_name,
6916 				      &exx);
6917 	funcpy = get_func_from_entry (parametric_entry_y,
6918 				      parametric_status_y,
6919 				      lp_t_name,
6920 				      &exy);
6921 	funcpz = get_func_from_entry (parametric_entry_z,
6922 				      parametric_status_z,
6923 				      lp_t_name,
6924 				      &exz);
6925 	if (((funcpx || exx) || (funcpy || exy)) && (funcpz || exz)) {
6926 		error_to_print = g_strdup_printf (_("Only specify %s and %s, or %s, not all at once."), lp_x_name, lp_y_name, lp_z_name);
6927 		goto whack_copied_funcs;
6928 	}
6929 
6930 	if ( ! ( (funcpz == NULL && funcpx != NULL && funcpy != NULL) ||
6931 		 (funcpz != NULL && funcpx == NULL && funcpy == NULL))) {
6932 		error_to_print = g_strdup (_("No functions to plot or no functions "
6933 					     "could be parsed"));
6934 		goto whack_copied_funcs;
6935 	}
6936 
6937 	if ( ! get_number_from_entry (spinx1_entry, plot_dialog, &x1) ||
6938 	     ! get_number_from_entry (spinx2_entry, plot_dialog, &x2) ||
6939 	     ! get_number_from_entry (spiny1_entry, plot_dialog, &y1) ||
6940 	     ! get_number_from_entry (spiny2_entry, plot_dialog, &y2) ) {
6941 		goto whack_copied_funcs;
6942 	}
6943 
6944 	if (x1 > x2) {
6945 		double s = x1;
6946 		x1 = x2;
6947 		x2 = s;
6948 	}
6949 
6950 	if (y1 > y2) {
6951 		double s = y1;
6952 		y1 = y2;
6953 		y2 = s;
6954 	}
6955 
6956 	if (x1 == x2) {
6957 		error_to_print = g_strdup_printf (_("Invalid %s range"),
6958 						  lp_x_name);
6959 		goto whack_copied_funcs;
6960 	}
6961 
6962 	if (y1 == y2) {
6963 		error_to_print = g_strdup_printf (_("Invalid %s range"),
6964 						  lp_y_name);
6965 		goto whack_copied_funcs;
6966 	}
6967 
6968 	if ( ! get_number_from_entry (spint1_entry, plot_dialog, &plott1) ||
6969 	     ! get_number_from_entry (spint2_entry, plot_dialog, &plott2) ||
6970 	     ! get_number_from_entry (spintinc_entry, plot_dialog, &plottinc) ) {
6971 		goto whack_copied_funcs;
6972 	}
6973 
6974 	reset_plotx1 = plotx1 = x1;
6975 	reset_plotx2 = plotx2 = x2;
6976 	reset_ploty1 = ploty1 = y1;
6977 	reset_ploty2 = ploty2 = y2;
6978 
6979 
6980 	if (plott1 >= plott2 ||
6981 	    plottinc <= 0.0) {
6982 		error_to_print = g_strdup_printf (_("Invalid %s range"),
6983 						  lp_t_name);
6984 		goto whack_copied_funcs;
6985 	}
6986 
6987 
6988 	line_plot_clear_funcs ();
6989 
6990 	parametric_func_x = funcpx;
6991 	parametric_func_y = funcpy;
6992 	parametric_func_z = funcpz;
6993 	if (funcpz != NULL) {
6994 		parametric_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (parametric_entry_z)));
6995 	} else {
6996 		parametric_name = g_strconcat (gtk_entry_get_text (GTK_ENTRY (parametric_entry_x)),
6997 					       ",",
6998 					       gtk_entry_get_text (GTK_ENTRY (parametric_entry_y)),
6999 					       NULL);
7000 	}
7001 
7002 	plot_functions (TRUE /* do_window_present */,
7003 			TRUE /* from_gui */,
7004 			lineplot_fit_dependent_axis_cb /*fit*/);
7005 
7006 	if (lineplot_fit_dependent_axis_cb) {
7007 		reset_plotx1 = plotx1;
7008 		reset_plotx2 = plotx2;
7009 		reset_ploty1 = ploty1;
7010 		reset_ploty2 = ploty2;
7011 	}
7012 
7013 	if (gel_interrupted)
7014 		gel_interrupted = FALSE;
7015 
7016 	gel_printout_infos_parent (graph_window);
7017 	if (last_errnum != total_errors &&
7018 	    ! genius_setup.error_box) {
7019 		gtk_widget_show (errors_label_box);
7020 	}
7021 
7022 	return;
7023 
7024 whack_copied_funcs:
7025 	d_freefunc (funcpx);
7026 	funcpx = NULL;
7027 	d_freefunc (funcpy);
7028 	funcpy = NULL;
7029 	d_freefunc (funcpz);
7030 	funcpz = NULL;
7031 
7032 	gel_printout_infos_parent (graph_window);
7033 	if (last_errnum != total_errors &&
7034 	    ! genius_setup.error_box) {
7035 		gtk_widget_show (errors_label_box);
7036 	}
7037 
7038 	if (error_to_print != NULL) {
7039 		genius_display_error (plot_dialog, error_to_print);
7040 		g_free (error_to_print);
7041 	}
7042 }
7043 
7044 static void
plot_from_dialog_slopefield(void)7045 plot_from_dialog_slopefield (void)
7046 {
7047 	GelEFunc *funcp = NULL;
7048 	double x1, x2, y1, y2;
7049 	char *error_to_print = NULL;
7050 	gboolean ex = FALSE;
7051 	long last_errnum;
7052 
7053 	plot_mode = MODE_LINEPLOT_SLOPEFIELD;
7054 
7055 	last_errnum = total_errors;
7056 
7057 	init_var_names ();
7058 
7059 	ex = FALSE;
7060 	funcp = get_func_from_entry2 (slopefield_entry, slopefield_status,
7061 				      lp_x_name,
7062 				      lp_y_name,
7063 				      lp_z_name,
7064 				      &ex);
7065 
7066 	if (funcp == NULL) {
7067 		error_to_print = g_strdup(_("No functions to plot or no functions "
7068 					    "could be parsed"));
7069 		goto whack_copied_funcs;
7070 	}
7071 
7072 	if ( ! get_number_from_entry (spinx1_entry, plot_dialog, &x1) ||
7073 	     ! get_number_from_entry (spinx2_entry, plot_dialog, &x2) ||
7074 	     ! get_number_from_entry (spiny1_entry, plot_dialog, &y1) ||
7075 	     ! get_number_from_entry (spiny2_entry, plot_dialog, &y2) ) {
7076 		goto whack_copied_funcs;
7077 	}
7078 
7079 	if (x1 > x2) {
7080 		double s = x1;
7081 		x1 = x2;
7082 		x2 = s;
7083 	}
7084 
7085 	if (y1 > y2) {
7086 		double s = y1;
7087 		y1 = y2;
7088 		y2 = s;
7089 	}
7090 
7091 	if (x1 == x2) {
7092 		error_to_print = g_strdup_printf (_("Invalid %s range"),
7093 						  lp_x_name);
7094 		goto whack_copied_funcs;
7095 	}
7096 
7097 	if (y1 == y2) {
7098 		error_to_print = g_strdup_printf (_("Invalid %s range"),
7099 						  lp_y_name);
7100 		goto whack_copied_funcs;
7101 	}
7102 
7103 	reset_plotx1 = plotx1 = x1;
7104 	reset_plotx2 = plotx2 = x2;
7105 	reset_ploty1 = ploty1 = y1;
7106 	reset_ploty2 = ploty2 = y2;
7107 
7108 	plotVtick = spinSVtick;
7109 	plotHtick = spinSHtick;
7110 
7111 	line_plot_clear_funcs ();
7112 
7113 	slopefield_func = funcp;
7114 	slopefield_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (slopefield_entry)));
7115 
7116 	plot_functions (TRUE /* do_window_present */,
7117 			TRUE /* from_gui */,
7118 			FALSE /*fit*/);
7119 
7120 	if (gel_interrupted)
7121 		gel_interrupted = FALSE;
7122 
7123 	gel_printout_infos_parent (graph_window);
7124 	if (last_errnum != total_errors &&
7125 	    ! genius_setup.error_box) {
7126 		gtk_widget_show (errors_label_box);
7127 	}
7128 
7129 	return;
7130 
7131 whack_copied_funcs:
7132 	d_freefunc (funcp);
7133 	funcp = NULL;
7134 
7135 	gel_printout_infos_parent (graph_window);
7136 	if (last_errnum != total_errors &&
7137 	    ! genius_setup.error_box) {
7138 		gtk_widget_show (errors_label_box);
7139 	}
7140 
7141 	if (error_to_print != NULL) {
7142 		genius_display_error (plot_dialog, error_to_print);
7143 		g_free (error_to_print);
7144 	}
7145 }
7146 
7147 static void
plot_from_dialog_vectorfield(void)7148 plot_from_dialog_vectorfield (void)
7149 {
7150 	GelEFunc *funcpx = NULL;
7151 	GelEFunc *funcpy = NULL;
7152 	double x1, x2, y1, y2;
7153 	char *error_to_print = NULL;
7154 	gboolean ex = FALSE;
7155 	long last_errnum;
7156 
7157 	plot_mode = MODE_LINEPLOT_VECTORFIELD;
7158 
7159 	last_errnum = total_errors;
7160 
7161 	vectorfield_normalize_arrow_length =
7162 		vectorfield_normalize_arrow_length_cb;
7163 
7164 	ex = FALSE;
7165 	funcpx = get_func_from_entry2 (vectorfield_entry_x,
7166 				       vectorfield_status_x,
7167 				       lp_x_name, lp_y_name, lp_z_name, &ex);
7168 	ex = FALSE;
7169 	funcpy = get_func_from_entry2 (vectorfield_entry_y,
7170 				       vectorfield_status_y,
7171 				       lp_x_name, lp_y_name, lp_z_name, &ex);
7172 
7173 	if (funcpx == NULL || funcpy == NULL) {
7174 		error_to_print = g_strdup (_("No functions to plot or no functions "
7175 					     "could be parsed"));
7176 		goto whack_copied_funcs;
7177 	}
7178 
7179 	if ( ! get_number_from_entry (spinx1_entry, plot_dialog, &x1) ||
7180 	     ! get_number_from_entry (spinx2_entry, plot_dialog, &x2) ||
7181 	     ! get_number_from_entry (spiny1_entry, plot_dialog, &y1) ||
7182 	     ! get_number_from_entry (spiny2_entry, plot_dialog, &y2) ) {
7183 		goto whack_copied_funcs;
7184 	}
7185 
7186 	if (x1 > x2) {
7187 		double s = x1;
7188 		x1 = x2;
7189 		x2 = s;
7190 	}
7191 
7192 	if (y1 > y2) {
7193 		double s = y1;
7194 		y1 = y2;
7195 		y2 = s;
7196 	}
7197 
7198 	if (x1 == x2) {
7199 		error_to_print = g_strdup_printf (_("Invalid %s range"),
7200 						  lp_x_name);
7201 		goto whack_copied_funcs;
7202 	}
7203 
7204 	if (y1 == y2) {
7205 		error_to_print = g_strdup_printf (_("Invalid %s range"),
7206 						  lp_y_name);
7207 		goto whack_copied_funcs;
7208 	}
7209 
7210 	reset_plotx1 = plotx1 = x1;
7211 	reset_plotx2 = plotx2 = x2;
7212 	reset_ploty1 = ploty1 = y1;
7213 	reset_ploty2 = ploty2 = y2;
7214 
7215 	plotVtick = spinVVtick;
7216 	plotHtick = spinVHtick;
7217 
7218 	line_plot_clear_funcs ();
7219 
7220 	vectorfield_func_x = funcpx;
7221 	vectorfield_func_y = funcpy;
7222 	vectorfield_name_x = g_strdup (gtk_entry_get_text (GTK_ENTRY (vectorfield_entry_x)));
7223 	vectorfield_name_y = g_strdup (gtk_entry_get_text (GTK_ENTRY (vectorfield_entry_y)));
7224 
7225 	plot_functions (TRUE /* do_window_present */,
7226 			TRUE /* from_gui */,
7227 			FALSE /*fit*/);
7228 
7229 	if (gel_interrupted)
7230 		gel_interrupted = FALSE;
7231 
7232 	gel_printout_infos_parent (graph_window);
7233 	if (last_errnum != total_errors &&
7234 	    ! genius_setup.error_box) {
7235 		gtk_widget_show (errors_label_box);
7236 	}
7237 
7238 	return;
7239 
7240 whack_copied_funcs:
7241 	d_freefunc (funcpx);
7242 	funcpx = NULL;
7243 	d_freefunc (funcpy);
7244 	funcpy = NULL;
7245 
7246 	gel_printout_infos_parent (graph_window);
7247 	if (last_errnum != total_errors &&
7248 	    ! genius_setup.error_box) {
7249 		gtk_widget_show (errors_label_box);
7250 	}
7251 
7252 	if (error_to_print != NULL) {
7253 		genius_display_error (plot_dialog, error_to_print);
7254 		g_free (error_to_print);
7255 	}
7256 }
7257 
7258 static void
plot_from_dialog(void)7259 plot_from_dialog (void)
7260 {
7261 	int function_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (function_notebook));
7262 
7263 	lineplot_draw_legends = lineplot_draw_legends_cb;
7264 	lineplot_draw_labels = lineplot_draw_labels_cb;
7265 
7266 	if (function_page == 0)
7267 		plot_from_dialog_lineplot ();
7268 	else if (function_page == 1)
7269 		plot_from_dialog_parametric ();
7270 	else if (function_page == 2)
7271 		plot_from_dialog_slopefield ();
7272 	else if (function_page == 3)
7273 		plot_from_dialog_vectorfield ();
7274 }
7275 
7276 static void
plot_dialog_response(GtkWidget * w,int response,gpointer data)7277 plot_dialog_response (GtkWidget *w, int response, gpointer data)
7278 {
7279 	if (response == GTK_RESPONSE_CLOSE ||
7280 	    response == GTK_RESPONSE_DELETE_EVENT) {
7281 		gtk_widget_destroy (plot_dialog);
7282 	} else if (response == RESPONSE_PLOT) {
7283 		int pg = gtk_notebook_get_current_page (GTK_NOTEBOOK (plot_notebook));
7284 		update_spinboxes (w);
7285 		if (pg == 0 /* line plot */)
7286 			plot_from_dialog ();
7287 		else if (pg == 1 /* surface plot */)
7288 			surface_from_dialog ();
7289 	}
7290 }
7291 
7292 void
genius_plot_dialog(void)7293 genius_plot_dialog (void)
7294 {
7295 	GtkWidget *insides;
7296 
7297 	if (plot_dialog != NULL) {
7298 		gtk_window_present (GTK_WINDOW (plot_dialog));
7299 		return;
7300 	}
7301 
7302 	plot_dialog = gtk_dialog_new_with_buttons
7303 		(_("Create Plot") /* title */,
7304 		 NULL /*GTK_WINDOW (genius_window)*/ /* parent */,
7305 		 0 /* flags */,
7306 		 _("_Close"),
7307 		 GTK_RESPONSE_CLOSE,
7308 		 _("_Plot"),
7309 		 RESPONSE_PLOT,
7310 		 NULL);
7311 	gtk_window_set_type_hint (GTK_WINDOW (plot_dialog),
7312 				  GDK_WINDOW_TYPE_HINT_NORMAL);
7313 	gtk_dialog_set_default_response (GTK_DIALOG (plot_dialog),
7314 					 RESPONSE_PLOT);
7315 
7316 	g_signal_connect (G_OBJECT (plot_dialog),
7317 			  "destroy",
7318 			  G_CALLBACK (gtk_widget_destroyed),
7319 			  &plot_dialog);
7320 	g_signal_connect (G_OBJECT (plot_dialog),
7321 			  "response",
7322 			  G_CALLBACK (plot_dialog_response),
7323 			  NULL);
7324 
7325 	insides = create_plot_dialog ();
7326 
7327 	gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (plot_dialog))),
7328 			    insides, TRUE, TRUE, 0);
7329 
7330 	gtk_widget_show_all (plot_dialog);
7331 	gtk_widget_grab_focus (plot_entries[0]);
7332 }
7333 
7334 static GelETree *
SurfacePlot_op(GelCtx * ctx,GelETree ** a,int * exception)7335 SurfacePlot_op (GelCtx *ctx, GelETree * * a, int *exception)
7336 {
7337 	double x1, x2, y1, y2, z1, z2;
7338 	int i;
7339 	GelEFunc *func = NULL;
7340 	gboolean fitz = FALSE;
7341 
7342 	if G_UNLIKELY (plot_in_progress != 0) {
7343 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
7344 			      "SurfacePlot", "SurfacePlot");
7345 		return NULL;
7346 	}
7347 
7348 	i = 0;
7349 
7350 	if (a[i] != NULL && a[i]->type != GEL_FUNCTION_NODE) {
7351 		gel_errorout (_("%s: argument not a function"), "SurfacePlot");
7352 		goto whack_copied_funcs;
7353 	}
7354 
7355 	func = d_copyfunc (a[i]->func.func);
7356 	func->context = -1;
7357 
7358 	i++;
7359 
7360 	if (a[i] != NULL && a[i]->type == GEL_FUNCTION_NODE) {
7361 		gel_errorout (_("%s: only one function supported"), "SurfacePlot");
7362 		goto whack_copied_funcs;
7363 	}
7364 
7365 	/* Defaults */
7366 	x1 = surf_defx1;
7367 	x2 = surf_defx2;
7368 	y1 = surf_defy1;
7369 	y2 = surf_defy2;
7370 	z1 = surf_defz1;
7371 	z2 = surf_defz2;
7372 
7373 	if (a[i] != NULL) {
7374 		if (a[i]->type == GEL_MATRIX_NODE) {
7375 			if (gel_matrixw_elements (a[i]->mat.matrix) == 6) {
7376 				if ( ! get_limits_from_matrix_surf (a[i], &x1, &x2, &y1, &y2, &z1, &z2))
7377 					goto whack_copied_funcs;
7378 				fitz = FALSE;
7379 			} else if (gel_matrixw_elements (a[i]->mat.matrix) == 4) {
7380 				if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
7381 					goto whack_copied_funcs;
7382 				fitz = TRUE;
7383 			} else {
7384 				gel_errorout (_("Graph limits not given as a 4-vector or a 6-vector"));
7385 				goto whack_copied_funcs;
7386 			}
7387 			i++;
7388 		} else {
7389 			GET_DOUBLE(x1, i, "SurfacePlot");
7390 			i++;
7391 			if (a[i] != NULL) {
7392 				GET_DOUBLE(x2, i, "SurfacePlot");
7393 				i++;
7394 				if (a[i] != NULL) {
7395 					GET_DOUBLE(y1, i, "SurfacePlot");
7396 					i++;
7397 					if (a[i] != NULL) {
7398 						GET_DOUBLE(y2, i, "SurfacePlot");
7399 						i++;
7400 						fitz = TRUE;
7401 						if (a[i] != NULL) {
7402 							GET_DOUBLE(z1, i, "SurfacePlot");
7403 							i++;
7404 							fitz = FALSE;
7405 							if (a[i] != NULL) {
7406 								GET_DOUBLE(z2, i, "SurfacePlot");
7407 								i++;
7408 							}
7409 						}
7410 					}
7411 				}
7412 			}
7413 			/* FIXME: what about errors */
7414 			if G_UNLIKELY (gel_error_num != 0) {
7415 				gel_error_num = 0;
7416 				goto whack_copied_funcs;
7417 			}
7418 		}
7419 	}
7420 
7421 	if (x1 > x2) {
7422 		double s = x1;
7423 		x1 = x2;
7424 		x2 = s;
7425 	}
7426 
7427 	if (y1 > y2) {
7428 		double s = y1;
7429 		y1 = y2;
7430 		y2 = s;
7431 	}
7432 
7433 	if (z1 > z2) {
7434 		double s = z1;
7435 		z1 = z2;
7436 		z2 = s;
7437 	}
7438 
7439 	if (x1 == x2) {
7440 		gel_errorout (_("%s: invalid X range"), "SurfacePlot");
7441 		goto whack_copied_funcs;
7442 	}
7443 
7444 	if (y1 == y2) {
7445 		gel_errorout (_("%s: invalid Y range"), "SurfacePlot");
7446 		goto whack_copied_funcs;
7447 	}
7448 
7449 	if (z1 == z2) {
7450 		gel_errorout (_("%s: invalid Z range"), "SurfacePlot");
7451 		goto whack_copied_funcs;
7452 	}
7453 
7454 	if (surface_func != NULL) {
7455 		d_freefunc (surface_func);
7456 	}
7457 	g_free (surface_func_name);
7458 	surface_func_name = NULL;
7459 
7460 	surface_func = func;
7461 	func = NULL;
7462 
7463 	/* don't plot from data */
7464 	if (surface_data_x != NULL) {
7465 		g_free (surface_data_x);
7466 		surface_data_x = NULL;
7467 	}
7468 	if (surface_data_y != NULL) {
7469 		g_free (surface_data_y);
7470 		surface_data_y = NULL;
7471 	}
7472 	if (surface_data_z != NULL) {
7473 		g_free (surface_data_z);
7474 		surface_data_z = NULL;
7475 	}
7476 	/* just in case */
7477 	if (surface_data != NULL) {
7478 		gtk_plot_data_set_x (GTK_PLOT_DATA (surface_data), NULL);
7479 		gtk_plot_data_set_y (GTK_PLOT_DATA (surface_data), NULL);
7480 		gtk_plot_data_set_z (GTK_PLOT_DATA (surface_data), NULL);
7481 	}
7482 
7483 	reset_surfacex1 = surfacex1 = x1;
7484 	reset_surfacex2 = surfacex2 = x2;
7485 	reset_surfacey1 = surfacey1 = y1;
7486 	reset_surfacey2 = surfacey2 = y2;
7487 	reset_surfacez1 = surfacez1 = z1;
7488 	reset_surfacez2 = surfacez2 = z2;
7489 
7490 	plot_mode = MODE_SURFACE;
7491 	plot_surface_functions (FALSE /* do_window_present */,
7492 				fitz /* fit */);
7493 
7494 	if (fitz) {
7495 		reset_surfacez1 = surfacez1;
7496 		reset_surfacez2 = surfacez2;
7497 	}
7498 
7499 	if (gel_interrupted)
7500 		return NULL;
7501 	else
7502 		return gel_makenum_null ();
7503 
7504 whack_copied_funcs:
7505 	if (func != NULL) {
7506 		d_freefunc (func);
7507 		func = NULL;
7508 	}
7509 
7510 	return NULL;
7511 }
7512 
7513 static GelETree *
SlopefieldDrawSolution_op(GelCtx * ctx,GelETree ** a,int * exception)7514 SlopefieldDrawSolution_op (GelCtx *ctx, GelETree * * a, int *exception)
7515 {
7516 	double x, y, dx;
7517 
7518 	if G_UNLIKELY (plot_in_progress != 0) {
7519 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
7520 			      "SlopefieldDrawSolution", "SlopefieldDrawSolution");
7521 		return NULL;
7522 	}
7523 
7524 	GET_DOUBLE (x, 0, "SlopefieldDrawSolution");
7525 	GET_DOUBLE (y, 1, "SlopefieldDrawSolution");
7526 	GET_DOUBLE (dx, 2, "SlopefieldDrawSolution");
7527 
7528 	if (dx <= 0.0) {
7529 		gel_errorout (_("%s: dx must be positive"),
7530 			      "SlopefieldDrawSolution");
7531 		return NULL;
7532 	}
7533 
7534 	if (plot_mode != MODE_LINEPLOT_SLOPEFIELD ||
7535 	    slopefield_func == NULL) {
7536 		gel_errorout (_("%s: Slope field not active"),
7537 			      "SlopefieldDrawSolution");
7538 		return NULL;
7539 	}
7540 
7541 	slopefield_draw_solution (x, y, dx, FALSE /*is_gui*/);
7542 
7543 	return gel_makenum_null ();
7544 }
7545 
7546 static GelETree *
SlopefieldClearSolutions_op(GelCtx * ctx,GelETree ** a,int * exception)7547 SlopefieldClearSolutions_op (GelCtx *ctx, GelETree * * a, int *exception)
7548 {
7549 	if G_UNLIKELY (plot_in_progress != 0) {
7550 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
7551 			      "SlopefieldClearSolutions", "SlopefieldClearSolutions");
7552 		return NULL;
7553 	}
7554 	if (plot_mode != MODE_LINEPLOT_SLOPEFIELD) {
7555 		gel_errorout (_("%s: Slope field not active"),
7556 			      "SlopefieldClearSolutions");
7557 		return NULL;
7558 	}
7559 
7560 	clear_solutions ();
7561 
7562 	return gel_makenum_null ();
7563 }
7564 
7565 static GelETree *
VectorfieldDrawSolution_op(GelCtx * ctx,GelETree ** a,int * exception)7566 VectorfieldDrawSolution_op (GelCtx *ctx, GelETree * * a, int *exception)
7567 {
7568 	double x, y, dt, tlen;
7569 
7570 	if G_UNLIKELY (plot_in_progress != 0) {
7571 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
7572 			      "VectorfieldDrawSolution", "VectorfieldDrawSolution");
7573 		return NULL;
7574 	}
7575 
7576 	GET_DOUBLE (x, 0, "VectorfieldDrawSolution");
7577 	GET_DOUBLE (y, 1, "VectorfieldDrawSolution");
7578 	GET_DOUBLE (dt, 2, "VectorfieldDrawSolution");
7579 	GET_DOUBLE (tlen, 3, "VectorfieldDrawSolution");
7580 
7581 	if G_UNLIKELY (dt <= 0.0) {
7582 		gel_errorout (_("%s: dt must be positive"),
7583 			      "VectorfieldDrawSolution");
7584 		return NULL;
7585 	}
7586 
7587 	if G_UNLIKELY (tlen <= 0.0) {
7588 		gel_errorout (_("%s: tlen must be positive"),
7589 			      "VectorfieldDrawSolution");
7590 		return NULL;
7591 	}
7592 
7593 	if G_UNLIKELY (plot_mode != MODE_LINEPLOT_VECTORFIELD ||
7594 		       vectorfield_func_x == NULL ||
7595 		       vectorfield_func_y == NULL) {
7596 		gel_errorout (_("%s: Vector field not active"),
7597 			      "VectorfieldDrawSolution");
7598 		return NULL;
7599 	}
7600 
7601 	vectorfield_draw_solution (x, y, dt, tlen, FALSE /*is_gui*/);
7602 
7603 	return gel_makenum_null ();
7604 }
7605 
7606 
7607 static GelETree *
VectorfieldClearSolutions_op(GelCtx * ctx,GelETree ** a,int * exception)7608 VectorfieldClearSolutions_op (GelCtx *ctx, GelETree * * a, int *exception)
7609 {
7610 	if G_UNLIKELY (plot_in_progress != 0) {
7611 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
7612 			      "VectorfieldClearSolutions", "VectorfieldClearSolutions");
7613 		return NULL;
7614 	}
7615 
7616 	if G_UNLIKELY (plot_mode != MODE_LINEPLOT_VECTORFIELD) {
7617 		gel_errorout (_("%s: Vector field not active"),
7618 			      "VectorfieldClearSolutions");
7619 		return NULL;
7620 	}
7621 
7622 	clear_solutions ();
7623 
7624 	return gel_makenum_null ();
7625 }
7626 
7627 static GelETree *
SlopefieldPlot_op(GelCtx * ctx,GelETree ** a,int * exception)7628 SlopefieldPlot_op (GelCtx *ctx, GelETree * * a, int *exception)
7629 {
7630 	double x1, x2, y1, y2;
7631 	GelEFunc *func = NULL;
7632 	int i;
7633 
7634 	if G_UNLIKELY (plot_in_progress != 0) {
7635 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
7636 			      "SlopefieldPlot", "SlopefieldPlot");
7637 		return NULL;
7638 	}
7639 
7640 	if G_UNLIKELY (a[0] == NULL ||
7641 		       a[0]->type != GEL_FUNCTION_NODE) {
7642 		gel_errorout (_("%s: First argument must be a function"),
7643 			      "SlopefieldPlot");
7644 		return NULL;
7645 	}
7646 
7647 	func = d_copyfunc (a[0]->func.func);
7648 	func->context = -1;
7649 
7650 	/* Defaults */
7651 	x1 = defx1;
7652 	x2 = defx2;
7653 	y1 = defy1;
7654 	y2 = defy2;
7655 
7656 	i = 1;
7657 
7658 	/* Get window limits */
7659 	if (a[i] != NULL) {
7660 		if (a[i]->type == GEL_MATRIX_NODE) {
7661 			if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
7662 				goto whack_copied_funcs;
7663 			i++;
7664 		} else {
7665 			GET_DOUBLE(x1, i, "SlopefieldPlot");
7666 			i++;
7667 			if (a[i] != NULL) {
7668 				GET_DOUBLE(x2, i, "SlopefieldPlot");
7669 				i++;
7670 				if (a[i] != NULL) {
7671 					GET_DOUBLE(y1, i, "SlopefieldPlot");
7672 					i++;
7673 					if (a[i] != NULL) {
7674 						GET_DOUBLE(y2, i, "SlopefieldPlot");
7675 						i++;
7676 					}
7677 				}
7678 			}
7679 			/* FIXME: what about errors */
7680 			if G_UNLIKELY (gel_error_num != 0) {
7681 				gel_error_num = 0;
7682 				goto whack_copied_funcs;
7683 			}
7684 		}
7685 	}
7686 
7687 	if (x1 > x2) {
7688 		double s = x1;
7689 		x1 = x2;
7690 		x2 = s;
7691 	}
7692 
7693 	if (y1 > y2) {
7694 		double s = y1;
7695 		y1 = y2;
7696 		y2 = s;
7697 	}
7698 
7699 	if (x1 == x2) {
7700 		gel_errorout (_("%s: invalid X range"), "SlopefieldPlot");
7701 		goto whack_copied_funcs;
7702 	}
7703 
7704 	if (y1 == y2) {
7705 		gel_errorout (_("%s: invalid Y range"), "SlopefieldPlot");
7706 		goto whack_copied_funcs;
7707 	}
7708 
7709 	line_plot_clear_funcs ();
7710 
7711 	slopefield_func = func;
7712 
7713 	reset_plotx1 = plotx1 = x1;
7714 	reset_plotx2 = plotx2 = x2;
7715 	reset_ploty1 = ploty1 = y1;
7716 	reset_ploty2 = ploty2 = y2;
7717 
7718 	plotVtick = plot_sf_Vtick;
7719 	plotHtick = plot_sf_Htick;
7720 
7721 	plot_mode = MODE_LINEPLOT_SLOPEFIELD;
7722 	plot_functions (FALSE /* do_window_present */,
7723 			FALSE /* from gui */,
7724 			FALSE /*fit*/);
7725 
7726 	if (gel_interrupted)
7727 		return NULL;
7728 	else
7729 		return gel_makenum_null ();
7730 
7731 whack_copied_funcs:
7732 	d_freefunc (func);
7733 	func = NULL;
7734 
7735 	return NULL;
7736 }
7737 
7738 static GelETree *
VectorfieldPlot_op(GelCtx * ctx,GelETree ** a,int * exception)7739 VectorfieldPlot_op (GelCtx *ctx, GelETree * * a, int *exception)
7740 {
7741 	double x1, x2, y1, y2;
7742 	GelEFunc *funcx = NULL;
7743 	GelEFunc *funcy = NULL;
7744 	int i;
7745 
7746 	if G_UNLIKELY (plot_in_progress != 0) {
7747 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
7748 			      "VectorfieldPlot", "VectorfieldPlot");
7749 		return NULL;
7750 	}
7751 
7752 	/* FIXME: also accept just one function and then treat it as complex
7753 	 * valued */
7754 
7755 	if G_UNLIKELY (a[0] == NULL || a[1] == NULL ||
7756 		       a[0]->type != GEL_FUNCTION_NODE ||
7757 		       a[1]->type != GEL_FUNCTION_NODE) {
7758 		gel_errorout (_("%s: First two arguments must be functions"), "VectorfieldPlot");
7759 		return NULL;
7760 	}
7761 
7762 	funcx = d_copyfunc (a[0]->func.func);
7763 	funcx->context = -1;
7764 	funcy = d_copyfunc (a[1]->func.func);
7765 	funcy->context = -1;
7766 
7767 	/* Defaults */
7768 	x1 = defx1;
7769 	x2 = defx2;
7770 	y1 = defy1;
7771 	y2 = defy2;
7772 
7773 	i = 2;
7774 
7775 	/* Get window limits */
7776 	if (a[i] != NULL) {
7777 		if (a[i]->type == GEL_MATRIX_NODE) {
7778 			if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
7779 				goto whack_copied_funcs;
7780 			i++;
7781 		} else {
7782 			GET_DOUBLE(x1, i, "VectorfieldPlot");
7783 			i++;
7784 			if (a[i] != NULL) {
7785 				GET_DOUBLE(x2, i, "VectorfieldPlot");
7786 				i++;
7787 				if (a[i] != NULL) {
7788 					GET_DOUBLE(y1, i, "VectorfieldPlot");
7789 					i++;
7790 					if (a[i] != NULL) {
7791 						GET_DOUBLE(y2, i, "VectorfieldPlot");
7792 						i++;
7793 					}
7794 				}
7795 			}
7796 			/* FIXME: what about errors */
7797 			if G_UNLIKELY (gel_error_num != 0) {
7798 				gel_error_num = 0;
7799 				goto whack_copied_funcs;
7800 			}
7801 		}
7802 	}
7803 
7804 	if G_UNLIKELY (x1 > x2) {
7805 		double s = x1;
7806 		x1 = x2;
7807 		x2 = s;
7808 	}
7809 
7810 	if G_UNLIKELY (y1 > y2) {
7811 		double s = y1;
7812 		y1 = y2;
7813 		y2 = s;
7814 	}
7815 
7816 	if G_UNLIKELY (x1 == x2) {
7817 		gel_errorout (_("%s: invalid X range"), "VectorfieldPlot");
7818 		goto whack_copied_funcs;
7819 	}
7820 
7821 	if G_UNLIKELY (y1 == y2) {
7822 		gel_errorout (_("%s: invalid Y range"), "VectorfieldPlot");
7823 		goto whack_copied_funcs;
7824 	}
7825 
7826 	line_plot_clear_funcs ();
7827 
7828 	vectorfield_func_x = funcx;
7829 	vectorfield_func_y = funcy;
7830 
7831 	reset_plotx1 = plotx1 = x1;
7832 	reset_plotx2 = plotx2 = x2;
7833 	reset_ploty1 = ploty1 = y1;
7834 	reset_ploty2 = ploty2 = y2;
7835 
7836 	vectorfield_normalize_arrow_length =
7837 		vectorfield_normalize_arrow_length_parameter;
7838 
7839 	plotVtick = plot_vf_Vtick;
7840 	plotHtick = plot_vf_Htick;
7841 
7842 	plot_mode = MODE_LINEPLOT_VECTORFIELD;
7843 	plot_functions (FALSE /* do_window_present */,
7844 			FALSE /* from_gui */,
7845 			FALSE /* fit */);
7846 
7847 	if (gel_interrupted)
7848 		return NULL;
7849 	else
7850 		return gel_makenum_null ();
7851 
7852 whack_copied_funcs:
7853 	d_freefunc (funcx);
7854 	funcx = NULL;
7855 	d_freefunc (funcy);
7856 	funcy = NULL;
7857 
7858 	return NULL;
7859 }
7860 
7861 static GelETree *
LinePlot_op(GelCtx * ctx,GelETree ** a,int * exception)7862 LinePlot_op (GelCtx *ctx, GelETree * * a, int *exception)
7863 {
7864 	double x1, x2, y1, y2;
7865 	int funcs = 0;
7866 	gboolean fity = FALSE;
7867 	GelEFunc *func[MAXFUNC] = { NULL };
7868 	int i;
7869 
7870 	if G_UNLIKELY (plot_in_progress != 0) {
7871 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
7872 			      "LinePlot", "LinePlot");
7873 		return NULL;
7874 	}
7875 
7876 	for (i = 0;
7877 	     i < MAXFUNC && a[i] != NULL && a[i]->type == GEL_FUNCTION_NODE;
7878 	     i++) {
7879 		func[funcs] = d_copyfunc (a[i]->func.func);
7880 		func[funcs]->context = -1;
7881 		funcs++;
7882 	}
7883 
7884 	if G_UNLIKELY (a[i] != NULL && a[i]->type == GEL_FUNCTION_NODE) {
7885 		gel_errorout (_("%s: only up to 10 functions supported"), "LinePlot");
7886 		goto whack_copied_funcs;
7887 	}
7888 
7889 	if G_UNLIKELY (funcs == 0) {
7890 		gel_errorout (_("%s: argument not a function"), "LinePlot");
7891 		goto whack_copied_funcs;
7892 	}
7893 
7894 	/* Defaults */
7895 	x1 = defx1;
7896 	x2 = defx2;
7897 	y1 = defy1;
7898 	y2 = defy2;
7899 
7900 	if (a[i] != NULL) {
7901 		if (a[i]->type == GEL_MATRIX_NODE) {
7902 			if (gel_matrixw_elements (a[i]->mat.matrix) == 4) {
7903 				if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
7904 					goto whack_copied_funcs;
7905 				fity = FALSE;
7906 			} else if (gel_matrixw_elements (a[i]->mat.matrix) == 2) {
7907 				if ( ! get_limits_from_matrix_xonly (a[i], &x1, &x2))
7908 					goto whack_copied_funcs;
7909 				fity = TRUE;
7910 			} else {
7911 				gel_errorout (_("Graph limits not given as a 2-vector or a 4-vector"));
7912 				goto whack_copied_funcs;
7913 			}
7914 			i++;
7915 		} else {
7916 			GET_DOUBLE(x1, i, "LinePlot");
7917 			i++;
7918 			if (a[i] != NULL) {
7919 				GET_DOUBLE(x2, i, "LinePlot");
7920 				i++;
7921 				fity = TRUE;
7922 				if (a[i] != NULL) {
7923 					GET_DOUBLE(y1, i, "LinePlot");
7924 					i++;
7925 					fity = FALSE;
7926 					if (a[i] != NULL) {
7927 						GET_DOUBLE(y2, i, "LinePlot");
7928 						i++;
7929 					}
7930 				}
7931 			}
7932 			/* FIXME: what about errors */
7933 			if G_UNLIKELY (gel_error_num != 0) {
7934 				gel_error_num = 0;
7935 				goto whack_copied_funcs;
7936 			}
7937 		}
7938 	}
7939 
7940 	if G_UNLIKELY (x1 > x2) {
7941 		double s = x1;
7942 		x1 = x2;
7943 		x2 = s;
7944 	}
7945 
7946 	if G_UNLIKELY (y1 > y2) {
7947 		double s = y1;
7948 		y1 = y2;
7949 		y2 = s;
7950 	}
7951 
7952 	if G_UNLIKELY (x1 == x2) {
7953 		gel_errorout (_("%s: invalid X range"), "LinePlot");
7954 		goto whack_copied_funcs;
7955 	}
7956 
7957 	if G_UNLIKELY (y1 == y2) {
7958 		gel_errorout (_("%s: invalid Y range"), "LinePlot");
7959 		goto whack_copied_funcs;
7960 	}
7961 
7962 	line_plot_clear_funcs ();
7963 
7964 	for (i = 0; i < MAXFUNC && func[i] != NULL; i++) {
7965 		plot_func[i] = func[i];
7966 		func[i] = NULL;
7967 	}
7968 
7969 	reset_plotx1 = plotx1 = x1;
7970 	reset_plotx2 = plotx2 = x2;
7971 	reset_ploty1 = ploty1 = y1;
7972 	reset_ploty2 = ploty2 = y2;
7973 
7974 	plot_mode = MODE_LINEPLOT;
7975 	plot_functions (FALSE /* do_window_present */,
7976 			FALSE /* from_gui */,
7977 			fity /* fit */);
7978 
7979 	if (fity) {
7980 		reset_ploty1 = ploty1;
7981 		reset_ploty2 = ploty2;
7982 	}
7983 
7984 	if G_UNLIKELY (gel_interrupted)
7985 		return NULL;
7986 	else
7987 		return gel_makenum_null ();
7988 
7989 whack_copied_funcs:
7990 	for (i = 0; i < MAXFUNC && func[i] != NULL; i++) {
7991 		d_freefunc (func[i]);
7992 		func[i] = NULL;
7993 	}
7994 
7995 	return NULL;
7996 }
7997 
7998 static GelETree *
LinePlotParametric_op(GelCtx * ctx,GelETree ** a,int * exception)7999 LinePlotParametric_op (GelCtx *ctx, GelETree * * a, int *exception)
8000 {
8001 	double x1, x2, y1, y2, t1, t2, tinc;
8002 	gboolean fit = FALSE;
8003 	GelEFunc *funcx = NULL;
8004 	GelEFunc *funcy = NULL;
8005 	int i;
8006 
8007 	if G_UNLIKELY (plot_in_progress != 0) {
8008 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
8009 			      "LinePlotParametric", "LinePlotParametric");
8010 		return NULL;
8011 	}
8012 
8013 	if G_UNLIKELY (a[0] == NULL || a[1] == NULL ||
8014 		       a[0]->type != GEL_FUNCTION_NODE ||
8015 		       a[1]->type != GEL_FUNCTION_NODE) {
8016 		gel_errorout (_("%s: First two arguments must be functions"), "LinePlotParametric");
8017 		return NULL;
8018 	}
8019 
8020 	funcx = d_copyfunc (a[0]->func.func);
8021 	funcx->context = -1;
8022 	funcy = d_copyfunc (a[1]->func.func);
8023 	funcy->context = -1;
8024 
8025 	/* Defaults */
8026 	x1 = defx1;
8027 	x2 = defx2;
8028 	y1 = defy1;
8029 	y2 = defy2;
8030 	t1 = deft1;
8031 	t2 = deft2;
8032 	tinc = deftinc;
8033 
8034 	i = 2;
8035 
8036 	/* Get t limits */
8037 	if (a[i] != NULL) {
8038 		GET_DOUBLE(t1, i, "LinePlotParametric");
8039 		i++;
8040 		if (a[i] != NULL) {
8041 			GET_DOUBLE(t2, i, "LinePlotParametric");
8042 			i++;
8043 			if (a[i] != NULL) {
8044 				GET_DOUBLE(tinc, i, "LinePlotParametric");
8045 				i++;
8046 			}
8047 		}
8048 		/* FIXME: what about errors */
8049 		if G_UNLIKELY (gel_error_num != 0) {
8050 			gel_error_num = 0;
8051 			goto whack_copied_funcs;
8052 		}
8053 	}
8054 
8055 	/* Get window limits */
8056 	if (a[i] != NULL) {
8057 		if ( (a[i]->type == GEL_STRING_NODE &&
8058 		      strcasecmp (a[i]->str.str, "fit") == 0) ||
8059 		     (a[i]->type == GEL_IDENTIFIER_NODE &&
8060 		      strcasecmp (a[i]->id.id->token, "fit") == 0)) {
8061 			fit = TRUE;
8062 		} else if (a[i]->type == GEL_MATRIX_NODE) {
8063 			if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
8064 				goto whack_copied_funcs;
8065 			i++;
8066 		} else {
8067 			GET_DOUBLE(x1, i, "LinePlotParametric");
8068 			i++;
8069 			if (a[i] != NULL) {
8070 				GET_DOUBLE(x2, i, "LinePlotParametric");
8071 				i++;
8072 				if (a[i] != NULL) {
8073 					GET_DOUBLE(y1, i, "LinePlotParametric");
8074 					i++;
8075 					if (a[i] != NULL) {
8076 						GET_DOUBLE(y2, i, "LinePlotParametric");
8077 						i++;
8078 					}
8079 				}
8080 			}
8081 			/* FIXME: what about errors */
8082 			if G_UNLIKELY (gel_error_num != 0) {
8083 				gel_error_num = 0;
8084 				goto whack_copied_funcs;
8085 			}
8086 		}
8087 	}
8088 
8089 	if G_UNLIKELY (x1 > x2) {
8090 		double s = x1;
8091 		x1 = x2;
8092 		x2 = s;
8093 	}
8094 
8095 	if G_UNLIKELY (y1 > y2) {
8096 		double s = y1;
8097 		y1 = y2;
8098 		y2 = s;
8099 	}
8100 
8101 	if G_UNLIKELY (x1 == x2) {
8102 		gel_errorout (_("%s: invalid X range"), "LinePlotParametric");
8103 		goto whack_copied_funcs;
8104 	}
8105 
8106 	if G_UNLIKELY (y1 == y2) {
8107 		gel_errorout (_("%s: invalid Y range"), "LinePlotParametric");
8108 		goto whack_copied_funcs;
8109 	}
8110 
8111 	if G_UNLIKELY (t1 >= t2 || tinc <= 0) {
8112 		gel_errorout (_("%s: invalid T range"), "LinePlotParametric");
8113 		goto whack_copied_funcs;
8114 	}
8115 
8116 	line_plot_clear_funcs ();
8117 
8118 	parametric_func_x = funcx;
8119 	parametric_func_y = funcy;
8120 
8121 	reset_plotx1 = plotx1 = x1;
8122 	reset_plotx2 = plotx2 = x2;
8123 	reset_ploty1 = ploty1 = y1;
8124 	reset_ploty2 = ploty2 = y2;
8125 
8126 	plott1 = t1;
8127 	plott2 = t2;
8128 	plottinc = tinc;
8129 
8130 	plot_mode = MODE_LINEPLOT_PARAMETRIC;
8131 	plot_functions (FALSE /* do_window_present */,
8132 			FALSE /* from_gui */,
8133 			fit /* fit */);
8134 
8135 	if (fit) {
8136 		reset_plotx1 = plotx1;
8137 		reset_plotx2 = plotx2;
8138 		reset_ploty1 = ploty1;
8139 		reset_ploty2 = ploty2;
8140 	}
8141 
8142 	if G_UNLIKELY (gel_interrupted)
8143 		return NULL;
8144 	else
8145 		return gel_makenum_null ();
8146 
8147 whack_copied_funcs:
8148 	d_freefunc (funcx);
8149 	funcx = NULL;
8150 	d_freefunc (funcy);
8151 	funcy = NULL;
8152 
8153 	return NULL;
8154 }
8155 
8156 static GelETree *
LinePlotCParametric_op(GelCtx * ctx,GelETree ** a,int * exception)8157 LinePlotCParametric_op (GelCtx *ctx, GelETree * * a, int *exception)
8158 {
8159 	double x1, x2, y1, y2, t1, t2, tinc;
8160 	gboolean fit = FALSE;
8161 	GelEFunc *func = NULL;
8162 	int i;
8163 
8164 	if G_UNLIKELY (plot_in_progress != 0) {
8165 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
8166 			      "LinePlotCParametric", "LinePlotCParametric");
8167 		return NULL;
8168 	}
8169 
8170 	if G_UNLIKELY (a[0] == NULL ||
8171 		       a[0]->type != GEL_FUNCTION_NODE) {
8172 		gel_errorout (_("%s: First argument must be a function"),
8173 			      "LinePlotCParametric");
8174 		return NULL;
8175 	}
8176 
8177 	func = d_copyfunc (a[0]->func.func);
8178 	func->context = -1;
8179 
8180 	/* Defaults */
8181 	x1 = defx1;
8182 	x2 = defx2;
8183 	y1 = defy1;
8184 	y2 = defy2;
8185 	t1 = deft1;
8186 	t2 = deft2;
8187 	tinc = deftinc;
8188 
8189 	i = 1;
8190 
8191 	/* Get t limits */
8192 	if (a[i] != NULL) {
8193 		GET_DOUBLE(t1, i, "LinePlotCParametric");
8194 		i++;
8195 		if (a[i] != NULL) {
8196 			GET_DOUBLE(t2, i, "LinePlotCParametric");
8197 			i++;
8198 			if (a[i] != NULL) {
8199 				GET_DOUBLE(tinc, i, "LinePlotCParametric");
8200 				i++;
8201 			}
8202 		}
8203 		/* FIXME: what about errors */
8204 		if G_UNLIKELY (gel_error_num != 0) {
8205 			gel_error_num = 0;
8206 			goto whack_copied_funcs;
8207 		}
8208 	}
8209 
8210 	/* Get window limits */
8211 	if (a[i] != NULL) {
8212 		if ( (a[i]->type == GEL_STRING_NODE &&
8213 		      strcasecmp (a[i]->str.str, "fit") == 0) ||
8214 		     (a[i]->type == GEL_IDENTIFIER_NODE &&
8215 		      strcasecmp (a[i]->id.id->token, "fit") == 0)) {
8216 			fit = TRUE;
8217 		} else if (a[i]->type == GEL_MATRIX_NODE) {
8218 			if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
8219 				goto whack_copied_funcs;
8220 			i++;
8221 		} else {
8222 			GET_DOUBLE(x1, i, "LinePlotCParametric");
8223 			i++;
8224 			if (a[i] != NULL) {
8225 				GET_DOUBLE(x2, i, "LinePlotCParametric");
8226 				i++;
8227 				if (a[i] != NULL) {
8228 					GET_DOUBLE(y1, i, "LinePlotCParametric");
8229 					i++;
8230 					if (a[i] != NULL) {
8231 						GET_DOUBLE(y2, i, "LinePlotCParametric");
8232 						i++;
8233 					}
8234 				}
8235 			}
8236 			/* FIXME: what about errors */
8237 			if G_UNLIKELY (gel_error_num != 0) {
8238 				gel_error_num = 0;
8239 				goto whack_copied_funcs;
8240 			}
8241 		}
8242 	}
8243 
8244 	if G_UNLIKELY (x1 > x2) {
8245 		double s = x1;
8246 		x1 = x2;
8247 		x2 = s;
8248 	}
8249 
8250 	if G_UNLIKELY (y1 > y2) {
8251 		double s = y1;
8252 		y1 = y2;
8253 		y2 = s;
8254 	}
8255 
8256 	if G_UNLIKELY (x1 == x2) {
8257 		gel_errorout (_("%s: invalid X range"), "LinePlotCParametric");
8258 		goto whack_copied_funcs;
8259 	}
8260 
8261 	if G_UNLIKELY (y1 == y2) {
8262 		gel_errorout (_("%s: invalid Y range"), "LinePlotCParametric");
8263 		goto whack_copied_funcs;
8264 	}
8265 
8266 	if G_UNLIKELY (t1 >= t2 || tinc <= 0) {
8267 		gel_errorout (_("%s: invalid T range"), "LinePlotCParametric");
8268 		goto whack_copied_funcs;
8269 	}
8270 
8271 	line_plot_clear_funcs ();
8272 
8273 	parametric_func_z = func;
8274 
8275 	reset_plotx1 = plotx1 = x1;
8276 	reset_plotx2 = plotx2 = x2;
8277 	reset_ploty1 = ploty1 = y1;
8278 	reset_ploty2 = ploty2 = y2;
8279 
8280 	plott1 = t1;
8281 	plott2 = t2;
8282 	plottinc = tinc;
8283 
8284 	plot_mode = MODE_LINEPLOT_PARAMETRIC;
8285 	plot_functions (FALSE /* do_window_present */,
8286 			FALSE /* from_gui */,
8287 			fit /* fit */);
8288 
8289 	if (fit) {
8290 		reset_plotx1 = plotx1;
8291 		reset_plotx2 = plotx2;
8292 		reset_ploty1 = ploty1;
8293 		reset_ploty2 = ploty2;
8294 	}
8295 
8296 	if G_UNLIKELY (gel_interrupted)
8297 		return NULL;
8298 	else
8299 		return gel_makenum_null ();
8300 
8301 whack_copied_funcs:
8302 	d_freefunc (func);
8303 	func = NULL;
8304 
8305 	return NULL;
8306 }
8307 
8308 static GelETree *
LinePlotClear_op(GelCtx * ctx,GelETree ** a,int * exception)8309 LinePlotClear_op (GelCtx *ctx, GelETree * * a, int *exception)
8310 {
8311 	if G_UNLIKELY (plot_in_progress != 0) {
8312 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
8313 			      "LinePlotClear", "LinePlotClear");
8314 		return NULL;
8315 	}
8316 
8317 	line_plot_clear_funcs ();
8318 
8319 	/* This will just clear the window */
8320 	plot_mode = MODE_LINEPLOT;
8321 	plot_functions (FALSE /* do_window_present */,
8322 			FALSE /* from_gui */,
8323 			FALSE /* fit */);
8324 
8325 	if G_UNLIKELY (gel_interrupted)
8326 		return NULL;
8327 	else
8328 		return gel_makenum_null ();
8329 }
8330 
8331 static GelETree *
SurfacePlotClear_op(GelCtx * ctx,GelETree ** a,int * exception)8332 SurfacePlotClear_op (GelCtx *ctx, GelETree * * a, int *exception)
8333 {
8334 	if G_UNLIKELY (plot_in_progress != 0) {
8335 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
8336 			      "SurfacePlotClear", "SurfacePlotClear");
8337 		return NULL;
8338 	}
8339 
8340 
8341 	if (surface_func != NULL) {
8342 		d_freefunc (surface_func);
8343 		surface_func = NULL;
8344 	}
8345 	g_free (surface_func_name);
8346 	surface_func_name = NULL;
8347 
8348 	/* don't plot from data */
8349 	if (surface_data_x != NULL) {
8350 		g_free (surface_data_x);
8351 		surface_data_x = NULL;
8352 	}
8353 	if (surface_data_y != NULL) {
8354 		g_free (surface_data_y);
8355 		surface_data_y = NULL;
8356 	}
8357 	if (surface_data_z != NULL) {
8358 		g_free (surface_data_z);
8359 		surface_data_z = NULL;
8360 	}
8361 	if (surface_data != NULL) {
8362 		gtk_plot_data_set_x (GTK_PLOT_DATA (surface_data), NULL);
8363 		gtk_plot_data_set_y (GTK_PLOT_DATA (surface_data), NULL);
8364 		gtk_plot_data_set_z (GTK_PLOT_DATA (surface_data), NULL);
8365 	}
8366 
8367 	plot_mode = MODE_SURFACE;
8368 	plot_surface_functions (TRUE /* do_window_present */,
8369 				FALSE /*fit*/);
8370 
8371 	if G_UNLIKELY (gel_interrupted)
8372 		return NULL;
8373 	else
8374 		return gel_makenum_null ();
8375 }
8376 
8377 static int plot_canvas_freeze_count = 0;
8378 
8379 static void
plot_freeze(void)8380 plot_freeze (void) {
8381 	if (plot_canvas_freeze_count == 0) {
8382 		if (plot_canvas != NULL /* sanity */)
8383 			gtk_plot_canvas_freeze (GTK_PLOT_CANVAS (plot_canvas));
8384 	}
8385 
8386 	plot_canvas_freeze_count ++;
8387 
8388 }
8389 
8390 static GelETree *
PlotCanvasFreeze_op(GelCtx * ctx,GelETree ** a,int * exception)8391 PlotCanvasFreeze_op (GelCtx *ctx, GelETree * * a, int *exception)
8392 {
8393 	plot_freeze ();
8394 
8395 	return gel_makenum_null ();
8396 }
8397 
8398 static void
plot_thaw(void)8399 plot_thaw (void)
8400 {
8401 	if (plot_canvas_freeze_count > 0) {
8402 		plot_canvas_freeze_count --;
8403 		if (plot_canvas_freeze_count == 0) {
8404 			if (plot_canvas != NULL /* sanity */) {
8405 				plot_in_progress ++;
8406 				gel_calc_running ++;
8407 				gtk_plot_canvas_thaw (GTK_PLOT_CANVAS (plot_canvas));
8408 				gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
8409 				gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
8410 				plot_in_progress --;
8411 				gel_calc_running --;
8412 				plot_window_setup ();
8413 
8414 				if (gel_evalnode_hook != NULL)
8415 					(*gel_evalnode_hook)();
8416 			}
8417 		}
8418 	}
8419 }
8420 
8421 static GelETree *
PlotCanvasThaw_op(GelCtx * ctx,GelETree ** a,int * exception)8422 PlotCanvasThaw_op (GelCtx *ctx, GelETree * * a, int *exception)
8423 {
8424 	plot_thaw ();
8425 
8426 	return gel_makenum_null ();
8427 }
8428 
8429 static GelETree *
PlotWindowPresent_op(GelCtx * ctx,GelETree ** a,int * exception)8430 PlotWindowPresent_op (GelCtx *ctx, GelETree * * a, int *exception)
8431 {
8432 	ensure_window (TRUE /* present */);
8433 
8434 	return gel_makenum_null ();
8435 }
8436 
8437 static GelETree *
LinePlotWaitForClick_op(GelCtx * ctx,GelETree ** a,int * exception)8438 LinePlotWaitForClick_op (GelCtx *ctx, GelETree * * a, int *exception)
8439 {
8440 	GelETree *n;
8441 	GelMatrixW *m;
8442 
8443 	ensure_window (FALSE /* do_window_present */);
8444 
8445 	if (plot_mode != MODE_LINEPLOT &&
8446 	    plot_mode != MODE_LINEPLOT_PARAMETRIC &&
8447 	    plot_mode != MODE_LINEPLOT_SLOPEFIELD &&
8448 	    plot_mode != MODE_LINEPLOT_VECTORFIELD) {
8449 		plot_mode = MODE_LINEPLOT;
8450 		clear_graph ();
8451 	}
8452 
8453 	if (line_plot == NULL) {
8454 		add_line_plot ();
8455 		plot_setup_axis ();
8456 	}
8457 
8458 	wait_for_click = TRUE;
8459 
8460 	while (wait_for_click) {
8461 		while (gtk_events_pending ())
8462 			gtk_main_iteration ();
8463 		if (gel_interrupted) {
8464 			wait_for_click = FALSE;
8465 			return NULL;
8466 		}
8467 		if (line_plot == NULL) {
8468 			wait_for_click = FALSE;
8469 			return gel_makenum_null ();
8470 		}
8471 	}
8472 
8473 	/*make us a new empty node*/
8474 	GEL_GET_NEW_NODE (n);
8475 	n->type = GEL_MATRIX_NODE;
8476 	m = n->mat.matrix = gel_matrixw_new ();
8477 	n->mat.quoted = FALSE;
8478 	gel_matrixw_set_size (m, 2, 1);
8479 
8480 	gel_matrixw_set_indexii (m, 0) = gel_makenum_d (click_x);
8481 	gel_matrixw_set_index (m, 1, 0) = gel_makenum_d (click_y);
8482 
8483 	return n;
8484 }
8485 
8486 static GelETree *
LinePlotMouseLocation_op(GelCtx * ctx,GelETree ** a,int * exception)8487 LinePlotMouseLocation_op (GelCtx *ctx, GelETree * * a, int *exception)
8488 {
8489 	if (line_plot != NULL) {
8490 		GdkSeat *s;
8491 		GelETree *n;
8492 		GelMatrixW *m;
8493 		int xx, yy;
8494 		double x, y;
8495 
8496 	        s = gdk_display_get_default_seat (gdk_display_get_default ());
8497 	        gdk_window_get_device_position (gtk_widget_get_window
8498 	                                        (GTK_WIDGET (line_plot)),
8499 	                                        gdk_seat_get_pointer (s),
8500 	                                        &xx, &yy, NULL);
8501 		gtk_plot_get_point (GTK_PLOT (line_plot), xx, yy, &x, &y);
8502 
8503 		/*make us a new empty node*/
8504 		GEL_GET_NEW_NODE (n);
8505 		n->type = GEL_MATRIX_NODE;
8506 		m = n->mat.matrix = gel_matrixw_new ();
8507 		n->mat.quoted = FALSE;
8508 		gel_matrixw_set_size (m, 2, 1);
8509 
8510 		gel_matrixw_set_indexii (m, 0) = gel_makenum_d (x);
8511 		gel_matrixw_set_index (m, 1, 0) = gel_makenum_d (y);
8512 
8513 		return n;
8514 	} else {
8515 		gel_errorout (_("%s: Not in line plot mode.  Perhaps run LinePlot or LinePlotClear first."),
8516 			      "LinePlotMouseLocation");
8517 		return gel_makenum_null ();
8518 	}
8519 }
8520 
8521 void
gel_plot_canvas_thaw_completely(void)8522 gel_plot_canvas_thaw_completely (void)
8523 {
8524 	if (plot_canvas_freeze_count > 0) {
8525 		plot_canvas_freeze_count = 0;
8526 		if (plot_canvas != NULL /* sanity */) {
8527 			plot_in_progress ++;
8528 			gel_calc_running ++;
8529 			gtk_plot_canvas_thaw (GTK_PLOT_CANVAS (plot_canvas));
8530 			gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
8531 			gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
8532 			plot_in_progress --;
8533 			gel_calc_running --;
8534 
8535 			/* a hook will get called eventually, no need to call it now,
8536 			 * this only gets called once we're about to be idle and get out of
8537 			 * computation */
8538 		}
8539 	}
8540 
8541 	/*always do setup */
8542 	plot_window_setup ();
8543 }
8544 
8545 static gboolean
update_lineplot_window(double x1,double x2,double y1,double y2)8546 update_lineplot_window (double x1, double x2, double y1, double y2)
8547 {
8548 	if G_UNLIKELY (x1 > x2) {
8549 		double s = x1;
8550 		x1 = x2;
8551 		x2 = s;
8552 	}
8553 
8554 	if G_UNLIKELY (y1 > y2) {
8555 		double s = y1;
8556 		y1 = y2;
8557 		y2 = s;
8558 	}
8559 
8560 	reset_plotx1 = defx1 = x1;
8561 	reset_plotx2 = defx2 = x2;
8562 	reset_ploty1 = defy1 = y1;
8563 	reset_ploty2 = defy2 = y2;
8564 
8565 	if (plotx1 == x1 &&
8566 	    plotx2 == x2 &&
8567 	    ploty1 == y1 &&
8568 	    ploty2 == y2)
8569 		return FALSE;
8570 
8571 	plotx1 = x1;
8572 	plotx2 = x2;
8573 	ploty1 = y1;
8574 	ploty2 = y2;
8575 
8576 	return TRUE;
8577 }
8578 
8579 static gboolean
update_surfaceplot_window(double x1,double x2,double y1,double y2,double z1,double z2)8580 update_surfaceplot_window (double x1, double x2, double y1, double y2, double z1, double z2)
8581 {
8582 	gboolean update = FALSE;
8583 
8584 	if G_UNLIKELY (x1 > x2) {
8585 		double s = x1;
8586 		x1 = x2;
8587 		x2 = s;
8588 	}
8589 
8590 	if G_UNLIKELY (y1 > y2) {
8591 		double s = y1;
8592 		y1 = y2;
8593 		y2 = s;
8594 	}
8595 
8596 	if G_UNLIKELY (z1 > z2) {
8597 		double s = z1;
8598 		z1 = z2;
8599 		z2 = s;
8600 	}
8601 
8602 
8603 	if (surfacex1 != x1 ||
8604 	    surfacex2 != x2 ||
8605 	    surfacey1 != y1 ||
8606 	    surfacey2 != y2 ||
8607 	    surfacez1 != z1 ||
8608 	    surfacez2 != z2)
8609 		update = TRUE;
8610 
8611 	reset_surfacex1 = surfacex1 = surf_defx1 = x1;
8612 	reset_surfacex2 = surfacex2 = surf_defx2 = x2;
8613 	reset_surfacey1 = surfacey1 = surf_defy1 = y1;
8614 	reset_surfacey2 = surfacey2 = surf_defy2 = y2;
8615 	reset_surfacez1 = surfacez1 = surf_defz1 = z1;
8616 	reset_surfacez2 = surfacez2 = surf_defz2 = z2;
8617 
8618 	return update;
8619 }
8620 
8621 
8622 static gboolean
get_line_numbers(GelETree * a,double ** x,double ** y,int * len,double * minx,double * maxx,double * miny,double * maxy,const char * funcname,int minn)8623 get_line_numbers (GelETree *a, double **x, double **y, int *len,
8624 		  double *minx, double *maxx, double *miny, double *maxy,
8625 		  const char *funcname, int minn)
8626 {
8627 	int i;
8628 	GelMatrixW *m;
8629 	gboolean nominmax = TRUE;
8630 #define UPDATE_MINMAX \
8631 	if (minx != NULL) { \
8632 		if (xx > *maxx || nominmax) *maxx = xx; \
8633 		if (xx < *minx || nominmax) *minx = xx; \
8634 		if (yy > *maxy || nominmax) *maxy = yy; \
8635 		if (yy < *miny || nominmax) *miny = yy; \
8636 		nominmax = FALSE; \
8637 	}
8638 
8639 	g_return_val_if_fail (a->type == GEL_MATRIX_NODE, FALSE);
8640 
8641 	m = a->mat.matrix;
8642 
8643 	if G_UNLIKELY ( ! gel_is_matrix_value_only (m)) {
8644 		gel_errorout (_("%s: Points should be given as a real, n by 2 matrix "
8645 				"with columns for x and y, n>=%d, or as a complex "
8646 				"valued n by 1 matrix"),
8647 			      funcname, minn);
8648 		return FALSE;
8649 	}
8650 
8651 	if (gel_matrixw_width (m) == 2 &&
8652 	    gel_matrixw_height (m) >= minn) {
8653 		if G_UNLIKELY ( ! gel_is_matrix_value_only_real (m)) {
8654 			gel_errorout (_("%s: If points are given as an n by 2 matrix, then this matrix must be real"),
8655 				      funcname);
8656 			return FALSE;
8657 		}
8658 
8659 		*len = gel_matrixw_height (m);
8660 
8661 
8662 		*x = g_new (double, *len);
8663 		*y = g_new (double, *len);
8664 
8665 		for (i = 0; i < *len; i++) {
8666 			double xx, yy;
8667 			GelETree *t = gel_matrixw_index (m, 0, i);
8668 			(*x)[i] = xx = mpw_get_double (t->val.value);
8669 			t = gel_matrixw_index (m, 1, i);
8670 			(*y)[i] = yy = mpw_get_double (t->val.value);
8671 			UPDATE_MINMAX
8672 		}
8673 	} else if (gel_matrixw_width (m) == 1 &&
8674 		   gel_matrixw_height (m) >= minn) {
8675 		*len = gel_matrixw_height (m);
8676 
8677 
8678 		*x = g_new (double, *len);
8679 		*y = g_new (double, *len);
8680 
8681 		for (i = 0; i < *len; i++) {
8682 			double xx, yy;
8683 			GelETree *t = gel_matrixw_index (m, 0, i);
8684 			mpw_get_complex_double (t->val.value, &xx, &yy);
8685 			(*x)[i] = xx;
8686 			(*y)[i] = yy;
8687 			UPDATE_MINMAX
8688 		}
8689 		/*
8690 		 * This was undocumented and conflicts with using complex numbers
8691 	} else if (gel_matrixw_width (m) == 1 &&
8692 		   gel_matrixw_height (m) % 2 == 0 &&
8693 		   gel_matrixw_height (m) >= 2*minn) {
8694 		*len = gel_matrixw_height (m) / 2;
8695 
8696 		*x = g_new (double, *len);
8697 		*y = g_new (double, *len);
8698 
8699 		for (i = 0; i < *len; i++) {
8700 			double xx, yy;
8701 			GelETree *t = gel_matrixw_index (m, 0, 2*i);
8702 			(*x)[i] = xx = mpw_get_double (t->val.value);
8703 			t = gel_matrixw_index (m, 0, (2*i) + 1);
8704 			(*y)[i] = yy = mpw_get_double (t->val.value);
8705 			UPDATE_MINMAX
8706 		}
8707 	} else if (gel_matrixw_height (m) == 1 &&
8708 		   gel_matrixw_width (m) % 2 == 0 &&
8709 		   gel_matrixw_width (m) >= 2*minn) {
8710 		*len = gel_matrixw_width (m) / 2;
8711 
8712 		*x = g_new (double, *len);
8713 		*y = g_new (double, *len);
8714 
8715 		for (i = 0; i < *len; i++) {
8716 			double xx, yy;
8717 			GelETree *t = gel_matrixw_index (m, 2*i, 0);
8718 			(*x)[i] = xx = mpw_get_double (t->val.value);
8719 			t = gel_matrixw_index (m, (2*i) + 1, 0);
8720 			(*y)[i] = yy = mpw_get_double (t->val.value);
8721 			UPDATE_MINMAX
8722 		}
8723 		*/
8724 	} else {
8725 		gel_errorout (_("%s: Points should be given as a real, n by 2 matrix "
8726 				"with columns for x and y, n>=%d, or as a complex "
8727 				"valued n by 1 matrix"),
8728 			      funcname, minn);
8729 		return FALSE;
8730 	}
8731 
8732 	return TRUE;
8733 #undef UPDATE_MINMAX
8734 }
8735 
8736 
8737 static gboolean
get_surface_line_numbers(GelETree * a,double ** x,double ** y,double ** z,int * len,double * minx,double * maxx,double * miny,double * maxy,double * minz,double * maxz,const char * funcname,int minn)8738 get_surface_line_numbers (GelETree *a,
8739 			  double **x, double **y, double **z, int *len,
8740 			  double *minx, double *maxx,
8741 			  double *miny, double *maxy,
8742 			  double *minz, double *maxz,
8743 			  const char *funcname, int minn)
8744 {
8745 	int i;
8746 	GelMatrixW *m;
8747 	gboolean nominmax = TRUE;
8748 #define UPDATE_MINMAX \
8749 	if (minx != NULL) { \
8750 		if (xx > *maxx || nominmax) *maxx = xx; \
8751 		if (xx < *minx || nominmax) *minx = xx; \
8752 		if (yy > *maxy || nominmax) *maxy = yy; \
8753 		if (yy < *miny || nominmax) *miny = yy; \
8754 		if (zz > *maxz || nominmax) *maxz = zz; \
8755 		if (zz < *minz || nominmax) *minz = zz; \
8756 		nominmax = FALSE; \
8757 	}
8758 
8759 	g_return_val_if_fail (a->type == GEL_MATRIX_NODE, FALSE);
8760 
8761 	m = a->mat.matrix;
8762 
8763 	if G_UNLIKELY ( ! gel_is_matrix_value_only_real (m)) {
8764 		gel_errorout (_("%s: Points should be given as a real, n by 3 matrix "
8765 				"with columns for x, y and z, n>=%d"),
8766 			      funcname, minn);
8767 		return FALSE;
8768 	}
8769 
8770 	if (gel_matrixw_width (m) == 3 &&
8771 	    gel_matrixw_height (m) >= minn) {
8772 		*len = gel_matrixw_height (m);
8773 
8774 		*x = g_new (double, *len);
8775 		*y = g_new (double, *len);
8776 		*z = g_new (double, *len);
8777 
8778 		for (i = 0; i < *len; i++) {
8779 			double xx, yy, zz;
8780 			GelETree *t = gel_matrixw_index (m, 0, i);
8781 			(*x)[i] = xx = mpw_get_double (t->val.value);
8782 			t = gel_matrixw_index (m, 1, i);
8783 			(*y)[i] = yy = mpw_get_double (t->val.value);
8784 			t = gel_matrixw_index (m, 2, i);
8785 			(*z)[i] = zz = mpw_get_double (t->val.value);
8786 			UPDATE_MINMAX
8787 		}
8788 	} else if (gel_matrixw_width (m) == 1 &&
8789 		   gel_matrixw_height (m) % 3 == 0 &&
8790 		   gel_matrixw_height (m) >= 3*minn) {
8791 		*len = gel_matrixw_height (m) / 3;
8792 
8793 		*x = g_new (double, *len);
8794 		*y = g_new (double, *len);
8795 		*z = g_new (double, *len);
8796 
8797 		for (i = 0; i < *len; i++) {
8798 			double xx, yy, zz;
8799 			GelETree *t = gel_matrixw_index (m, 0, 2*i);
8800 			(*x)[i] = xx = mpw_get_double (t->val.value);
8801 			t = gel_matrixw_index (m, 0, (2*i) + 1);
8802 			(*y)[i] = yy = mpw_get_double (t->val.value);
8803 			t = gel_matrixw_index (m, 0, (2*i) + 2);
8804 			(*z)[i] = zz = mpw_get_double (t->val.value);
8805 			UPDATE_MINMAX
8806 		}
8807 	} else if (gel_matrixw_height (m) == 1 &&
8808 		   gel_matrixw_width (m) % 3 == 0 &&
8809 		   gel_matrixw_width (m) >= 3*minn) {
8810 		*len = gel_matrixw_width (m) / 3;
8811 
8812 		*x = g_new (double, *len);
8813 		*y = g_new (double, *len);
8814 		*z = g_new (double, *len);
8815 
8816 		for (i = 0; i < *len; i++) {
8817 			double xx, yy, zz;
8818 			GelETree *t = gel_matrixw_index (m, 2*i, 0);
8819 			(*x)[i] = xx = mpw_get_double (t->val.value);
8820 			t = gel_matrixw_index (m, (2*i) + 1, 0);
8821 			(*y)[i] = yy = mpw_get_double (t->val.value);
8822 			t = gel_matrixw_index (m, (2*i) + 2, 0);
8823 			(*z)[i] = zz = mpw_get_double (t->val.value);
8824 			UPDATE_MINMAX
8825 		}
8826 	} else {
8827 		gel_errorout (_("%s: Points should be given as a real, n by 3 matrix "
8828 				"with columns for x, y and z, n>=%d"),
8829 			      funcname, minn);
8830 		return FALSE;
8831 	}
8832 
8833 	return TRUE;
8834 #undef UPDATE_MINMAX
8835 }
8836 
8837 static void
draw_arrowhead(double xx1,double yy1,double xx2,double yy2,int thickness,GdkRGBA * color)8838 draw_arrowhead (double xx1, double yy1, double xx2, double yy2,
8839 		int thickness, GdkRGBA *color)
8840 {
8841 	double x1, x2, y1, y2, xm, ym;
8842 	double *ax, *ay;
8843 	double angle;
8844 
8845 	/* nowhere to go */
8846 	if (xx1 == xx2 && yy1 == yy2)
8847 		return;
8848 
8849 
8850 	gtk_plot_get_pixel (GTK_PLOT (line_plot), xx1, yy1, &x1, &y1);
8851 	gtk_plot_get_pixel (GTK_PLOT (line_plot), xx2, yy2, &x2, &y2);
8852 
8853 	angle = atan2 ( (y2-y1) , (x2-x1) );
8854 
8855 	ax = g_new (double, 3);
8856 	ay = g_new (double, 3);
8857 	ax[1] = xx2;
8858 	ay[1] = yy2;
8859 
8860 	xm = x2 - cos(angle) * /*al*/ 5* thickness;
8861 	ym = y2 - sin(angle) * /*al*/ 5* thickness;
8862 	gtk_plot_get_point (GTK_PLOT (line_plot),
8863 			    xm - sin(angle)* /*aw*/5* thickness / 2.0,
8864 			    ym + cos(angle)* /*aw*/5* thickness / 2.0,
8865 			    & (ax[0]), & (ay[0]));
8866 	gtk_plot_get_point (GTK_PLOT (line_plot),
8867 			    xm + sin(angle)* /*aw*/5* thickness / 2.0,
8868 			    ym - cos(angle)* /*aw*/5* thickness / 2.0,
8869 			    & (ax[2]), & (ay[2]));
8870 
8871 	draw_line (ax, ay, 3, thickness, color, NULL /* legend */,
8872 		   FALSE /* filled */);
8873 }
8874 
8875 static gboolean
get_color(GelETree * a,GdkRGBA * c,const char * funcname)8876 get_color (GelETree *a, GdkRGBA *c, const char *funcname)
8877 {
8878 	if (a == NULL) {
8879 		gel_errorout (_("%s: No color specified"),
8880 			      funcname);
8881 		return FALSE;
8882 	} else if (a->type == GEL_STRING_NODE) {
8883 		if ( ! gdk_rgba_parse (c, a->str.str)) {
8884 			gel_errorout (_("%s: Cannot parse color '%s'"),
8885 				      funcname, a->str.str);
8886 			return FALSE;
8887 		}
8888 		return TRUE;
8889 	} else if (a->type == GEL_IDENTIFIER_NODE) {
8890 		if ( ! gdk_rgba_parse (c, a->id.id->token)) {
8891 			gel_errorout (_("%s: Cannot parse color '%s'"),
8892 				      funcname, a->id.id->token);
8893 			return FALSE;
8894 		}
8895 		return TRUE;
8896 	} else if (a->type == GEL_MATRIX_NODE) {
8897 		GelMatrixW *m = a->mat.matrix;
8898 		GelETree *t;
8899 		double r;
8900 		double g;
8901 		double b;
8902 
8903 		if G_UNLIKELY ( ! gel_is_matrix_value_only_real (m) ||
8904 			       gel_matrixw_elements(m) != 3) {
8905 			gel_errorout (_("%s: A vector giving color should be a 3-vector of real numbers between 0 and 1"),
8906 				      funcname);
8907 			return FALSE;
8908 		}
8909 		/* we know we have 3 values, so we always get non-null t here that's a value node */
8910 		t = gel_matrixw_vindex (m, 0);
8911 		r = mpw_get_double (t->val.value);
8912 		t = gel_matrixw_vindex (m, 1);
8913 		g = mpw_get_double (t->val.value);
8914 		t = gel_matrixw_vindex (m, 2);
8915 		b = mpw_get_double (t->val.value);
8916 
8917 #define FUDGE 0.000001
8918 		if G_UNLIKELY ( r < -FUDGE || r > (1+FUDGE) ||
8919 				g < -FUDGE || g > (1+FUDGE) ||
8920 				b < -FUDGE || b > (1+FUDGE) ) {
8921 			gel_errorout (_("%s: Warning: Values for red, green, or blue out of range (0 to 1), I will clip them to this interval"),
8922 				      funcname);
8923 		}
8924 #undef FUDGE
8925 		r = MAX(MIN(r,1.0),0.0);
8926 		g = MAX(MIN(g,1.0),0.0);
8927 		b = MAX(MIN(b,1.0),0.0);
8928 
8929 		c->red = r;
8930 		c->green = g;
8931 		c->blue = b;
8932 		c->alpha = 1.0;
8933 
8934 		return TRUE;
8935 	}
8936 
8937 
8938 	gel_errorout (_("%s: Color must be a string or a three-vector of rgb values (between 0 and 1)"),
8939 		      funcname);
8940 
8941 	return FALSE;
8942 }
8943 
8944 
8945 static GelETree *
LinePlotDrawLine_op(GelCtx * ctx,GelETree ** a,int * exception)8946 LinePlotDrawLine_op (GelCtx *ctx, GelETree * * a, int *exception)
8947 {
8948 	int len;
8949 	int nextarg;
8950 	double *x, *y;
8951 	double minx = 0, miny = 0, maxx = 0, maxy = 0;
8952 	GdkRGBA color;
8953 	int thickness;
8954 	gboolean arrow_origin = FALSE;
8955 	gboolean arrow_end = FALSE;
8956 	gboolean filled = FALSE;
8957 	int i;
8958 	gboolean update = FALSE;
8959 	char *legend = NULL;
8960 
8961 	if G_UNLIKELY (plot_in_progress != 0) {
8962 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
8963 			      "LinePlotDrawLine", "LinePlotDrawLine");
8964 		return NULL;
8965 	}
8966 
8967 	ensure_window (FALSE /* do_window_present */);
8968 
8969 	if (a[0]->type == GEL_NULL_NODE) {
8970 		return gel_makenum_null ();
8971 	} else if (a[0]->type == GEL_MATRIX_NODE) {
8972 		if G_UNLIKELY ( ! get_line_numbers (a[0], &x, &y, &len,
8973 						    &minx, &maxx, &miny, &maxy,
8974 						    "LinePlotDrawLine",
8975 						    2))
8976 			return NULL;
8977 		nextarg = 1;
8978 	} else {
8979 		double x1, y1, x2, y2;
8980 		if G_UNLIKELY (gel_count_arguments (a) < 4) {
8981 			gel_errorout (_("%s: Wrong number of arguments"),
8982 				      "LinePlotDrawLine");
8983 			return NULL;
8984 		}
8985 		GET_DOUBLE(x1, 0, "LinePlotDrawLine");
8986 		GET_DOUBLE(y1, 1, "LinePlotDrawLine");
8987 		GET_DOUBLE(x2, 2, "LinePlotDrawLine");
8988 		GET_DOUBLE(y2, 3, "LinePlotDrawLine");
8989 		len = 2;
8990 		x = g_new (double, 2);
8991 		x[0] = x1;
8992 		x[1] = x2;
8993 		y = g_new (double, 2);
8994 		y[0] = y1;
8995 		y[1] = y2;
8996 		nextarg = 4;
8997 
8998 		minx = MIN(x1,x2);
8999 		maxx = MAX(x1,x2);
9000 		miny = MIN(y1,y2);
9001 		maxy = MAX(y1,y2);
9002 	}
9003 
9004 	gdk_rgba_parse (&color, "black");
9005 	thickness = 2;
9006 
9007 	for (i = nextarg; a[i] != NULL; i++) {
9008 		if G_LIKELY (a[i]->type == GEL_STRING_NODE ||
9009 			     a[i]->type == GEL_IDENTIFIER_NODE) {
9010 			GelToken *id;
9011 			static GelToken *colorid = NULL;
9012 			static GelToken *thicknessid = NULL;
9013 			static GelToken *windowid = NULL;
9014 			static GelToken *fitid = NULL;
9015 			static GelToken *arrowid = NULL;
9016 			static GelToken *originid = NULL;
9017 			static GelToken *endid = NULL;
9018 			static GelToken *bothid = NULL;
9019 			static GelToken *noneid = NULL;
9020 			static GelToken *legendid = NULL;
9021 			static GelToken *filledid = NULL;
9022 
9023 			if (colorid == NULL) {
9024 				colorid = d_intern ("color");
9025 				thicknessid = d_intern ("thickness");
9026 				windowid = d_intern ("window");
9027 				fitid = d_intern ("fit");
9028 				arrowid = d_intern ("arrow");
9029 				originid = d_intern ("origin");
9030 				endid = d_intern ("end");
9031 				bothid = d_intern ("both");
9032 				noneid = d_intern ("none");
9033 				legendid = d_intern ("legend");
9034 				filledid = d_intern ("filled");
9035 			}
9036 
9037 			if (a[i]->type == GEL_STRING_NODE)
9038 				id = d_intern (a[i]->str.str);
9039 			else
9040 				id = a[i]->id.id;
9041 			if (id == colorid) {
9042 			        if G_UNLIKELY ( ! get_color (a[i+1], &color, "LinePlotDrawLine")) {
9043 					g_free (legend);
9044 					g_free (x);
9045 					g_free (y);
9046 					return NULL;
9047 				}
9048 				i++;
9049 			} else if (id == thicknessid) {
9050 				if G_UNLIKELY (a[i+1] == NULL)  {
9051 					gel_errorout (_("%s: No thickness specified"),
9052 						      "LinePlotDrawLine");
9053 					g_free (legend);
9054 					g_free (x);
9055 					g_free (y);
9056 					return NULL;
9057 				}
9058 				if G_UNLIKELY ( ! check_argument_positive_integer (a, i+1,
9059 										   "LinePlotDrawLine")) {
9060 					g_free (legend);
9061 					g_free (x);
9062 					g_free (y);
9063 					return NULL;
9064 				}
9065 				thickness = gel_get_nonnegative_integer (a[i+1]->val.value,
9066 									 "LinePlotDrawLine");
9067 				i++;
9068 			} else if (id == windowid) {
9069 				double x1, x2, y1, y2;
9070 				if G_UNLIKELY (a[i+1] == NULL ||
9071 					       (a[i+1]->type != GEL_STRING_NODE &&
9072 						a[i+1]->type != GEL_IDENTIFIER_NODE &&
9073 						a[i+1]->type != GEL_MATRIX_NODE)) {
9074 					gel_errorout (_("%s: No window specified"),
9075 						      "LinePlotDrawLine");
9076 					g_free (legend);
9077 					g_free (x);
9078 					g_free (y);
9079 					return NULL;
9080 				}
9081 				if ((a[i+1]->type == GEL_STRING_NODE &&
9082 				     fitid == d_intern (a[i+1]->str.str)) ||
9083 				    (a[i+1]->type == GEL_IDENTIFIER_NODE &&
9084 				     fitid == a[i+1]->id.id)) {
9085 					x1 = minx;
9086 					x2 = maxx;
9087 					y1 = miny;
9088 					y2 = maxy;
9089 					if G_UNLIKELY (x1 == x2) {
9090 						x1 -= 0.1;
9091 						x2 += 0.1;
9092 					}
9093 
9094 					/* assume line is a graph so x fits tightly */
9095 
9096 					if G_UNLIKELY (y1 == y2) {
9097 						y1 -= 0.1;
9098 						y2 += 0.1;
9099 					} else {
9100 						/* Make window 5% larger on each vertical side */
9101 						double height = (y2-y1);
9102 						y1 -= height * 0.05;
9103 						y2 += height * 0.05;
9104 					}
9105 
9106 					update = update_lineplot_window (x1, x2, y1, y2);
9107 				} else if (get_limits_from_matrix (a[i+1], &x1, &x2, &y1, &y2)) {
9108 					update = update_lineplot_window (x1, x2, y1, y2);
9109 				} else {
9110 					g_free (legend);
9111 					g_free (x);
9112 					g_free (y);
9113 					return NULL;
9114 				}
9115 				i++;
9116 			} else if (id == arrowid) {
9117 				GelToken *astyleid;
9118 
9119 				if G_UNLIKELY (a[i+1] == NULL ||
9120 					       (a[i+1]->type != GEL_STRING_NODE &&
9121 						a[i+1]->type != GEL_IDENTIFIER_NODE)) {
9122 					gel_errorout (_("%s: arrow style should be \"origin\", \"end\", \"both\", or \"none\""),
9123 						      "LinePlotDrawLine");
9124 					g_free (legend);
9125 					g_free (x);
9126 					g_free (y);
9127 					return NULL;
9128 				}
9129 				if (a[i+1]->type == GEL_STRING_NODE)
9130 					astyleid = d_intern (a[i+1]->str.str);
9131 				else
9132 					astyleid = a[i+1]->id.id;
9133 
9134 				if (astyleid == originid) {
9135 					arrow_origin = TRUE;
9136 					arrow_end = FALSE;
9137 				} else if (astyleid == endid) {
9138 					arrow_origin = FALSE;
9139 					arrow_end = TRUE;
9140 				} else if (astyleid == bothid) {
9141 					arrow_origin = TRUE;
9142 					arrow_end = TRUE;
9143 				} else if (astyleid == noneid) {
9144 					arrow_origin = FALSE;
9145 					arrow_end = FALSE;
9146 				} else {
9147 					gel_errorout (_("%s: arrow style should be \"origin\", \"end\", \"both\", or \"none\""),
9148 						      "LinePlotDrawLine");
9149 					g_free (legend);
9150 					g_free (x);
9151 					g_free (y);
9152 					return NULL;
9153 				}
9154 				i++;
9155 			} else if (id == legendid) {
9156 				if G_UNLIKELY (a[i+1] == NULL)  {
9157 					gel_errorout (_("%s: No legend specified"),
9158 						      "LinePlotDrawLine");
9159 					g_free (legend);
9160 					g_free (x);
9161 					g_free (y);
9162 					return NULL;
9163 				}
9164 				if (a[i+1]->type == GEL_STRING_NODE) {
9165 					g_free (legend);
9166 					legend = g_strdup (a[i+1]->str.str);
9167 				} else if (a[i+1]->type == GEL_IDENTIFIER_NODE) {
9168 					g_free (legend);
9169 					legend = g_strdup (a[i+1]->id.id->token);
9170 				} else {
9171 					gel_errorout (_("%s: Legend must be a string"),
9172 						      "LinePlotDrawLine");
9173 					g_free (legend);
9174 					g_free (x);
9175 					g_free (y);
9176 					return NULL;
9177 				}
9178 				i++;
9179 			} else if (id == filledid) {
9180 				filled = TRUE;
9181 			} else {
9182 				gel_errorout (_("%s: Unknown style: %s"),
9183 					      "LinePlotDrawLine",
9184 					      id->token);
9185 				g_free (legend);
9186 				g_free (x);
9187 				g_free (y);
9188 				return NULL;
9189 			}
9190 		} else {
9191 			gel_errorout (_("%s: Bad parameter"), "LinePlotDrawLine");
9192 			g_free (legend);
9193 			g_free (x);
9194 			g_free (y);
9195 			return NULL;
9196 		}
9197 	}
9198 
9199 	if (plot_mode != MODE_LINEPLOT &&
9200 	    plot_mode != MODE_LINEPLOT_PARAMETRIC &&
9201 	    plot_mode != MODE_LINEPLOT_SLOPEFIELD &&
9202 	    plot_mode != MODE_LINEPLOT_VECTORFIELD) {
9203 		plot_mode = MODE_LINEPLOT;
9204 		clear_graph ();
9205 		update = FALSE;
9206 	}
9207 
9208 
9209 	if (line_plot == NULL) {
9210 		add_line_plot ();
9211 		plot_setup_axis ();
9212 		update = FALSE;
9213 	}
9214 
9215 	if (update) {
9216 		plot_axis ();
9217 	}
9218 
9219 	draw_line (x, y, len, thickness, &color, legend, filled);
9220 
9221 	if (arrow_end && len > 1)
9222 		draw_arrowhead (x[len-2], y[len-2],
9223 				x[len-1], y[len-1],
9224 				thickness, &color);
9225 	if (arrow_origin && len > 1)
9226 		draw_arrowhead (x[1], y[1],
9227 				x[0], y[0],
9228 				thickness, &color);
9229 
9230 	g_free (legend);
9231 
9232 	return gel_makenum_null ();
9233 }
9234 
9235 static GelETree *
LinePlotDrawPoints_op(GelCtx * ctx,GelETree ** a,int * exception)9236 LinePlotDrawPoints_op (GelCtx *ctx, GelETree * * a, int *exception)
9237 {
9238 	int len;
9239 	int nextarg;
9240 	double *x, *y;
9241 	double minx = 0, miny = 0, maxx = 0, maxy = 0;
9242 	GdkRGBA color;
9243 	int thickness;
9244 	int i;
9245 	gboolean update = FALSE;
9246 	char *legend = NULL;
9247 
9248 	if G_UNLIKELY (plot_in_progress != 0) {
9249 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
9250 			      "LinePlotDrawPoints", "LinePlotDrawPoints");
9251 		return NULL;
9252 	}
9253 
9254 	ensure_window (FALSE /* do_window_present */);
9255 
9256 	if (a[0]->type == GEL_NULL_NODE) {
9257 		return gel_makenum_null ();
9258 	} else if (a[0]->type == GEL_MATRIX_NODE) {
9259 		if G_UNLIKELY ( ! get_line_numbers (a[0], &x, &y, &len,
9260 						    &minx, &maxx, &miny, &maxy,
9261 						    "LinePlotDrawPoints",
9262 						    1))
9263 			return NULL;
9264 		nextarg = 1;
9265 	} else {
9266 		double x1, y1;
9267 		if G_UNLIKELY (gel_count_arguments (a) < 2) {
9268 			gel_errorout (_("%s: Wrong number of arguments"),
9269 				      "LinePlotDrawPoints");
9270 			return NULL;
9271 		}
9272 		GET_DOUBLE(x1, 0, "LinePlotDrawPoints");
9273 		GET_DOUBLE(y1, 1, "LinePlotDrawPoints");
9274 		len = 1;
9275 		x = g_new (double, 1);
9276 		x[0] = x1;
9277 		y = g_new (double, 1);
9278 		y[0] = y1;
9279 		nextarg = 2;
9280 
9281 		minx = x1;
9282 		maxx = x1;
9283 		miny = y1;
9284 		maxy = y1;
9285 	}
9286 
9287 	gdk_rgba_parse (&color, "black");
9288 	thickness = 2;
9289 
9290 	for (i = nextarg; a[i] != NULL; i++) {
9291 		if G_LIKELY (a[i]->type == GEL_STRING_NODE ||
9292 			     a[i]->type == GEL_IDENTIFIER_NODE) {
9293 			GelToken *id;
9294 			static GelToken *colorid = NULL;
9295 			static GelToken *thicknessid = NULL;
9296 			static GelToken *windowid = NULL;
9297 			static GelToken *fitid = NULL;
9298 			static GelToken *legendid = NULL;
9299 
9300 			if (colorid == NULL) {
9301 				colorid = d_intern ("color");
9302 				thicknessid = d_intern ("thickness");
9303 				windowid = d_intern ("window");
9304 				fitid = d_intern ("fit");
9305 				legendid = d_intern ("legend");
9306 			}
9307 
9308 			if (a[i]->type == GEL_STRING_NODE)
9309 				id = d_intern (a[i]->str.str);
9310 			else
9311 				id = a[i]->id.id;
9312 			if (id == colorid) {
9313 			        if G_UNLIKELY ( ! get_color (a[i+1], &color, "LinePlotDrawPoints")) {
9314 					g_free (legend);
9315 					g_free (x);
9316 					g_free (y);
9317 					return NULL;
9318 				}
9319 				i++;
9320 			} else if (id == thicknessid) {
9321 				if G_UNLIKELY (a[i+1] == NULL)  {
9322 					gel_errorout (_("%s: No thickness specified"),
9323 						      "LinePlotDrawPoints");
9324 					g_free (legend);
9325 					g_free (x);
9326 					g_free (y);
9327 					return NULL;
9328 				}
9329 				if G_UNLIKELY ( ! check_argument_positive_integer (a, i+1,
9330 										   "LinePlotDrawPoints")) {
9331 					g_free (legend);
9332 					g_free (x);
9333 					g_free (y);
9334 					return NULL;
9335 				}
9336 				thickness = gel_get_nonnegative_integer (a[i+1]->val.value,
9337 									 "LinePlotDrawPoints");
9338 				i++;
9339 			} else if (id == windowid) {
9340 				double x1, x2, y1, y2;
9341 				if G_UNLIKELY (a[i+1] == NULL ||
9342 					       (a[i+1]->type != GEL_STRING_NODE &&
9343 						a[i+1]->type != GEL_IDENTIFIER_NODE &&
9344 						a[i+1]->type != GEL_MATRIX_NODE)) {
9345 					gel_errorout (_("%s: No window specified"),
9346 						      "LinePlotDrawPoints");
9347 					g_free (legend);
9348 					g_free (x);
9349 					g_free (y);
9350 					return NULL;
9351 				}
9352 				if ((a[i+1]->type == GEL_STRING_NODE &&
9353 				     fitid == d_intern (a[i+1]->str.str)) ||
9354 				    (a[i+1]->type == GEL_IDENTIFIER_NODE &&
9355 				     fitid == a[i+1]->id.id)) {
9356 					x1 = minx;
9357 					x2 = maxx;
9358 					y1 = miny;
9359 					y2 = maxy;
9360 					if G_UNLIKELY (x1 == x2) {
9361 						x1 -= 0.1;
9362 						x2 += 0.1;
9363 					}
9364 
9365 					/* assume line is a graph so x fits tightly */
9366 
9367 					if G_UNLIKELY (y1 == y2) {
9368 						y1 -= 0.1;
9369 						y2 += 0.1;
9370 					} else {
9371 						/* Make window 5% larger on each vertical side */
9372 						double height = (y2-y1);
9373 						y1 -= height * 0.05;
9374 						y2 += height * 0.05;
9375 					}
9376 
9377 					update = update_lineplot_window (x1, x2, y1, y2);
9378 				} else if (get_limits_from_matrix (a[i+1], &x1, &x2, &y1, &y2)) {
9379 					update = update_lineplot_window (x1, x2, y1, y2);
9380 				} else {
9381 					g_free (legend);
9382 					g_free (x);
9383 					g_free (y);
9384 					return NULL;
9385 				}
9386 				i++;
9387 			} else if (id == legendid) {
9388 				if G_UNLIKELY (a[i+1] == NULL)  {
9389 					gel_errorout (_("%s: No legend specified"),
9390 						      "LinePlotDrawPoints");
9391 					g_free (legend);
9392 					g_free (x);
9393 					g_free (y);
9394 					return NULL;
9395 				}
9396 				if (a[i+1]->type == GEL_STRING_NODE) {
9397 					g_free (legend);
9398 					legend = g_strdup (a[i+1]->str.str);
9399 				} else if (a[i+1]->type == GEL_IDENTIFIER_NODE) {
9400 					g_free (legend);
9401 					legend = g_strdup (a[i+1]->id.id->token);
9402 				} else {
9403 					gel_errorout (_("%s: Legend must be a string"),
9404 						      "LinePlotDrawPoints");
9405 					g_free (legend);
9406 					g_free (x);
9407 					g_free (y);
9408 					return NULL;
9409 				}
9410 				i++;
9411 			} else {
9412 				gel_errorout (_("%s: Unknown style"), "LinePlotDrawPoints");
9413 				g_free (legend);
9414 				g_free (x);
9415 				g_free (y);
9416 				return NULL;
9417 			}
9418 		} else {
9419 			gel_errorout (_("%s: Bad parameter"), "LinePlotDrawPoints");
9420 			g_free (legend);
9421 			g_free (x);
9422 			g_free (y);
9423 			return NULL;
9424 		}
9425 	}
9426 
9427 	if (plot_mode != MODE_LINEPLOT &&
9428 	    plot_mode != MODE_LINEPLOT_PARAMETRIC &&
9429 	    plot_mode != MODE_LINEPLOT_SLOPEFIELD &&
9430 	    plot_mode != MODE_LINEPLOT_VECTORFIELD) {
9431 		plot_mode = MODE_LINEPLOT;
9432 		clear_graph ();
9433 		update = FALSE;
9434 	}
9435 
9436 
9437 	if (line_plot == NULL) {
9438 		add_line_plot ();
9439 		plot_setup_axis ();
9440 		update = FALSE;
9441 	}
9442 
9443 	if (update) {
9444 		plot_axis ();
9445 	}
9446 
9447 	draw_points (x, y, len, thickness, &color, legend);
9448 
9449 	g_free(legend);
9450 
9451 	return gel_makenum_null ();
9452 }
9453 
9454 static GelETree *
SurfacePlotDrawLine_op(GelCtx * ctx,GelETree ** a,int * exception)9455 SurfacePlotDrawLine_op (GelCtx *ctx, GelETree * * a, int *exception)
9456 {
9457 	int len;
9458 	int nextarg;
9459 	double *x, *y, *z;
9460 	double minx = 0, miny = 0, maxx = 0, maxy = 0, minz = 0, maxz = 0;
9461 	GdkRGBA color;
9462 	int thickness;
9463 	int i;
9464 	gboolean update = FALSE;
9465 	char *legend = NULL;
9466 
9467 	if G_UNLIKELY (plot_in_progress != 0) {
9468 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
9469 			      "SurfacePlotDrawLine", "SurfacePlotDrawLine");
9470 		return NULL;
9471 	}
9472 
9473 	ensure_window (FALSE /* do_window_present */);
9474 
9475 	if (a[0]->type == GEL_NULL_NODE) {
9476 		return gel_makenum_null ();
9477 	} else if (a[0]->type == GEL_MATRIX_NODE) {
9478 		if G_UNLIKELY ( ! get_surface_line_numbers (a[0], &x, &y, &z, &len,
9479 							    &minx, &maxx,
9480 							    &miny, &maxy,
9481 							    &minz, &maxz,
9482 							    "SurfacePlotDrawLine",
9483 							    2))
9484 			return NULL;
9485 		nextarg = 1;
9486 	} else {
9487 		double x1, y1, x2, y2, z1, z2;
9488 		if G_UNLIKELY (gel_count_arguments (a) < 6) {
9489 			gel_errorout (_("%s: Wrong number of arguments"),
9490 				      "SurfacePlotDrawLine");
9491 			return NULL;
9492 		}
9493 		GET_DOUBLE(x1, 0, "SurfacePlotDrawLine");
9494 		GET_DOUBLE(y1, 1, "SurfacePlotDrawLine");
9495 		GET_DOUBLE(z1, 2, "SurfacePlotDrawLine");
9496 		GET_DOUBLE(x2, 3, "SurfacePlotDrawLine");
9497 		GET_DOUBLE(y2, 4, "SurfacePlotDrawLine");
9498 		GET_DOUBLE(z2, 5, "SurfacePlotDrawLine");
9499 		len = 2;
9500 		x = g_new (double, 2);
9501 		x[0] = x1;
9502 		x[1] = x2;
9503 		y = g_new (double, 2);
9504 		y[0] = y1;
9505 		y[1] = y2;
9506 		z = g_new (double, 2);
9507 		z[0] = z1;
9508 		z[1] = z2;
9509 		nextarg = 6;
9510 
9511 		minx = MIN(x1,x2);
9512 		maxx = MAX(x1,x2);
9513 		miny = MIN(y1,y2);
9514 		maxy = MAX(y1,y2);
9515 		minz = MIN(z1,z2);
9516 		maxz = MAX(z1,z2);
9517 	}
9518 
9519 	gdk_rgba_parse (&color, "black");
9520 	thickness = 2;
9521 
9522 	for (i = nextarg; a[i] != NULL; i++) {
9523 		if G_LIKELY (a[i]->type == GEL_STRING_NODE ||
9524 			     a[i]->type == GEL_IDENTIFIER_NODE) {
9525 			GelToken *id;
9526 			static GelToken *colorid = NULL;
9527 			static GelToken *thicknessid = NULL;
9528 			static GelToken *windowid = NULL;
9529 			static GelToken *fitid = NULL;
9530 			static GelToken *legendid = NULL;
9531 
9532 			if (colorid == NULL) {
9533 				colorid = d_intern ("color");
9534 				thicknessid = d_intern ("thickness");
9535 				windowid = d_intern ("window");
9536 				fitid = d_intern ("fit");
9537 				legendid = d_intern ("legend");
9538 			}
9539 
9540 			if (a[i]->type == GEL_STRING_NODE)
9541 				id = d_intern (a[i]->str.str);
9542 			else
9543 				id = a[i]->id.id;
9544 			if (id == colorid) {
9545 			        if G_UNLIKELY ( ! get_color (a[i+1], &color, "SurfacePlotDrawLine")) {
9546 					g_free (legend);
9547 					g_free (x);
9548 					g_free (y);
9549 					g_free (z);
9550 					return NULL;
9551 				}
9552 				i++;
9553 			} else if (id == thicknessid) {
9554 				if G_UNLIKELY (a[i+1] == NULL)  {
9555 					gel_errorout (_("%s: No thickness specified"),
9556 						      "SurfacePlotDrawLine");
9557 					g_free (legend);
9558 					g_free (x);
9559 					g_free (y);
9560 					g_free (z);
9561 					return NULL;
9562 				}
9563 				if G_UNLIKELY ( ! check_argument_positive_integer (a, i+1,
9564 										   "SurfacePlotDrawLine")) {
9565 					g_free (legend);
9566 					g_free (x);
9567 					g_free (y);
9568 					g_free (z);
9569 					return NULL;
9570 				}
9571 				thickness = gel_get_nonnegative_integer (a[i+1]->val.value,
9572 									 "SurfacePlotDrawLine");
9573 				i++;
9574 			} else if (id == windowid) {
9575 				double x1, x2, y1, y2, z1, z2;
9576 				if G_UNLIKELY (a[i+1] == NULL ||
9577 					       (a[i+1]->type != GEL_STRING_NODE &&
9578 						a[i+1]->type != GEL_IDENTIFIER_NODE &&
9579 						a[i+1]->type != GEL_MATRIX_NODE)) {
9580 					gel_errorout (_("%s: No window specified"),
9581 						      "SurfacePlotDrawLine");
9582 					g_free (legend);
9583 					g_free (x);
9584 					g_free (y);
9585 					g_free (z);
9586 					return NULL;
9587 				}
9588 				if ((a[i+1]->type == GEL_STRING_NODE &&
9589 				     fitid == d_intern (a[i+1]->str.str)) ||
9590 				    (a[i+1]->type == GEL_IDENTIFIER_NODE &&
9591 				     fitid == a[i+1]->id.id)) {
9592 					x1 = minx;
9593 					x2 = maxx;
9594 					y1 = miny;
9595 					y2 = maxy;
9596 					z1 = minz;
9597 					z2 = maxz;
9598 					if G_UNLIKELY (x1 == x2) {
9599 						x1 -= 0.1;
9600 						x2 += 0.1;
9601 					}
9602 					if G_UNLIKELY (y1 == y2) {
9603 						y1 -= 0.1;
9604 						y2 += 0.1;
9605 					}
9606 
9607 					/* assume line is a graph so x fits tightly */
9608 
9609 					if G_UNLIKELY (z1 == z2) {
9610 						z1 -= 0.1;
9611 						z2 += 0.1;
9612 					} else {
9613 						/* Make window 5% larger on each vertical side */
9614 						double height = (z2-z1);
9615 						z1 -= height * 0.05;
9616 						z2 += height * 0.05;
9617 					}
9618 
9619 					update = update_surfaceplot_window (x1, x2, y1, y2, z1, z2);
9620 				} else if (get_limits_from_matrix_surf (a[i+1], &x1, &x2, &y1, &y2, &z1, &z2)) {
9621 					update = update_surfaceplot_window (x1, x2, y1, y2, z1, z2);
9622 				} else {
9623 					g_free (legend);
9624 					g_free (x);
9625 					g_free (y);
9626 					g_free (z);
9627 					return NULL;
9628 				}
9629 				i++;
9630 			} else if (id == legendid) {
9631 				if G_UNLIKELY (a[i+1] == NULL)  {
9632 					gel_errorout (_("%s: No legend specified"),
9633 						      "SurfacePlotDrawLine");
9634 					g_free (legend);
9635 					g_free (x);
9636 					g_free (y);
9637 					g_free (z);
9638 					return NULL;
9639 				}
9640 				if (a[i+1]->type == GEL_STRING_NODE) {
9641 					g_free (legend);
9642 					legend = g_strdup (a[i+1]->str.str);
9643 				} else if (a[i+1]->type == GEL_IDENTIFIER_NODE) {
9644 					g_free (legend);
9645 					legend = g_strdup (a[i+1]->id.id->token);
9646 				} else {
9647 					gel_errorout (_("%s: Legend must be a string"),
9648 						      "SurfacePlotDrawLine");
9649 					g_free (legend);
9650 					g_free (x);
9651 					g_free (y);
9652 					g_free (z);
9653 					return NULL;
9654 				}
9655 				i++;
9656 			} else {
9657 				gel_errorout (_("%s: Unknown style"), "SurfacePlotDrawLine");
9658 				g_free (legend);
9659 				g_free (x);
9660 				g_free (y);
9661 				g_free (z);
9662 				return NULL;
9663 			}
9664 		} else {
9665 			gel_errorout (_("%s: Bad parameter"), "SurfacePlotDrawLine");
9666 			g_free (legend);
9667 			g_free (x);
9668 			g_free (y);
9669 			g_free (z);
9670 			return NULL;
9671 		}
9672 	}
9673 
9674 	if (plot_mode != MODE_SURFACE ||
9675 	    surface_plot == NULL) {
9676 		plot_mode = MODE_SURFACE;
9677 		plot_surface_functions (TRUE /* do_window_present */,
9678 					surfaceplot_fit_dependent_axis_cb /*fit*/);
9679 		update = FALSE;
9680 
9681 	}
9682 
9683 	draw_surface_line (x, y, z, len, thickness, &color, legend);
9684 
9685 	if (update && surface_plot != NULL) {
9686 		plot_axis ();
9687 	}
9688 
9689 	g_free (legend);
9690 
9691 	return gel_makenum_null ();
9692 }
9693 
9694 static GelETree *
SurfacePlotDrawPoints_op(GelCtx * ctx,GelETree ** a,int * exception)9695 SurfacePlotDrawPoints_op (GelCtx *ctx, GelETree * * a, int *exception)
9696 {
9697 	int len;
9698 	int nextarg;
9699 	double *x, *y, *z;
9700 	double minx = 0, miny = 0, maxx = 0, maxy = 0, minz = 0, maxz = 0;
9701 	GdkRGBA color;
9702 	int thickness;
9703 	int i;
9704 	gboolean update = FALSE;
9705 	char *legend = NULL;
9706 
9707 	if G_UNLIKELY (plot_in_progress != 0) {
9708 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
9709 			      "SurfacePlotDrawPoints", "SurfacePlotDrawPoints");
9710 		return NULL;
9711 	}
9712 
9713 	ensure_window (FALSE /* do_window_present */);
9714 
9715 	if (a[0]->type == GEL_NULL_NODE) {
9716 		return gel_makenum_null ();
9717 	} else if (a[0]->type == GEL_MATRIX_NODE) {
9718 		if G_UNLIKELY ( ! get_surface_line_numbers (a[0], &x, &y, &z, &len,
9719 							    &minx, &maxx,
9720 							    &miny, &maxy,
9721 							    &minz, &maxz,
9722 							    "SurfacePlotDrawPoints",
9723 							    1))
9724 			return NULL;
9725 		nextarg = 1;
9726 	} else {
9727 		double x1, y1, z1;
9728 		if G_UNLIKELY (gel_count_arguments (a) < 3) {
9729 			gel_errorout (_("%s: Wrong number of arguments"),
9730 				      "SurfacePlotDrawSurface");
9731 			return NULL;
9732 		}
9733 		GET_DOUBLE(x1, 0, "SurfacePlotDrawPoints");
9734 		GET_DOUBLE(y1, 1, "SurfacePlotDrawPoints");
9735 		GET_DOUBLE(z1, 2, "SurfacePlotDrawPoints");
9736 		len = 1;
9737 		x = g_new (double, 1);
9738 		x[0] = x1;
9739 		y = g_new (double, 1);
9740 		y[0] = y1;
9741 		z = g_new (double, 1);
9742 		z[0] = z1;
9743 		nextarg = 3;
9744 
9745 		minx = x1;
9746 		maxx = x1;
9747 		miny = y1;
9748 		maxy = y1;
9749 		minz = z1;
9750 		maxz = z1;
9751 	}
9752 
9753 	gdk_rgba_parse (&color, "black");
9754 	thickness = 2;
9755 
9756 	for (i = nextarg; a[i] != NULL; i++) {
9757 		if G_LIKELY (a[i]->type == GEL_STRING_NODE ||
9758 			     a[i]->type == GEL_IDENTIFIER_NODE) {
9759 			GelToken *id;
9760 			static GelToken *colorid = NULL;
9761 			static GelToken *thicknessid = NULL;
9762 			static GelToken *windowid = NULL;
9763 			static GelToken *fitid = NULL;
9764 			static GelToken *legendid = NULL;
9765 
9766 			if (colorid == NULL) {
9767 				colorid = d_intern ("color");
9768 				thicknessid = d_intern ("thickness");
9769 				windowid = d_intern ("window");
9770 				fitid = d_intern ("fit");
9771 				legendid = d_intern ("legend");
9772 			}
9773 
9774 			if (a[i]->type == GEL_STRING_NODE)
9775 				id = d_intern (a[i]->str.str);
9776 			else
9777 				id = a[i]->id.id;
9778 			if (id == colorid) {
9779 			        if G_UNLIKELY ( ! get_color (a[i+1], &color, "SurfacePlotDrawPoints")) {
9780 					g_free (legend);
9781 					g_free (x);
9782 					g_free (y);
9783 					g_free (z);
9784 					return NULL;
9785 				}
9786 				i++;
9787 			} else if (id == thicknessid) {
9788 				if G_UNLIKELY (a[i+1] == NULL)  {
9789 					gel_errorout (_("%s: No thickness specified"),
9790 						      "SurfacePlotDrawPoints");
9791 					g_free (legend);
9792 					g_free (x);
9793 					g_free (y);
9794 					g_free (z);
9795 					return NULL;
9796 				}
9797 				if G_UNLIKELY ( ! check_argument_positive_integer (a, i+1,
9798 										   "SurfacePlotDrawPoints")) {
9799 					g_free (legend);
9800 					g_free (x);
9801 					g_free (y);
9802 					g_free (z);
9803 					return NULL;
9804 				}
9805 				thickness = gel_get_nonnegative_integer (a[i+1]->val.value,
9806 									 "SurfacePlotDrawPoints");
9807 				i++;
9808 			} else if (id == windowid) {
9809 				double x1, x2, y1, y2, z1, z2;
9810 				if G_UNLIKELY (a[i+1] == NULL ||
9811 					       (a[i+1]->type != GEL_STRING_NODE &&
9812 						a[i+1]->type != GEL_IDENTIFIER_NODE &&
9813 						a[i+1]->type != GEL_MATRIX_NODE)) {
9814 					gel_errorout (_("%s: No window specified"),
9815 						      "SurfacePlotDrawPoints");
9816 					g_free (legend);
9817 					g_free (x);
9818 					g_free (y);
9819 					g_free (z);
9820 					return NULL;
9821 				}
9822 				if ((a[i+1]->type == GEL_STRING_NODE &&
9823 				     fitid == d_intern (a[i+1]->str.str)) ||
9824 				    (a[i+1]->type == GEL_IDENTIFIER_NODE &&
9825 				     fitid == a[i+1]->id.id)) {
9826 					x1 = minx;
9827 					x2 = maxx;
9828 					y1 = miny;
9829 					y2 = maxy;
9830 					z1 = minz;
9831 					z2 = maxz;
9832 					if G_UNLIKELY (x1 == x2) {
9833 						x1 -= 0.1;
9834 						x2 += 0.1;
9835 					}
9836 					if G_UNLIKELY (y1 == y2) {
9837 						y1 -= 0.1;
9838 						y2 += 0.1;
9839 					}
9840 
9841 					/* assume line is a graph so x fits tightly */
9842 
9843 					if G_UNLIKELY (z1 == z2) {
9844 						z1 -= 0.1;
9845 						z2 += 0.1;
9846 					} else {
9847 						/* Make window 5% larger on each vertical side */
9848 						double height = (z2-z1);
9849 						z1 -= height * 0.05;
9850 						z2 += height * 0.05;
9851 					}
9852 
9853 					update = update_surfaceplot_window (x1, x2, y1, y2, z1, z2);
9854 				} else if (get_limits_from_matrix_surf (a[i+1], &x1, &x2, &y1, &y2, &z1, &z2)) {
9855 					update = update_surfaceplot_window (x1, x2, y1, y2, z1, z2);
9856 				} else {
9857 					g_free (legend);
9858 					g_free (x);
9859 					g_free (y);
9860 					g_free (z);
9861 					return NULL;
9862 				}
9863 				i++;
9864 			} else if (id == legendid) {
9865 				if G_UNLIKELY (a[i+1] == NULL)  {
9866 					gel_errorout (_("%s: No legend specified"),
9867 						      "SurfacePlotDrawPoints");
9868 					g_free (legend);
9869 					g_free (x);
9870 					g_free (y);
9871 					g_free (z);
9872 					return NULL;
9873 				}
9874 				if (a[i+1]->type == GEL_STRING_NODE) {
9875 					g_free (legend);
9876 					legend = g_strdup (a[i+1]->str.str);
9877 				} else if (a[i+1]->type == GEL_IDENTIFIER_NODE) {
9878 					g_free (legend);
9879 					legend = g_strdup (a[i+1]->id.id->token);
9880 				} else {
9881 					gel_errorout (_("%s: Legend must be a string"),
9882 						      "SurfacePlotDrawPoints");
9883 					g_free (legend);
9884 					g_free (x);
9885 					g_free (y);
9886 					g_free (z);
9887 					return NULL;
9888 				}
9889 				i++;
9890 			} else {
9891 				gel_errorout (_("%s: Unknown style"), "SurfacePlotDrawPoints");
9892 				g_free (legend);
9893 				g_free (x);
9894 				g_free (y);
9895 				g_free (z);
9896 				return NULL;
9897 			}
9898 		} else {
9899 			gel_errorout (_("%s: Bad parameter"), "SurfacePlotDrawPoints");
9900 			g_free (legend);
9901 			g_free (x);
9902 			g_free (y);
9903 			g_free (z);
9904 			return NULL;
9905 		}
9906 	}
9907 
9908 	if (plot_mode != MODE_SURFACE ||
9909 	    surface_plot == NULL) {
9910 		plot_mode = MODE_SURFACE;
9911 		plot_surface_functions (TRUE /* do_window_present */,
9912 					surfaceplot_fit_dependent_axis_cb /*fit*/);
9913 		update = FALSE;
9914 
9915 	}
9916 
9917 	draw_surface_points (x, y, z, len, thickness, &color, legend);
9918 
9919 	if (update && surface_plot != NULL) {
9920 		plot_axis ();
9921 	}
9922 
9923 	g_free (legend);
9924 
9925 	return gel_makenum_null ();
9926 }
9927 
9928 static gboolean
get_surface_data(GelETree * a,double ** x,double ** y,double ** z,int * len,double * minx,double * maxx,double * miny,double * maxy,double * minz,double * maxz)9929 get_surface_data (GelETree *a, double **x, double **y, double **z, int *len,
9930 		  double *minx, double *maxx,
9931 		  double *miny, double *maxy,
9932 		  double *minz, double *maxz)
9933 {
9934 	int i;
9935 	GelMatrixW *m;
9936 	gboolean nominmax = TRUE;
9937 #define UPDATE_MINMAX \
9938 	if (minx != NULL) { \
9939 		if (xx > *maxx || nominmax) *maxx = xx; \
9940 		if (xx < *minx || nominmax) *minx = xx; \
9941 		if (yy > *maxy || nominmax) *maxy = yy; \
9942 		if (yy < *miny || nominmax) *miny = yy; \
9943 		if (zz > *maxz || nominmax) *maxz = zz; \
9944 		if (zz < *minz || nominmax) *minz = zz; \
9945 		nominmax = FALSE; \
9946 	}
9947 
9948 	g_return_val_if_fail (a->type == GEL_MATRIX_NODE, FALSE);
9949 
9950 	m = a->mat.matrix;
9951 
9952 	if G_UNLIKELY ( ! gel_is_matrix_value_only_real (m)) {
9953 		gel_errorout (_("%s: Surface should be given as a real, n by 3 matrix "
9954 				"with columns for x, y, z, where n>=3"),
9955 			      "SurfacePlotData");
9956 		return FALSE;
9957 	}
9958 
9959 	if (gel_matrixw_width (m) == 3 &&
9960 	    gel_matrixw_height (m) >= 3) {
9961 		*len = gel_matrixw_height (m);
9962 
9963 		*x = g_new (double, *len);
9964 		*y = g_new (double, *len);
9965 		*z = g_new (double, *len);
9966 
9967 		for (i = 0; i < *len; i++) {
9968 			double xx, yy, zz;
9969 			GelETree *t = gel_matrixw_index (m, 0, i);
9970 			(*x)[i] = xx = mpw_get_double (t->val.value);
9971 			t = gel_matrixw_index (m, 1, i);
9972 			(*y)[i] = yy = mpw_get_double (t->val.value);
9973 			t = gel_matrixw_index (m, 2, i);
9974 			(*z)[i] = zz = mpw_get_double (t->val.value);
9975 			UPDATE_MINMAX
9976 		}
9977 	} else if (gel_matrixw_width (m) == 1 &&
9978 		   gel_matrixw_height (m) % 3 == 0 &&
9979 		   gel_matrixw_height (m) >= 9) {
9980 		*len = gel_matrixw_height (m) / 3;
9981 
9982 		*x = g_new (double, *len);
9983 		*y = g_new (double, *len);
9984 		*z = g_new (double, *len);
9985 
9986 		for (i = 0; i < *len; i++) {
9987 			double xx, yy, zz;
9988 			GelETree *t = gel_matrixw_index (m, 0, 3*i);
9989 			(*x)[i] = xx = mpw_get_double (t->val.value);
9990 			t = gel_matrixw_index (m, 0, (3*i) + 1);
9991 			(*y)[i] = yy = mpw_get_double (t->val.value);
9992 			t = gel_matrixw_index (m, 0, (3*i) + 2);
9993 			(*z)[i] = zz = mpw_get_double (t->val.value);
9994 			UPDATE_MINMAX
9995 		}
9996 	} else if (gel_matrixw_height (m) == 1 &&
9997 		   gel_matrixw_width (m) % 3 == 0 &&
9998 		   gel_matrixw_width (m) >= 9) {
9999 		*len = gel_matrixw_width (m) / 3;
10000 
10001 		*x = g_new (double, *len);
10002 		*y = g_new (double, *len);
10003 		*z = g_new (double, *len);
10004 
10005 		for (i = 0; i < *len; i++) {
10006 			double xx, yy, zz;
10007 			GelETree *t = gel_matrixw_index (m, 3*i, 0);
10008 			(*x)[i] = xx = mpw_get_double (t->val.value);
10009 			t = gel_matrixw_index (m, (3*i) + 1, 0);
10010 			(*y)[i] = yy = mpw_get_double (t->val.value);
10011 			t = gel_matrixw_index (m, (3*i) + 2, 0);
10012 			(*z)[i] = zz = mpw_get_double (t->val.value);
10013 			UPDATE_MINMAX
10014 		}
10015 	} else {
10016 		gel_errorout (_("%s: Surface should be given as a real, n by 3 matrix "
10017 				"with columns for x, y, z, where n>=3"),
10018 			      "SurfacePlotData");
10019 		return FALSE;
10020 	}
10021 
10022 	return TRUE;
10023 #undef UPDATE_MINMAX
10024 }
10025 
10026 static GelETree *
SurfacePlotData_op(GelCtx * ctx,GelETree ** a,int * exception)10027 SurfacePlotData_op (GelCtx *ctx, GelETree * * a, int *exception)
10028 {
10029 	double x1, x2, y1, y2, z1, z2;
10030 	double *x,*y,*z;
10031 	char *name = NULL;
10032 	int len;
10033 	int i;
10034 
10035 	if G_UNLIKELY (plot_in_progress != 0) {
10036 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10037 			      "SurfacePlotData", "SurfacePlotData");
10038 		return NULL;
10039 	}
10040 
10041 	i = 0;
10042 
10043 	if (a[i] != NULL && a[i]->type != GEL_MATRIX_NODE) {
10044 		gel_errorout (_("%s: argument not a matrix of data"), "SurfacePlotData");
10045 		return NULL;
10046 	}
10047 
10048 	if ( ! get_surface_data (a[i], &x, &y, &z, &len,
10049 				 &x1, &x2, &y1, &y2, &z1, &z2)) {
10050 		return NULL;
10051 	}
10052 
10053 	/* sanity checks */
10054 	if (x1 == x2) {
10055 		x1=x1-1;
10056 		x2=x2+1;
10057 	}
10058 	if (y1 == y2) {
10059 		y1=y1-1;
10060 		y2=y2+1;
10061 	}
10062 	if (z1 == z2) {
10063 		z1=z1-1;
10064 		z2=z2+1;
10065 	}
10066 
10067 	i++;
10068 
10069 	if (a[i] != NULL && a[i]->type == GEL_STRING_NODE) {
10070 		name = a[i]->str.str;
10071 		i++;
10072 	}
10073 
10074 	/* Defaults to min/max of the data */
10075 
10076 	if (a[i] != NULL) {
10077 		if (a[i]->type == GEL_MATRIX_NODE) {
10078 			if (gel_matrixw_elements (a[i]->mat.matrix) == 6) {
10079 				if ( ! get_limits_from_matrix_surf (a[i], &x1, &x2, &y1, &y2, &z1, &z2))
10080 					goto whack_copied_data;
10081 			} else {
10082 				if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
10083 					goto whack_copied_data;
10084 			}
10085 
10086 			i++;
10087 		} else {
10088 			GET_DOUBLE(x1, i, "SurfacePlotData");
10089 			i++;
10090 			if (a[i] != NULL) {
10091 				GET_DOUBLE(x2, i, "SurfacePlotData");
10092 				i++;
10093 				if (a[i] != NULL) {
10094 					GET_DOUBLE(y1, i, "SurfacePlotData");
10095 					i++;
10096 					if (a[i] != NULL) {
10097 						GET_DOUBLE(y2, i, "SurfacePlotData");
10098 						i++;
10099 						if (a[i] != NULL) {
10100 							GET_DOUBLE(z1, i, "SurfacePlotData");
10101 							i++;
10102 							if (a[i] != NULL) {
10103 								GET_DOUBLE(z2, i, "SurfacePlotData");
10104 								i++;
10105 							}
10106 						}
10107 					}
10108 				}
10109 			}
10110 			/* FIXME: what about errors */
10111 			if G_UNLIKELY (gel_error_num != 0) {
10112 				gel_error_num = 0;
10113 				goto whack_copied_data;
10114 			}
10115 		}
10116 	}
10117 
10118 	if (x1 > x2) {
10119 		double s = x1;
10120 		x1 = x2;
10121 		x2 = s;
10122 	}
10123 
10124 	if (y1 > y2) {
10125 		double s = y1;
10126 		y1 = y2;
10127 		y2 = s;
10128 	}
10129 
10130 	if (z1 > z2) {
10131 		double s = z1;
10132 		z1 = z2;
10133 		z2 = s;
10134 	}
10135 
10136 	if (x1 == x2) {
10137 		gel_errorout (_("%s: invalid X range"), "SurfacePlotData");
10138 		goto whack_copied_data;
10139 	}
10140 
10141 	if (y1 == y2) {
10142 		gel_errorout (_("%s: invalid Y range"), "SurfacePlotData");
10143 		goto whack_copied_data;
10144 	}
10145 
10146 	if (z1 == z2) {
10147 		gel_errorout (_("%s: invalid Z range"), "SurfacePlotData");
10148 		goto whack_copied_data;
10149 	}
10150 
10151 	/* name could also come after */
10152 	if (a[i] != NULL && a[i]->type == GEL_STRING_NODE) {
10153 		name = a[i]->str.str;
10154 		i++;
10155 	}
10156 
10157 	if (surface_func_name != NULL)
10158 		g_free (surface_func_name);
10159 	if (name != NULL)
10160 		surface_func_name = g_strdup (name);
10161 	else
10162 		surface_func_name = NULL;
10163 
10164 	surface_func = NULL;
10165 	surface_data_x = x;
10166 	x = NULL;
10167 	surface_data_y = y;
10168 	y = NULL;
10169 	surface_data_z = z;
10170 	z = NULL;
10171 	surface_data_len = len;
10172 
10173 	reset_surfacex1 = surfacex1 = x1;
10174 	reset_surfacex2 = surfacex2 = x2;
10175 	reset_surfacey1 = surfacey1 = y1;
10176 	reset_surfacey2 = surfacey2 = y2;
10177 	reset_surfacez1 = surfacez1 = z1;
10178 	reset_surfacez2 = surfacez2 = z2;
10179 
10180 	plot_minz = z1;
10181 	plot_maxz = z2;
10182 
10183 	plot_mode = MODE_SURFACE;
10184 	plot_surface_functions (FALSE /* do_window_present */,
10185 				FALSE /* fit */);
10186 
10187 	if (gel_interrupted)
10188 		return NULL;
10189 	else
10190 		return gel_makenum_null ();
10191 
10192 whack_copied_data:
10193 	if (x != NULL)
10194 		g_free (x);
10195 	if (y != NULL)
10196 		g_free (y);
10197 	if (z != NULL)
10198 		g_free (z);
10199 
10200 	return NULL;
10201 }
10202 
10203 static gboolean
get_surface_data_grid(GelETree * a,double ** x,double ** y,double ** z,int * len,double minx,double maxx,double miny,double maxy,gboolean setz,double * minz,double * maxz)10204 get_surface_data_grid (GelETree *a,
10205 		       double **x, double **y, double **z, int *len,
10206 		       double minx, double maxx,
10207 		       double miny, double maxy,
10208 		       gboolean setz,
10209 		       double *minz, double *maxz)
10210 {
10211 	int i, j, k;
10212 	GelMatrixW *m;
10213 	gboolean nominmax = TRUE;
10214 	int w, h;
10215 
10216 #define UPDATE_MINMAX \
10217 	if (setz) { \
10218 		if (zz > *maxz || nominmax) *maxz = zz; \
10219 		if (zz < *minz || nominmax) *minz = zz; \
10220 		nominmax = FALSE; \
10221 	}
10222 
10223 	g_return_val_if_fail (a->type == GEL_MATRIX_NODE, FALSE);
10224 
10225 	m = a->mat.matrix;
10226 
10227 	if G_UNLIKELY ( ! gel_is_matrix_value_only_real (m)) {
10228 		gel_errorout (_("%s: Surface grid data should be given as a real matrix "),
10229 			      "SurfacePlotDataGrid");
10230 		return FALSE;
10231 	}
10232 
10233 	w = gel_matrixw_width (m);
10234 	h = gel_matrixw_height (m);
10235 	*len = w * h;
10236 
10237 	*x = g_new (double, *len);
10238 	*y = g_new (double, *len);
10239 	*z = g_new (double, *len);
10240 
10241 	k = 0;
10242 	for (i = 0; i < w; i++) {
10243 		for (j = 0; j < h; j++) {
10244 			double zz;
10245 			GelETree *t = gel_matrixw_index (m, i, j);
10246 			(*z)[k] = zz = mpw_get_double (t->val.value);
10247 			(*x)[k] = minx+((double)j)*(maxx-minx)/((double)(h-1));
10248 			(*y)[k] = miny+((double)i)*(maxy-miny)/((double)(w-1));
10249 			k++;
10250 			UPDATE_MINMAX
10251 		}
10252 	}
10253 
10254 	return TRUE;
10255 #undef UPDATE_MINMAX
10256 }
10257 
10258 static GelETree *
SurfacePlotDataGrid_op(GelCtx * ctx,GelETree ** a,int * exception)10259 SurfacePlotDataGrid_op (GelCtx *ctx, GelETree * * a, int *exception)
10260 {
10261 	double x1, x2, y1, y2, z1, z2;
10262 	double *x,*y,*z;
10263 	char *name = NULL;
10264 	int len;
10265 	gboolean setz = FALSE;
10266 
10267 	if G_UNLIKELY (plot_in_progress != 0) {
10268 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10269 			      "SurfacePlotDataGrid", "SurfacePlotDataGrid");
10270 		return NULL;
10271 	}
10272 
10273 	if (a[1]->type != GEL_MATRIX_NODE) {
10274 		gel_errorout (_("%s: first argument not a matrix of data"), "SurfacePlotDataGrid");
10275 		return NULL;
10276 	}
10277 
10278 	if (a[1]->type != GEL_MATRIX_NODE &&
10279 	    (gel_matrixw_elements (a[1]->mat.matrix) != 6 &&
10280 	     gel_matrixw_elements (a[1]->mat.matrix) != 4)) {
10281 		gel_errorout (_("%s: second argument not a 4 or 6 element vector of limits"), "SurfacePlotDataGrid");
10282 		return NULL;
10283 	}
10284 
10285 	if (gel_matrixw_elements (a[1]->mat.matrix) == 6) {
10286 		if ( ! get_limits_from_matrix_surf (a[1], &x1, &x2, &y1, &y2, &z1, &z2))
10287 			return NULL;
10288 		setz = FALSE;
10289 	} else {
10290 		if ( ! get_limits_from_matrix (a[1], &x1, &x2, &y1, &y2))
10291 			return NULL;
10292 		setz = TRUE;
10293 	}
10294 
10295 	if (a[2] != NULL && a[2]->type == GEL_STRING_NODE) {
10296 		name = a[2]->str.str;
10297 	} else if (a[2] != NULL && a[3] != NULL) {
10298 		gel_errorout (_("%s: too many arguments or last argument not a string label"), "SurfacePlotDataGrid");
10299 		return NULL;
10300 	}
10301 
10302 	if ( ! get_surface_data_grid (a[0], &x, &y, &z, &len, x1, x2, y1, y2, setz, &z1, &z2)) {
10303 		return NULL;
10304 	}
10305 
10306 	/* sanity */
10307 	if (z1 == z2) {
10308 		z1=z1-1;
10309 		z2=z2+1;
10310 	}
10311 
10312 	if (surface_func_name != NULL)
10313 		g_free (surface_func_name);
10314 	if (name != NULL)
10315 		surface_func_name = g_strdup (name);
10316 	else
10317 		surface_func_name = NULL;
10318 
10319 	surface_func = NULL;
10320 	surface_data_x = x;
10321 	x = NULL;
10322 	surface_data_y = y;
10323 	y = NULL;
10324 	surface_data_z = z;
10325 	z = NULL;
10326 	surface_data_len = len;
10327 
10328 	reset_surfacex1 = surfacex1 = x1;
10329 	reset_surfacex2 = surfacex2 = x2;
10330 	reset_surfacey1 = surfacey1 = y1;
10331 	reset_surfacey2 = surfacey2 = y2;
10332 	reset_surfacez1 = surfacez1 = z1;
10333 	reset_surfacez2 = surfacez2 = z2;
10334 
10335 	plot_minz = z1;
10336 	plot_maxz = z2;
10337 
10338 	plot_mode = MODE_SURFACE;
10339 	plot_surface_functions (FALSE /* do_window_present */,
10340 				FALSE /* fit */);
10341 
10342 	if (gel_interrupted)
10343 		return NULL;
10344 	else
10345 		return gel_makenum_null ();
10346 
10347 	return NULL;
10348 }
10349 
10350 static GelETree *
ExportPlot_op(GelCtx * ctx,GelETree ** a,int * exception)10351 ExportPlot_op (GelCtx *ctx, GelETree * * a, int *exception)
10352 {
10353 	char *file;
10354 	char *type;
10355 
10356 	if G_UNLIKELY (plot_in_progress != 0) {
10357 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10358 			      "ExportPlot", "ExportPlot");
10359 		return NULL;
10360 	}
10361 
10362 	if (a[0]->type != GEL_STRING_NODE ||
10363 	    ve_string_empty(a[0]->str.str)) {
10364 		gel_errorout (_("%s: first argument not a nonempty string"), "ExportPlot");
10365 		return NULL;
10366 	}
10367 	file = a[0]->str.str;
10368 
10369 	if (a[1] == NULL) {
10370 		char *dot = strrchr (file, '.');
10371 		if (dot == NULL) {
10372 			gel_errorout (_("%s: type not specified and filename has no extension"), "ExportPlot");
10373 			return NULL;
10374 		}
10375 		type = dot+1;
10376 	}
10377 
10378 	if (a[1] != NULL) {
10379 		if (a[1]->type != GEL_STRING_NODE ||
10380 		    ve_string_empty(a[1]->str.str)) {
10381 			gel_errorout (_("%s: second argument not a nonempty string"), "ExportPlot");
10382 			return NULL;
10383 		}
10384 		type = a[1]->str.str;
10385 	}
10386 
10387 	if (a[1] != NULL && a[2] != NULL) {
10388 		gel_errorout (_("%s: too many arguments"), "ExportPlot");
10389 		return NULL;
10390 	}
10391 
10392 	if (plot_canvas == NULL) {
10393 		gel_errorout (_("%s: plot canvas not active, cannot export"), "ExportPlot");
10394 		return NULL;
10395 	}
10396 
10397 	if (strcasecmp (type, "png") == 0) {
10398 		GdkPixbuf *pix;
10399 
10400 		/* sanity */
10401 		if (GTK_PLOT_CANVAS (plot_canvas)->pixmap == NULL) {
10402 			gel_errorout (_("%s: export failed"), "ExportPlot");
10403 			return NULL;
10404 		}
10405 
10406 		pix = gdk_pixbuf_get_from_surface
10407 			(GTK_PLOT_CANVAS (plot_canvas)->pixmap,
10408 			 0 /* src x */, 0 /* src y */,
10409 			 GTK_PLOT_CANVAS (plot_canvas)->pixmap_width,
10410 			 GTK_PLOT_CANVAS (plot_canvas)->pixmap_height);
10411 
10412 		if (pix == NULL ||
10413 		    ! gdk_pixbuf_save (pix, file, "png", NULL /* error */, NULL)) {
10414 			if (pix != NULL)
10415 				g_object_unref (G_OBJECT (pix));
10416 			gel_errorout (_("%s: export failed"), "ExportPlot");
10417 			return NULL;
10418 		}
10419 
10420 		g_object_unref (G_OBJECT (pix));
10421 	} else if (strcasecmp (type, "eps") == 0 ||
10422 		   strcasecmp (type, "ps") == 0) {
10423 		gboolean eps = (strcasecmp (type, "eps") == 0);
10424 
10425 		plot_in_progress ++;
10426 		gel_calc_running ++;
10427 		plot_window_setup ();
10428 
10429 		if ( ! gtk_plot_canvas_export_ps_with_size
10430 			(GTK_PLOT_CANVAS (plot_canvas),
10431 			 file,
10432 			 GTK_PLOT_PORTRAIT,
10433 			 eps /* epsflag */,
10434 			 GTK_PLOT_PSPOINTS,
10435 			 400, ASPECT * 400)) {
10436 			plot_in_progress --;
10437 			gel_calc_running --;
10438 			plot_window_setup ();
10439 			gel_errorout (_("%s: export failed"), "ExportPlot");
10440 			return NULL;
10441 		}
10442 
10443 		/* need this for some reason */
10444 		if (plot_canvas != NULL) {
10445 			gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
10446 		}
10447 
10448 		plot_in_progress --;
10449 		gel_calc_running --;
10450 		plot_window_setup ();
10451 	} else {
10452 		gel_errorout (_("%s: unknown file type, can be \"png\", \"eps\", or \"ps\"."), "ExportPlot");
10453 		return NULL;
10454 	}
10455 
10456 	return gel_makenum_bool (TRUE);
10457 }
10458 
10459 
10460 
10461 static GelETree *
set_LinePlotWindow(GelETree * a)10462 set_LinePlotWindow (GelETree * a)
10463 {
10464 	double x1, x2, y1, y2;
10465 
10466 	if G_UNLIKELY (plot_in_progress != 0) {
10467 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10468 			      "set_LinePlotWindow", "set_LinePlotWindow");
10469 		return NULL;
10470 	}
10471 
10472 
10473 	if G_UNLIKELY ( ! get_limits_from_matrix (a, &x1, &x2, &y1, &y2))
10474 		return NULL;
10475 
10476 	if (update_lineplot_window (x1, x2, y1, y2)) {
10477 		if (line_plot != NULL)
10478 			plot_axis ();
10479 	}
10480 
10481 	return make_matrix_from_limits ();
10482 }
10483 
10484 
10485 
10486 static GelETree *
get_LinePlotWindow(void)10487 get_LinePlotWindow (void)
10488 {
10489 	return make_matrix_from_limits ();
10490 }
10491 
10492 static GelETree *
set_SurfacePlotWindow(GelETree * a)10493 set_SurfacePlotWindow (GelETree * a)
10494 {
10495 	double x1, x2, y1, y2, z1, z2;
10496 
10497 	if G_UNLIKELY (plot_in_progress != 0) {
10498 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10499 			      "set_SurfacePlotWindow", "set_SurfacePlotWindow");
10500 		return NULL;
10501 	}
10502 
10503 	if G_UNLIKELY ( ! get_limits_from_matrix_surf (a, &x1, &x2, &y1, &y2, &z1, &z2))
10504 		return NULL;
10505 
10506 	if (update_surfaceplot_window (x1, x2, y1, y2, z1, z2)) {
10507 		if (surface_plot != NULL) {
10508 			plot_axis ();
10509 		}
10510 	}
10511 
10512 	return make_matrix_from_limits_surf ();
10513 }
10514 
10515 static GelETree *
get_SurfacePlotWindow(void)10516 get_SurfacePlotWindow (void)
10517 {
10518 	return make_matrix_from_limits_surf ();
10519 }
10520 
10521 static GelETree *
set_SlopefieldTicks(GelETree * a)10522 set_SlopefieldTicks (GelETree * a)
10523 {
10524 	int v, h;
10525 
10526 	if G_UNLIKELY (plot_in_progress != 0) {
10527 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10528 			      "set_SlopefieldTicks", "set_SlopefieldTicks");
10529 		return NULL;
10530 	}
10531 
10532 	if G_UNLIKELY ( ! get_ticks_from_matrix (a, &v, &h))
10533 		return NULL;
10534 
10535 	plot_sf_Vtick = v;
10536 	plot_sf_Htick = h;
10537 
10538 	return make_matrix_from_ticks (plot_sf_Vtick, plot_sf_Htick);
10539 }
10540 
10541 static GelETree *
get_SlopefieldTicks(void)10542 get_SlopefieldTicks (void)
10543 {
10544 	return make_matrix_from_ticks (plot_sf_Vtick, plot_sf_Htick);
10545 }
10546 
10547 static GelETree *
set_VectorfieldTicks(GelETree * a)10548 set_VectorfieldTicks (GelETree * a)
10549 {
10550 	int v, h;
10551 
10552 	if G_UNLIKELY (plot_in_progress != 0) {
10553 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10554 			      "set_VectorfieldTicks", "set_VectorfieldTicks");
10555 		return NULL;
10556 	}
10557 
10558 	if G_UNLIKELY ( ! get_ticks_from_matrix (a, &v, &h))
10559 		return NULL;
10560 
10561 	plot_vf_Vtick = v;
10562 	plot_vf_Htick = h;
10563 
10564 	return make_matrix_from_ticks (plot_vf_Vtick, plot_vf_Htick);
10565 }
10566 
10567 static GelETree *
get_VectorfieldTicks(void)10568 get_VectorfieldTicks (void)
10569 {
10570 	return make_matrix_from_ticks (plot_vf_Vtick, plot_vf_Htick);
10571 }
10572 
10573 static GelETree *
set_LinePlotVariableNames(GelETree * a)10574 set_LinePlotVariableNames (GelETree * a)
10575 {
10576 	GelETree *t;
10577 	char *sx, *sy, *sz, *st;
10578 
10579 	init_var_names ();
10580 
10581 	if G_UNLIKELY (plot_in_progress != 0) {
10582 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10583 			      "set_LinePlotVariableNames", "set_LinePlotVariableNames");
10584 		return NULL;
10585 	}
10586 
10587 	if (a->type != GEL_MATRIX_NODE ||
10588 	    gel_matrixw_elements (a->mat.matrix) != 4) {
10589 		gel_errorout (_("Variable names not given in a 4-vector"));
10590 		return NULL;
10591 	}
10592 
10593 	t = gel_matrixw_vindex (a->mat.matrix, 0);
10594 	if (t->type == GEL_IDENTIFIER_NODE) {
10595 		sx = t->id.id->token;
10596 	} else if (t->type == GEL_STRING_NODE) {
10597 		sx = t->str.str;
10598 	} else {
10599 		gel_errorout (_("Variable names should be strings"));
10600 		return NULL;
10601 	}
10602 	t = gel_matrixw_vindex (a->mat.matrix, 1);
10603 	if (t->type == GEL_IDENTIFIER_NODE) {
10604 		sy = t->id.id->token;
10605 	} else if (t->type == GEL_STRING_NODE) {
10606 		sy = t->str.str;
10607 	} else {
10608 		gel_errorout (_("Variable names should be strings"));
10609 		return NULL;
10610 	}
10611 	t = gel_matrixw_vindex (a->mat.matrix, 2);
10612 	if (t->type == GEL_IDENTIFIER_NODE) {
10613 		sz = t->id.id->token;
10614 	} else if (t->type == GEL_STRING_NODE) {
10615 		sz = t->str.str;
10616 	} else {
10617 		gel_errorout (_("Variable names should be strings"));
10618 		return NULL;
10619 	}
10620 	t = gel_matrixw_vindex (a->mat.matrix, 3);
10621 	if (t->type == GEL_IDENTIFIER_NODE) {
10622 		st = t->id.id->token;
10623 	} else if (t->type == GEL_STRING_NODE) {
10624 		st = t->str.str;
10625 	} else {
10626 		gel_errorout (_("Variable names should be strings"));
10627 		return NULL;
10628 	}
10629 	if ( ! is_identifier (sx) ||
10630 	     ! is_identifier (sy) ||
10631 	     ! is_identifier (sz) ||
10632 	     ! is_identifier (st)) {
10633 		gel_errorout (_("Variable names must be valid identifiers"));
10634 		return NULL;
10635 	}
10636 	if (strcmp (sx, sy) == 0 ||
10637 	    strcmp (sx, sz) == 0 ||
10638 	    strcmp (sx, st) == 0 ||
10639 	    strcmp (sy, sz) == 0 ||
10640 	    strcmp (sy, st) == 0 ||
10641 	    strcmp (sz, st) == 0) {
10642 		gel_errorout (_("Variable names must be mutually distinct"));
10643 		return NULL;
10644 	}
10645 
10646 	g_free (lp_x_name);
10647 	g_free (lp_y_name);
10648 	g_free (lp_z_name);
10649 	g_free (lp_t_name);
10650 	lp_x_name = g_strdup (sx);
10651 	lp_y_name = g_strdup (sy);
10652 	lp_z_name = g_strdup (sz);
10653 	lp_t_name = g_strdup (st);
10654 
10655 	set_lineplot_labels ();
10656 	set_solver_labels ();
10657 
10658 	return make_matrix_from_lp_varnames ();
10659 }
10660 
10661 static GelETree *
get_LinePlotVariableNames(void)10662 get_LinePlotVariableNames (void)
10663 {
10664 	return make_matrix_from_lp_varnames ();
10665 }
10666 
10667 static GelETree *
set_SurfacePlotVariableNames(GelETree * a)10668 set_SurfacePlotVariableNames (GelETree * a)
10669 {
10670 	GelETree *t;
10671 	char *sx, *sy, *sz;
10672 
10673 	init_var_names ();
10674 
10675 	if G_UNLIKELY (plot_in_progress != 0) {
10676 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10677 			      "set_SurfacePlotVariableNames", "set_SurfacePlotVariableNames");
10678 		return NULL;
10679 	}
10680 
10681 	if (a->type != GEL_MATRIX_NODE ||
10682 	    gel_matrixw_elements (a->mat.matrix) != 3) {
10683 		gel_errorout (_("Variable names not given in a 3-vector"));
10684 		return NULL;
10685 	}
10686 
10687 	t = gel_matrixw_vindex (a->mat.matrix, 0);
10688 	if (t->type == GEL_IDENTIFIER_NODE) {
10689 		sx = t->id.id->token;
10690 	} else if (t->type == GEL_STRING_NODE) {
10691 		sx = t->str.str;
10692 	} else {
10693 		gel_errorout (_("Variable names should be strings"));
10694 		return NULL;
10695 	}
10696 	t = gel_matrixw_vindex (a->mat.matrix, 1);
10697 	if (t->type == GEL_IDENTIFIER_NODE) {
10698 		sy = t->id.id->token;
10699 	} else if (t->type == GEL_STRING_NODE) {
10700 		sy = t->str.str;
10701 	} else {
10702 		gel_errorout (_("Variable names should be strings"));
10703 		return NULL;
10704 	}
10705 	t = gel_matrixw_vindex (a->mat.matrix, 2);
10706 	if (t->type == GEL_IDENTIFIER_NODE) {
10707 		sz = t->id.id->token;
10708 	} else if (t->type == GEL_STRING_NODE) {
10709 		sz = t->str.str;
10710 	} else {
10711 		gel_errorout (_("Variable names should be strings"));
10712 		return NULL;
10713 	}
10714 	if ( ! is_identifier (sx) ||
10715 	     ! is_identifier (sy) ||
10716 	     ! is_identifier (sz)) {
10717 		gel_errorout (_("Variable names must be valid identifiers"));
10718 		return NULL;
10719 	}
10720 	if (strcmp (sx, sy) == 0 ||
10721 	    strcmp (sx, sz) == 0 ||
10722 	    strcmp (sy, sz) == 0) {
10723 		gel_errorout (_("Variable names must be mutually distinct"));
10724 		return NULL;
10725 	}
10726 
10727 	g_free (sp_x_name);
10728 	g_free (sp_y_name);
10729 	g_free (sp_z_name);
10730 	sp_x_name = g_strdup (sx);
10731 	sp_y_name = g_strdup (sy);
10732 	sp_z_name = g_strdup (sz);
10733 
10734 	set_surface_labels ();
10735 
10736 	if (surface_plot != NULL) {
10737 		GtkPlotAxis *axis;
10738 
10739 		axis = gtk_plot_get_axis (GTK_PLOT (surface_plot), GTK_PLOT_AXIS_BOTTOM);
10740 		gtk_plot_axis_set_title (axis, sp_x_name);
10741 		axis = gtk_plot_get_axis (GTK_PLOT (surface_plot), GTK_PLOT_AXIS_LEFT);
10742 		gtk_plot_axis_set_title (axis, sp_y_name);
10743 		axis = gtk_plot_get_axis (GTK_PLOT (surface_plot), GTK_PLOT_AXIS_TOP);
10744 		gtk_plot_axis_set_title (axis, "");
10745 
10746 		if (plot_canvas != NULL) {
10747 			gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
10748 			gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
10749 		}
10750 	}
10751 
10752 	return make_matrix_from_sp_varnames ();
10753 }
10754 
10755 static GelETree *
get_SurfacePlotVariableNames(void)10756 get_SurfacePlotVariableNames (void)
10757 {
10758 	return make_matrix_from_sp_varnames ();
10759 }
10760 
10761 static GelETree *
set_VectorfieldNormalized(GelETree * a)10762 set_VectorfieldNormalized (GelETree * a)
10763 {
10764 	if G_UNLIKELY (plot_in_progress != 0) {
10765 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10766 			      "set_VectorfieldNormalized", "set_VectorfieldNormalized");
10767 		return NULL;
10768 	}
10769 
10770 	if G_UNLIKELY ( ! check_argument_bool (&a, 0, "set_VectorfieldNormalized"))
10771 		return NULL;
10772 	if (a->type == GEL_VALUE_NODE)
10773 		vectorfield_normalize_arrow_length_parameter
10774 			= ! mpw_zero_p (a->val.value);
10775 	else /* a->type == GEL_BOOL_NODE */
10776 		vectorfield_normalize_arrow_length_parameter = a->bool_.bool_;
10777 
10778 	return gel_makenum_bool (vectorfield_normalize_arrow_length_parameter);
10779 }
10780 static GelETree *
get_VectorfieldNormalized(void)10781 get_VectorfieldNormalized (void)
10782 {
10783 	return gel_makenum_bool (vectorfield_normalize_arrow_length_parameter);
10784 }
10785 
10786 static GelETree *
set_LinePlotDrawLegends(GelETree * a)10787 set_LinePlotDrawLegends (GelETree * a)
10788 {
10789 	if G_UNLIKELY (plot_in_progress != 0) {
10790 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10791 			      "set_LinePlotDrawLegends", "set_LinePlotDrawLegends");
10792 		return NULL;
10793 	}
10794 	if G_UNLIKELY ( ! check_argument_bool (&a, 0, "set_LinePlotDrawLegends"))
10795 		return NULL;
10796 	if (a->type == GEL_VALUE_NODE)
10797 		lineplot_draw_legends
10798 			= ! mpw_zero_p (a->val.value);
10799 	else /* a->type == GEL_BOOL_NODE */
10800 		lineplot_draw_legends = a->bool_.bool_;
10801 
10802 	if (line_plot != NULL) {
10803 		line_plot_move_about ();
10804 
10805 		if (plot_canvas != NULL) {
10806 			gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
10807 			gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
10808 		}
10809 	}
10810 
10811 	return gel_makenum_bool (lineplot_draw_legends);
10812 }
10813 static GelETree *
get_LinePlotDrawLegends(void)10814 get_LinePlotDrawLegends (void)
10815 {
10816 	return gel_makenum_bool (lineplot_draw_legends);
10817 }
10818 
10819 static GelETree *
set_SurfacePlotDrawLegends(GelETree * a)10820 set_SurfacePlotDrawLegends (GelETree * a)
10821 {
10822 	if G_UNLIKELY (plot_in_progress != 0) {
10823 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10824 			      "set_SurfacePlotDrawLegends", "set_SurfacePlotDrawLegends");
10825 		return NULL;
10826 	}
10827 	if G_UNLIKELY ( ! check_argument_bool (&a, 0, "set_LinePlotDrawLegends"))
10828 		return NULL;
10829 	if (a->type == GEL_VALUE_NODE)
10830 		surfaceplot_draw_legends
10831 			= ! mpw_zero_p (a->val.value);
10832 	else /* a->type == GEL_BOOL_NODE */
10833 		surfaceplot_draw_legends = a->bool_.bool_;
10834 
10835 	if (surface_plot != NULL) {
10836 		if (surface_data != NULL) {
10837 			if (surfaceplot_draw_legends) {
10838 				gtk_plot_data_gradient_set_visible (GTK_PLOT_DATA (surface_data), TRUE);
10839 				gtk_plot_data_show_legend (GTK_PLOT_DATA (surface_data));
10840 			} else {
10841 				gtk_plot_data_gradient_set_visible (GTK_PLOT_DATA (surface_data), FALSE);
10842 				gtk_plot_data_hide_legend (GTK_PLOT_DATA (surface_data));
10843 			}
10844 		}
10845 
10846 		if (surfaceplot_draw_legends)
10847 			gtk_plot_show_legends (GTK_PLOT (surface_plot));
10848 		else
10849 			gtk_plot_hide_legends (GTK_PLOT (surface_plot));
10850 
10851 		surface_plot_move_about ();
10852 
10853 		if (plot_canvas != NULL) {
10854 			gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
10855 			gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
10856 		}
10857 	}
10858 
10859 	return gel_makenum_bool (surfaceplot_draw_legends);
10860 }
10861 static GelETree *
get_SurfacePlotDrawLegends(void)10862 get_SurfacePlotDrawLegends (void)
10863 {
10864 	return gel_makenum_bool (surfaceplot_draw_legends);
10865 }
10866 
10867 static GelETree *
set_LinePlotDrawAxisLabels(GelETree * a)10868 set_LinePlotDrawAxisLabels (GelETree * a)
10869 {
10870 	if G_UNLIKELY (plot_in_progress != 0) {
10871 		gel_errorout (_("%s: Plotting in progress, cannot call %s"),
10872 			      "set_LinePlotDrawAxisLabels", "set_LinePlotDrawAxisLabels");
10873 		return NULL;
10874 	}
10875 	if G_UNLIKELY ( ! check_argument_bool (&a, 0, "set_LinePlotDrawAxisLabels"))
10876 		return NULL;
10877 	if (a->type == GEL_VALUE_NODE)
10878 		lineplot_draw_labels
10879 			= ! mpw_zero_p (a->val.value);
10880 	else /* a->type == GEL_BOOL_NODE */
10881 		lineplot_draw_labels = a->bool_.bool_;
10882 
10883 	if (line_plot != NULL) {
10884 		plot_setup_axis ();
10885 
10886 		if (plot_canvas != NULL) {
10887 			gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
10888 			gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
10889 		}
10890 	}
10891 
10892 	return gel_makenum_bool (lineplot_draw_labels);
10893 }
10894 static GelETree *
get_LinePlotDrawAxisLabels(void)10895 get_LinePlotDrawAxisLabels (void)
10896 {
10897 	return gel_makenum_bool (lineplot_draw_labels);
10898 }
10899 
10900 void
gel_add_graph_functions(void)10901 gel_add_graph_functions (void)
10902 {
10903 	GelEFunc *f;
10904 	GelToken *id;
10905 
10906 	gel_new_category ("plotting", N_("Plotting"), TRUE /* internal */);
10907 
10908 	VFUNC (LinePlot, 2, "func,args", "plotting", N_("Plot a function with a line.  First come the functions (up to 10) then optionally limits as x1,x2,y1,y2"));
10909 	VFUNC (LinePlotParametric, 3, "xfunc,yfunc,args", "plotting", N_("Plot a parametric function with a line.  First come the functions for x and y then optionally the t limits as t1,t2,tinc, then optionally the limits as x1,x2,y1,y2"));
10910 	VFUNC (LinePlotCParametric, 2, "zfunc,args", "plotting", N_("Plot a parametric complex valued function with a line.  First comes the function that returns x+iy then optionally the t limits as t1,t2,tinc, then optionally the limits as x1,x2,y1,y2"));
10911 
10912 	VFUNC (SlopefieldPlot, 2, "func,args", "plotting", N_("Draw a slope field.  First comes the function dy/dx in terms of x and y (or a complex z) then optionally the limits as x1,x2,y1,y2"));
10913 	VFUNC (VectorfieldPlot, 3, "xfunc,yfunc,args", "plotting", N_("Draw a vector field.  First come the functions dx/dt and dy/dt in terms of x and y then optionally the limits as x1,x2,y1,y2"));
10914 
10915 	FUNC (SlopefieldDrawSolution, 3, "x,y,dx", "plotting", N_("Draw a solution for a slope field starting at x,y and using dx as increment"));
10916 	FUNC (SlopefieldClearSolutions, 0, "", "plotting", N_("Clear all the slopefield solutions"));
10917 
10918 	FUNC (VectorfieldDrawSolution, 4, "x,y,dt,tlen", "plotting", N_("Draw a solution for a vector field starting at x,y, using dt as increment for tlen units"));
10919 	FUNC (VectorfieldClearSolutions, 0, "", "plotting", N_("Clear all the vectorfield solutions"));
10920 
10921 
10922 	VFUNC (SurfacePlot, 2, "func,args", "plotting", N_("Plot a surface function which takes either two arguments or a complex number.  First comes the function then optionally limits as x1,x2,y1,y2,z1,z2"));
10923 
10924 	FUNC (SurfacePlotClear, 0, "", "plotting", N_("Show the surface (3d) plot window and clear out functions"));
10925 
10926 	VFUNC (SurfacePlotData, 2, "data,args", "plotting", N_("Plot surface data given as n by 3 matrix (n>=3) of data with each row being x,y,z.  Optionally can pass a label string and limits.  If no limits passed, limits computed from data."));
10927 	VFUNC (SurfacePlotDataGrid, 3, "data,limits,label", "plotting", N_("Plot surface data given as a matrix (where rows are the x coordinate and columns are the y coordinate), the limits are given as [x1,x2,y1,y2] or optionally [x1,x2,y1,y2,z1,z2], and optionally a string for the label."));
10928 	VFUNC (SurfacePlotDrawLine, 2, "x1,y1,z1,x2,y2,z2,args", "plotting", N_("Draw a line from x1,y1,z1 to x2,y2,z2 on the surface (3d) plot.  x1,y1,z1,x2,y2,z2 can be replaced by a n by 3 matrix for a longer line"));
10929 	VFUNC (SurfacePlotDrawPoints, 2, "x,y,z,args", "plotting", N_("Draw a point at x,y,z on the surface (3d) plot.  x,y,z can be replaced by a n by 3 matrix for more points."));
10930 
10931 	FUNC (LinePlotClear, 0, "", "plotting", N_("Show the line plot window and clear out functions"));
10932 	VFUNC (LinePlotDrawLine, 2, "x1,y1,x2,y2,args", "plotting", N_("Draw a line from x1,y1 to x2,y2.  x1,y1,x2,y2 can be replaced by a n by 2 matrix for a longer line."));
10933 	VFUNC (LinePlotDrawPoints, 2, "x,y,args", "plotting", N_("Draw a point at x,y.  x,y can be replaced by a n by 2 matrix for more points."));
10934 
10935 	FUNC (PlotCanvasFreeze, 0, "", "plotting", N_("Freeze the plot canvas, that is, inhibit drawing"));
10936 	FUNC (PlotCanvasThaw, 0, "", "plotting", N_("Thaw the plot canvas and redraw the plot immediately"));
10937 	FUNC (PlotWindowPresent, 0, "", "plotting", N_("Raise the plot window, and create the window if necessary"));
10938 
10939 	FUNC (LinePlotWaitForClick, 0, "", "plotting", N_("Wait for a click on the line plot window, return the location."));
10940 	FUNC (LinePlotMouseLocation, 0, "", "plotting", N_("Return current mouse location on the line plot window."));
10941 
10942 	VFUNC (ExportPlot, 2, "filename,type", "plotting", N_("Export the current contents of the plot canvas to a file.  The file type is given by the string type, which can be \"png\", \"eps\", or \"ps\"."));
10943 
10944 	PARAMETER (SlopefieldTicks, N_("Number of slopefield ticks as a vector [vertical,horizontal]."));
10945 	PARAMETER (VectorfieldTicks, N_("Number of vectorfield ticks as a vector [vertical,horizontal]."));
10946 	PARAMETER (LinePlotVariableNames, N_("Default names used by all 2D plot functions.  Should be a 4 vector of strings or identifiers [x,y,z,t]."));
10947 	PARAMETER (SurfacePlotVariableNames, N_("Default names used by surface plot functions.  Should be a 3 vector of strings or identifiers [x,y,z] (where z=x+iy and not the dependent axis)."));
10948 
10949 	PARAMETER (VectorfieldNormalized, N_("Normalize vectorfields if true.  That is, only show direction and not magnitude."));
10950 	PARAMETER (LinePlotDrawLegends, N_("If to draw legends or not on line plots."));
10951 	PARAMETER (LinePlotDrawAxisLabels, N_("If to draw axis labels on line plots."));
10952 
10953 	PARAMETER (SurfacePlotDrawLegends, N_("If to draw legends or not on surface plots."));
10954 
10955 	PARAMETER (LinePlotWindow, N_("Line plotting window (limits) as a 4-vector of the form [x1,x2,y1,y2]"));
10956 	PARAMETER (SurfacePlotWindow, N_("Surface plotting window (limits) as a 6-vector of the form [x1,x2,y1,y2,z1,z2]"));
10957 }
10958