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 ¶metric_info_label);
5974
5975 /* x */
5976 b = create_expression_box ("x=",
5977 ¶metric_x_label,
5978 ¶metric_entry_x,
5979 ¶metric_status_x);
5980 gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
5981
5982 /* y */
5983 b = create_expression_box ("y=",
5984 ¶metric_y_label,
5985 ¶metric_entry_y,
5986 ¶metric_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 ¶metric_z_label,
5996 ¶metric_entry_z,
5997 ¶metric_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:"), ¶metric_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