/* GENIUS Calculator
* Copyright (C) 2003-2007 Jiri (George) Lebl
*
* Author: Jiri (George) Lebl
*
* This file is part of Genius.
*
* Genius is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "config.h"
#include
#include
#include
#include
#include
#include "gtkextra.h"
#include "calc.h"
#include "eval.h"
#include "util.h"
#include "dict.h"
#include "funclib.h"
#include "matrixw.h"
#include "compil.h"
#include "plugin.h"
#include "geloutput.h"
#include "mpwrap.h"
#include "matop.h"
#include "gnome-genius.h"
#include "graphing.h"
#define MAXFUNC 10
static GtkWidget *graph_window = NULL;
static GtkWidget *plot_canvas = NULL;
static GtkWidget *plot_dialog = NULL;
static GtkWidget *plot_notebook = NULL;
static GtkWidget *function_notebook = NULL;
static GtkWidget *plot_zoomout_item = NULL;
static GtkWidget *plot_zoomin_item = NULL;
static GtkWidget *plot_zoomfit_item = NULL;
static GtkWidget *plot_print_item = NULL;
static GtkWidget *plot_exportps_item = NULL;
static GtkWidget *plot_exporteps_item = NULL;
static GtkWidget *plot_exportpng_item = NULL;
static GtkWidget *surface_menu_item = NULL;
enum {
MODE_LINEPLOT,
MODE_LINEPLOT_PARAMETRIC,
MODE_SURFACE
} plot_mode = MODE_LINEPLOT;
static GtkPlotCanvasChild *plot_child = NULL;
/* Smallest plot window */
#define MINPLOT (1e-10)
/*
plot (lineplot)
*/
static GtkWidget *line_plot = NULL;
static GtkPlotData *line_data[MAXFUNC] = { NULL };
static GtkPlotData *parametric_data = NULL;
static GtkWidget *plot_entries[MAXFUNC] = { NULL };
static GtkWidget *plot_entries_status[MAXFUNC] = { NULL };
static GtkWidget *parametric_entry_x = NULL;
static GtkWidget *parametric_entry_y = NULL;
static GtkWidget *parametric_entry_z = NULL;
static GtkWidget *parametric_status_x = NULL;
static GtkWidget *parametric_status_y = NULL;
static GtkWidget *parametric_status_z = NULL;
static double spinx1 = -10;
static double spinx2 = 10;
static double spiny1 = -10;
static double spiny2 = 10;
static double spint1 = 0.0;
static double spint2 = 1.0;
static double spintinc = 0.01;
static double defx1 = -10;
static double defx2 = 10;
static double defy1 = -10;
static double defy2 = 10;
static double deft1 = 0.0;
static double deft2 = 1.0;
static double deftinc = 0.01;
/* Replotting info */
static GelEFunc *plot_func[MAXFUNC] = { NULL };
static char *plot_func_name[MAXFUNC] = { NULL };
static GelEFunc *parametric_func_x = NULL;
static GelEFunc *parametric_func_y = NULL;
static GelEFunc *parametric_func_z = NULL;
static char *parametric_name = NULL;
static double plotx1 = -10;
static double plotx2 = 10;
static double ploty1 = -10;
static double ploty2 = 10;
static double plott1 = 0.0;
static double plott2 = 1.0;
static double plottinc = 0.01;
/*
Surface
*/
static GtkWidget *surface_plot = NULL;
static GtkPlotData *surface_data = NULL;
static GtkWidget *surface_entry = NULL;
static GtkWidget *surface_entry_status = NULL;
static double surf_spinx1 = -10;
static double surf_spinx2 = 10;
static double surf_spiny1 = -10;
static double surf_spiny2 = 10;
static double surf_spinz1 = -10;
static double surf_spinz2 = 10;
static double surf_defx1 = -10;
static double surf_defx2 = 10;
static double surf_defy1 = -10;
static double surf_defy2 = 10;
static double surf_defz1 = -10;
static double surf_defz2 = 10;
/* Replotting info */
static GelEFunc *surface_func = NULL;
static char *surface_func_name = NULL;
static double surfacex1 = -10;
static double surfacex2 = 10;
static double surfacey1 = -10;
static double surfacey2 = 10;
static double surfacez1 = -10;
static double surfacez2 = 10;
/* used for both */
static double plot_maxy = - G_MAXDOUBLE/2;
static double plot_miny = G_MAXDOUBLE/2;
static double plot_maxx = - G_MAXDOUBLE/2;
static double plot_minx = G_MAXDOUBLE/2;
static GelCtx *plot_ctx = NULL;
static GelETree *plot_arg = NULL;
static GelETree *plot_arg2 = NULL;
static GelETree *plot_arg3 = NULL;
static int plot_in_progress = 0;
static gboolean whack_window_after_plot = FALSE;
static void plot_axis (void);
/* lineplots */
static void plot_functions (void);
/* surfaces */
static void plot_surface_functions (void);
#define WIDTH 640
#define HEIGHT 480
#define ASPECT ((double)HEIGHT/(double)WIDTH)
#define PROPORTION 0.85
#define PROPORTION3D 0.80
#define PROPORTION_OFFSET 0.075
#define PROPORTION3D_OFFSET 0.1
#include "funclibhelper.cP"
enum {
RESPONSE_STOP = 1,
RESPONSE_PLOT
};
static void
plot_window_setup (void)
{
if (graph_window != NULL) {
if (plot_in_progress == 0 &&
whack_window_after_plot) {
gtk_widget_destroy (graph_window);
whack_window_after_plot = FALSE;
return;
}
if (plot_in_progress)
genius_setup_window_cursor (plot_canvas, GDK_WATCH);
else
genius_unsetup_window_cursor (plot_canvas);
gtk_dialog_set_response_sensitive (GTK_DIALOG (graph_window),
RESPONSE_STOP, plot_in_progress);
gtk_widget_set_sensitive (plot_zoomout_item, ! plot_in_progress);
gtk_widget_set_sensitive (plot_zoomin_item, ! plot_in_progress);
gtk_widget_set_sensitive (plot_zoomfit_item, ! plot_in_progress);
gtk_widget_set_sensitive (plot_print_item, ! plot_in_progress);
gtk_widget_set_sensitive (plot_exportps_item, ! plot_in_progress);
gtk_widget_set_sensitive (plot_exporteps_item, ! plot_in_progress);
gtk_widget_set_sensitive (plot_exportpng_item, ! plot_in_progress);
gtk_widget_set_sensitive (surface_menu_item, ! plot_in_progress);
}
}
static void
show_z_axis (gboolean do_show)
{
GtkPlotAxis *zx, *zy;
zx = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot),
GTK_PLOT_SIDE_ZX);
zy = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot),
GTK_PLOT_SIDE_ZY);
if (do_show) {
gtk_plot_axis_show_labels (zy,
GTK_PLOT_LABEL_OUT);
gtk_plot_axis_show_labels (zx,
GTK_PLOT_LABEL_OUT);
gtk_plot_axis_show_ticks (zy,
GTK_PLOT_TICKS_OUT,
GTK_PLOT_TICKS_OUT);
gtk_plot_axis_show_ticks (zx,
GTK_PLOT_TICKS_OUT,
GTK_PLOT_TICKS_OUT);
gtk_plot_axis_show_title (zy);
gtk_plot_axis_show_title (zx);
} else {
gtk_plot_axis_show_labels (zy,
GTK_PLOT_LABEL_NONE);
gtk_plot_axis_show_labels (zx,
GTK_PLOT_LABEL_NONE);
gtk_plot_axis_show_ticks (zy,
GTK_PLOT_TICKS_NONE,
GTK_PLOT_TICKS_NONE);
gtk_plot_axis_show_ticks (zx,
GTK_PLOT_TICKS_NONE,
GTK_PLOT_TICKS_NONE);
gtk_plot_axis_hide_title (zx);
gtk_plot_axis_hide_title (zy);
}
}
static void
rotate_x_cb (GtkWidget *button, gpointer data)
{
int rot = GPOINTER_TO_INT (data);
gtk_plot3d_rotate_x (GTK_PLOT3D (surface_plot), rot);
show_z_axis (TRUE);
gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
}
static void
rotate_y_cb (GtkWidget *button, gpointer data)
{
int rot = GPOINTER_TO_INT (data);
gtk_plot3d_rotate_y (GTK_PLOT3D (surface_plot), rot);
show_z_axis (TRUE);
gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
}
static void
rotate_z_cb (GtkWidget *button, gpointer data)
{
int rot = GPOINTER_TO_INT (data);
gtk_plot3d_rotate_z (GTK_PLOT3D (surface_plot), rot);
/* don't neccessarily show the z axis here, if we're in top
view this could be legitimate */
gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
}
static void
rotate_cb (GtkWidget *item, gpointer data)
{
GtkWidget *req = NULL;
GtkWidget *hbox, *w, *b;
GtkSizeGroup *sg;
if (surface_plot == NULL)
return;
req = gtk_dialog_new_with_buttons
(_("Rotate") /* title */,
GTK_WINDOW (graph_window) /* parent */,
GTK_DIALOG_MODAL /* flags */,
GTK_STOCK_CLOSE,
GTK_RESPONSE_CLOSE,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (req),
GTK_RESPONSE_CLOSE);
gtk_dialog_set_has_separator (GTK_DIALOG (req), FALSE);
sg = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
/* X dir */
hbox = gtk_hbox_new (FALSE, GNOME_PAD);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (req)->vbox),
hbox, TRUE, TRUE, 0);
w = gtk_label_new (_("Rotate X: "));
gtk_size_group_add_widget (sg, w);
gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
b = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (b),
gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE));
g_signal_connect (G_OBJECT (b), "clicked",
G_CALLBACK (rotate_x_cb),
GINT_TO_POINTER (360-10));
b = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (b),
gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE));
g_signal_connect (G_OBJECT (b), "clicked",
G_CALLBACK (rotate_x_cb),
GINT_TO_POINTER (10));
/* Y dir */
hbox = gtk_hbox_new (FALSE, GNOME_PAD);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (req)->vbox),
hbox, TRUE, TRUE, 0);
w = gtk_label_new (_("Rotate Y: "));
gtk_size_group_add_widget (sg, w);
gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
b = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (b),
gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE));
g_signal_connect (G_OBJECT (b), "clicked",
G_CALLBACK (rotate_y_cb),
GINT_TO_POINTER (360-10));
b = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (b),
gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE));
g_signal_connect (G_OBJECT (b), "clicked",
G_CALLBACK (rotate_y_cb),
GINT_TO_POINTER (10));
/* Z dir */
hbox = gtk_hbox_new (FALSE, GNOME_PAD);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (req)->vbox),
hbox, TRUE, TRUE, 0);
w = gtk_label_new (_("Rotate Z: "));
gtk_size_group_add_widget (sg, w);
gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
b = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (b),
gtk_arrow_new (GTK_ARROW_LEFT, GTK_SHADOW_NONE));
g_signal_connect (G_OBJECT (b), "clicked",
G_CALLBACK (rotate_z_cb),
GINT_TO_POINTER (360-10));
b = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (hbox), b, FALSE, FALSE, 0);
gtk_container_add (GTK_CONTAINER (b),
gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_NONE));
g_signal_connect (G_OBJECT (b), "clicked",
G_CALLBACK (rotate_z_cb),
GINT_TO_POINTER (10));
g_signal_connect (G_OBJECT (req), "destroy",
G_CALLBACK (gtk_widget_destroyed),
&req);
gtk_widget_show_all (req);
gtk_dialog_run (GTK_DIALOG (req));
gtk_widget_destroy (req);
}
static void
reset_angles_cb (GtkWidget *button, gpointer data)
{
if (surface_plot != NULL) {
gtk_plot3d_reset_angles (GTK_PLOT3D (surface_plot));
gtk_plot3d_rotate_x (GTK_PLOT3D (surface_plot), 60.0);
gtk_plot3d_rotate_z (GTK_PLOT3D (surface_plot), 30.0);
show_z_axis (TRUE);
gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
}
}
static void
top_view_cb (GtkWidget *button, gpointer data)
{
if (surface_plot != NULL) {
gtk_plot3d_reset_angles (GTK_PLOT3D (surface_plot));
/*gtk_plot3d_rotate_y (GTK_PLOT3D (surface_plot), 90.0);*/
show_z_axis (FALSE);
gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
}
}
static gboolean
dialog_delete_event (GtkWidget *w, gpointer data)
{
if (plot_in_progress > 0) {
interrupted = TRUE;
whack_window_after_plot = TRUE;
return TRUE;
} else {
return FALSE;
}
}
static void
dialog_response (GtkWidget *w, int response, gpointer data)
{
if (response == GTK_RESPONSE_CLOSE ||
response == GTK_RESPONSE_DELETE_EVENT) {
if (plot_in_progress > 0) {
interrupted = TRUE;
whack_window_after_plot = TRUE;
} else {
gtk_widget_destroy (graph_window);
}
} else if (response == RESPONSE_STOP && plot_in_progress > 0) {
interrupted = TRUE;
}
}
static void
print_entry_activate (GtkWidget *entry, gpointer data)
{
gtk_dialog_response (GTK_DIALOG (data), GTK_RESPONSE_OK);
}
static void
plot_print_cb (void)
{
gboolean ret;
GtkWidget *req = NULL;
GtkWidget *hbox, *w, *cmd;
int fd;
char tmpfile[] = "/tmp/genius-ps-XXXXXX";
static char *last_cmd = NULL;
if (last_cmd == NULL)
last_cmd = g_strdup ("lpr");
req = gtk_dialog_new_with_buttons
(_("Print") /* title */,
GTK_WINDOW (graph_window) /* parent */,
GTK_DIALOG_MODAL /* flags */,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_PRINT,
GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (req),
GTK_RESPONSE_OK);
gtk_dialog_set_has_separator (GTK_DIALOG (req), FALSE);
hbox = gtk_hbox_new (FALSE, GNOME_PAD);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (req)->vbox),
hbox, TRUE, TRUE, 0);
w = gtk_label_new (_("Print command: "));
gtk_box_pack_start (GTK_BOX (hbox), w, FALSE, FALSE, 0);
cmd = gtk_entry_new ();
g_signal_connect (G_OBJECT (cmd), "activate",
G_CALLBACK (print_entry_activate), req);
gtk_box_pack_start (GTK_BOX (hbox), cmd, TRUE, TRUE, 0);
gtk_entry_set_text (GTK_ENTRY (cmd), last_cmd);
gtk_widget_show_all (hbox);
g_signal_connect (G_OBJECT (req), "destroy",
G_CALLBACK (gtk_widget_destroyed),
&req);
if (gtk_dialog_run (GTK_DIALOG (req)) != GTK_RESPONSE_OK) {
gtk_widget_destroy (req);
return;
}
last_cmd = g_strdup (gtk_entry_get_text (GTK_ENTRY (cmd)));
gtk_widget_destroy (req);
fd = g_mkstemp (tmpfile);
if (fd < 0) {
genius_display_error (graph_window, _("Cannot open temporary file, cannot print."));
return;
}
plot_in_progress ++;
plot_window_setup ();
/* Letter will fit on A4, so just currently do that */
if (plot_canvas != NULL)
ret = gtk_plot_canvas_export_ps (GTK_PLOT_CANVAS (plot_canvas),
tmpfile,
GTK_PLOT_LANDSCAPE,
FALSE /* epsflag */,
GTK_PLOT_LETTER);
else
ret = FALSE;
if ( ! ret || interrupted) {
plot_in_progress --;
plot_window_setup ();
if ( ! interrupted)
genius_display_error (graph_window, _("Printing failed"));
interrupted = FALSE;
close (fd);
unlink (tmpfile);
return;
}
{
char *cmdstring = g_strdup_printf ("cat %s | %s", tmpfile, last_cmd);
system (cmdstring);
g_free (cmdstring);
}
plot_in_progress --;
plot_window_setup ();
close (fd);
unlink (tmpfile);
}
static char *last_export_dir = NULL;
static void
really_export_cb (GtkFileChooser *fs, int response, gpointer data)
{
char *s;
char *base;
gboolean ret;
gboolean eps;
char tmpfile[] = "/tmp/genius-ps-XXXXXX";
char *file_to_write = NULL;
int fd = -1;
eps = GPOINTER_TO_INT (data);
if (response != GTK_RESPONSE_OK) {
gtk_widget_destroy (GTK_WIDGET (fs));
/* FIXME: don't want to deal with modality issues right now */
gtk_widget_set_sensitive (graph_window, TRUE);
return;
}
s = g_strdup (gtk_file_chooser_get_filename (fs));
if (s == NULL)
return;
base = g_path_get_basename (s);
if (base != NULL && base[0] != '\0' &&
strchr (base, '.') == NULL) {
char *n;
if (eps)
n = g_strconcat (s, ".eps", NULL);
else
n = g_strconcat (s, ".ps", NULL);
g_free (s);
s = n;
}
g_free (base);
if (access (s, F_OK) == 0 &&
! genius_ask_question (GTK_WIDGET (fs),
_("File already exists. Overwrite it?"))) {
g_free (s);
return;
}
g_free (last_export_dir);
last_export_dir = gtk_file_chooser_get_current_folder (fs);
gtk_widget_destroy (GTK_WIDGET (fs));
/* FIXME: don't want to deal with modality issues right now */
gtk_widget_set_sensitive (graph_window, TRUE);
file_to_write = s;
if (eps && ve_is_prog_in_path ("ps2epsi",
g_getenv ("PATH"))) {
fd = g_mkstemp (tmpfile);
/* FIXME: tell about errors ?*/
if (fd >= 0) {
file_to_write = tmpfile;
}
}
plot_in_progress ++;
plot_window_setup ();
/* FIXME: There should be some options about size and stuff */
if (plot_canvas != NULL)
ret = gtk_plot_canvas_export_ps_with_size
(GTK_PLOT_CANVAS (plot_canvas),
file_to_write,
GTK_PLOT_PORTRAIT,
eps /* epsflag */,
GTK_PLOT_PSPOINTS,
400, ASPECT * 400);
else
ret = FALSE;
/* If we used a temporary file, now use ps2epsi */
if (fd >= 0) {
int status;
char *qs = g_shell_quote (s);
char *cmd = g_strdup_printf ("ps2epsi %s %s", tmpfile, qs);
if ( ! g_spawn_command_line_sync (cmd,
NULL /*stdout*/,
NULL /*stderr*/,
&status,
NULL /* error */)) {
status = -1;
}
close (fd);
if (status == 0) {
unlink (tmpfile);
} else {
/* EEK, couldn't run ps2epsi for some reason */
rename (tmpfile, s);
}
g_free (cmd);
g_free (qs);
}
plot_in_progress --;
plot_window_setup ();
if ( ! ret || interrupted) {
if ( ! interrupted)
genius_display_error (graph_window, _("Export failed"));
g_free (s);
interrupted = FALSE;
return;
}
g_free (s);
}
static void
really_export_png_cb (GtkFileChooser *fs, int response, gpointer data)
{
char *s;
char *base;
GdkPixbuf *pix;
if (response != GTK_RESPONSE_OK) {
gtk_widget_destroy (GTK_WIDGET (fs));
/* FIXME: don't want to deal with modality issues right now */
gtk_widget_set_sensitive (graph_window, TRUE);
return;
}
s = g_strdup (gtk_file_chooser_get_filename (fs));
if (s == NULL)
return;
base = g_path_get_basename (s);
if (base != NULL && base[0] != '\0' &&
strchr (base, '.') == NULL) {
char *n = g_strconcat (s, ".png", NULL);
g_free (s);
s = n;
}
g_free (base);
if (access (s, F_OK) == 0 &&
! genius_ask_question (GTK_WIDGET (fs),
_("File already exists. Overwrite it?"))) {
g_free (s);
return;
}
g_free (last_export_dir);
last_export_dir = gtk_file_chooser_get_current_folder (fs);
gtk_widget_destroy (GTK_WIDGET (fs));
/* FIXME: don't want to deal with modality issues right now */
gtk_widget_set_sensitive (graph_window, TRUE);
/* sanity */
if (GTK_PLOT_CANVAS (plot_canvas)->pixmap == NULL) {
genius_display_error (graph_window, _("Export failed"));
return;
}
pix = gdk_pixbuf_get_from_drawable
(NULL /* dest */,
GTK_PLOT_CANVAS (plot_canvas)->pixmap,
NULL /* cmap */,
0 /* src x */, 0 /* src y */,
0 /* dest x */, 0 /* dest y */,
GTK_PLOT_CANVAS (plot_canvas)->pixmap_width,
GTK_PLOT_CANVAS (plot_canvas)->pixmap_height);
if (pix == NULL ||
! gdk_pixbuf_save (pix, s, "png", NULL /* error */, NULL)) {
if (pix != NULL)
g_object_unref (G_OBJECT (pix));
g_free (s);
genius_display_error (graph_window, _("Export failed"));
return;
}
g_object_unref (G_OBJECT (pix));
g_free (s);
}
enum {
EXPORT_PS,
EXPORT_EPS,
EXPORT_PNG
};
static void
do_export_cb (int export_type)
{
static GtkWidget *fs = NULL;
GtkFileFilter *filter_ps;
GtkFileFilter *filter_all;
const char *title;
if (fs != NULL) {
gtk_window_present (GTK_WINDOW (fs));
return;
}
/* FIXME: don't want to deal with modality issues right now */
gtk_widget_set_sensitive (graph_window, FALSE);
if (export_type == EXPORT_EPS)
title = _("Export encapsulated postscript");
else if (export_type == EXPORT_PS)
title = _("Export postscript");
else if (export_type == EXPORT_PNG)
title = _("Export PNG");
else
/* should never happen */
title = "Export ???";
fs = gtk_file_chooser_dialog_new (title,
GTK_WINDOW (graph_window),
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_OK,
NULL);
gtk_file_chooser_set_local_only (GTK_FILE_CHOOSER (fs), TRUE);
filter_ps = gtk_file_filter_new ();
if (export_type == EXPORT_EPS) {
gtk_file_filter_set_name (filter_ps, _("EPS files"));
gtk_file_filter_add_pattern (filter_ps, "*.eps");
gtk_file_filter_add_pattern (filter_ps, "*.EPS");
} else if (export_type == EXPORT_PS) {
gtk_file_filter_set_name (filter_ps, _("PS files"));
gtk_file_filter_add_pattern (filter_ps, "*.ps");
gtk_file_filter_add_pattern (filter_ps, "*.PS");
} else if (export_type == EXPORT_PNG) {
gtk_file_filter_set_name (filter_ps, _("PNG files"));
gtk_file_filter_add_pattern (filter_ps, "*.png");
gtk_file_filter_add_pattern (filter_ps, "*.PNG");
}
filter_all = gtk_file_filter_new ();
gtk_file_filter_set_name (filter_all, _("All files"));
gtk_file_filter_add_pattern (filter_all, "*");
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fs), filter_ps);
gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fs), filter_all);
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (fs), filter_ps);
g_signal_connect (G_OBJECT (fs), "destroy",
G_CALLBACK (gtk_widget_destroyed), &fs);
if (export_type == EXPORT_EPS) {
g_signal_connect (G_OBJECT (fs), "response",
G_CALLBACK (really_export_cb),
GINT_TO_POINTER (TRUE /*eps*/));
} else if (export_type == EXPORT_PS) {
g_signal_connect (G_OBJECT (fs), "response",
G_CALLBACK (really_export_cb),
GINT_TO_POINTER (FALSE /*eps*/));
} else if (export_type == EXPORT_PNG) {
g_signal_connect (G_OBJECT (fs), "response",
G_CALLBACK (really_export_png_cb),
NULL);
}
if (last_export_dir != NULL) {
gtk_file_chooser_set_current_folder
(GTK_FILE_CHOOSER (fs), last_export_dir);
}
gtk_widget_show (fs);
}
static void
plot_exportps_cb (void)
{
do_export_cb (EXPORT_PS);
}
static void
plot_exporteps_cb (void)
{
do_export_cb (EXPORT_EPS);
}
static void
plot_exportpng_cb (void)
{
do_export_cb (EXPORT_PNG);
}
static void
plot_zoomin_cb (void)
{
if (plot_in_progress == 0) {
double len;
gboolean last_info = genius_setup.info_box;
gboolean last_error = genius_setup.error_box;
genius_setup.info_box = TRUE;
genius_setup.error_box = TRUE;
if (plot_mode == MODE_LINEPLOT ||
plot_mode == MODE_LINEPLOT_PARAMETRIC) {
len = plotx2 - plotx1;
plotx2 -= len/4.0;
plotx1 += len/4.0;
len = ploty2 - ploty1;
ploty2 -= len/4.0;
ploty1 += len/4.0;
} else if (plot_mode == MODE_SURFACE) {
len = surfacex2 - surfacex1;
surfacex2 -= len/4.0;
surfacex1 += len/4.0;
len = surfacey2 - surfacey1;
surfacey2 -= len/4.0;
surfacey1 += len/4.0;
len = surfacez2 - surfacez1;
surfacez2 -= len/4.0;
surfacez1 += len/4.0;
}
plot_axis ();
if (interrupted)
interrupted = FALSE;
gel_printout_infos ();
genius_setup.info_box = last_info;
genius_setup.error_box = last_error;
}
}
static void
plot_zoomout_cb (void)
{
if (plot_in_progress == 0) {
double len;
gboolean last_info = genius_setup.info_box;
gboolean last_error = genius_setup.error_box;
genius_setup.info_box = TRUE;
genius_setup.error_box = TRUE;
if (plot_mode == MODE_LINEPLOT ||
plot_mode == MODE_LINEPLOT_PARAMETRIC) {
len = plotx2 - plotx1;
plotx2 += len/2.0;
plotx1 -= len/2.0;
len = ploty2 - ploty1;
ploty2 += len/2.0;
ploty1 -= len/2.0;
} else if (plot_mode == MODE_SURFACE) {
len = surfacex2 - surfacex1;
surfacex2 += len/2.0;
surfacex1 -= len/2.0;
len = surfacey2 - surfacey1;
surfacey2 += len/2.0;
surfacey1 -= len/2.0;
len = surfacez2 - surfacez1;
surfacez2 += len/2.0;
surfacez1 -= len/2.0;
}
plot_axis ();
if (interrupted)
interrupted = FALSE;
gel_printout_infos ();
genius_setup.info_box = last_info;
genius_setup.error_box = last_error;
}
}
static void
plot_zoomfit_cb (void)
{
if (plot_in_progress == 0) {
double size;
gboolean last_info = genius_setup.info_box;
gboolean last_error = genius_setup.error_box;
genius_setup.info_box = TRUE;
genius_setup.error_box = TRUE;
size = plot_maxy - plot_miny;
if (size <= 0)
size = 1.0;
if (plot_mode == MODE_LINEPLOT) {
ploty1 = plot_miny - size * 0.05;
ploty2 = plot_maxy + size * 0.05;
/* sanity */
if (ploty2 < ploty1)
ploty2 = ploty1 + 0.1;
/* sanity */
if (ploty1 < -(G_MAXDOUBLE/2))
ploty1 = -(G_MAXDOUBLE/2);
if (ploty2 > (G_MAXDOUBLE/2))
ploty2 = (G_MAXDOUBLE/2);
} else if (plot_mode == MODE_LINEPLOT_PARAMETRIC) {
double sizex;
sizex = plot_maxx - plot_minx;
if (sizex <= 0)
sizex = 1.0;
plotx1 = plot_minx - sizex * 0.05;
plotx2 = plot_maxx + sizex * 0.05;
/* sanity */
if (plotx2 < plotx1)
plotx2 = plotx1 + 0.1;
/* sanity */
if (plotx1 < -(G_MAXDOUBLE/2))
plotx1 = -(G_MAXDOUBLE/2);
if (plotx2 > (G_MAXDOUBLE/2))
plotx2 = (G_MAXDOUBLE/2);
ploty1 = plot_miny - size * 0.05;
ploty2 = plot_maxy + size * 0.05;
/* sanity */
if (ploty2 < ploty1)
ploty2 = ploty1 + 0.1;
/* sanity */
if (ploty1 < -(G_MAXDOUBLE/2))
ploty1 = -(G_MAXDOUBLE/2);
if (ploty2 > (G_MAXDOUBLE/2))
ploty2 = (G_MAXDOUBLE/2);
} else if (plot_mode == MODE_SURFACE) {
surfacez1 = plot_miny - size * 0.05;
surfacez2 = plot_maxy + size * 0.05;
/* sanity */
if (surfacez2 < surfacez1)
surfacez2 = surfacez1 + 0.1;
/* sanity */
if (surfacez1 < -(G_MAXDOUBLE/2))
surfacez1 = -(G_MAXDOUBLE/2);
if (surfacez2 > (G_MAXDOUBLE/2))
surfacez2 = (G_MAXDOUBLE/2);
}
plot_axis ();
if (interrupted)
interrupted = FALSE;
gel_printout_infos ();
genius_setup.info_box = last_info;
genius_setup.error_box = last_error;
}
}
static void
plot_select_region (GtkPlotCanvas *canvas,
gdouble xmin,
gdouble ymin,
gdouble xmax,
gdouble ymax)
{
/* only for line plots! */
if (plot_in_progress == 0 && line_plot != NULL) {
double len;
double px, py, pw, ph;
gboolean last_info = genius_setup.info_box;
gboolean last_error = genius_setup.error_box;
genius_setup.info_box = TRUE;
genius_setup.error_box = TRUE;
/* FIXME: evil because this is the selection thingie,
hmmm, I dunno another way to do this though */
gtk_plot_get_position (GTK_PLOT (line_plot), &px, &py);
gtk_plot_get_size (GTK_PLOT (line_plot), &pw, &ph);
xmin -= px;
ymin -= py;
xmax -= px;
ymax -= py;
xmin /= pw;
ymin /= ph;
xmax /= pw;
ymax /= ph;
{ /* flip the y coordinate */
double oldymin = ymin;
ymin = 1.0 - ymax;
ymax = 1.0 - oldymin;
}
len = plotx2 - plotx1;
plotx1 += len * xmin;
plotx2 = plotx1 + (len * (xmax-xmin));
len = ploty2 - ploty1;
ploty1 += len * ymin;
ploty2 = ploty1 + (len * (ymax-ymin));
/* sanity */
if (plotx2 - plotx1 < MINPLOT)
plotx2 = plotx1 + MINPLOT;
/* sanity */
if (ploty2 - ploty1 < MINPLOT)
ploty2 = ploty1 + MINPLOT;
plot_axis ();
if (interrupted)
interrupted = FALSE;
gel_printout_infos ();
genius_setup.info_box = last_info;
genius_setup.error_box = last_error;
}
}
static void
line_plot_move_about (void)
{
if (line_plot == NULL)
return;
if (plot_mode == MODE_LINEPLOT) {
gtk_plot_move (GTK_PLOT (line_plot),
PROPORTION_OFFSET,
PROPORTION_OFFSET);
gtk_plot_resize (GTK_PLOT (line_plot),
1.0-2*PROPORTION_OFFSET,
1.0-2*PROPORTION_OFFSET);
gtk_plot_legends_move (GTK_PLOT (line_plot), 0.80, 0.05);
} else if (plot_mode == MODE_LINEPLOT_PARAMETRIC) {
gtk_plot_move (GTK_PLOT (line_plot),
PROPORTION_OFFSET,
PROPORTION_OFFSET);
gtk_plot_resize (GTK_PLOT (line_plot),
1.0-2*PROPORTION_OFFSET,
1.0-2*PROPORTION_OFFSET-0.05);
gtk_plot_legends_move (GTK_PLOT (line_plot),
0.0,
1.07);
}
}
static void
add_line_plot (void)
{
GtkPlotAxis *top, *right, *bottom, *left;
line_plot = gtk_plot_new_with_size (NULL, PROPORTION, PROPORTION);
gtk_widget_show (line_plot);
g_signal_connect (G_OBJECT (line_plot),
"destroy",
G_CALLBACK (gtk_widget_destroyed),
&line_plot);
plot_child = gtk_plot_canvas_plot_new (GTK_PLOT (line_plot));
gtk_plot_canvas_put_child (GTK_PLOT_CANVAS (plot_canvas),
plot_child,
PROPORTION_OFFSET,
PROPORTION_OFFSET,
1.0-PROPORTION_OFFSET,
1.0-PROPORTION_OFFSET);
top = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_TOP);
right = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_RIGHT);
bottom = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_BOTTOM);
left = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_LEFT);
gtk_plot_axis_set_visible (top, TRUE);
gtk_plot_axis_set_visible (right, TRUE);
gtk_plot_grids_set_visible (GTK_PLOT (line_plot),
FALSE, FALSE, FALSE, FALSE);
gtk_plot_axis_hide_title (top);
gtk_plot_axis_hide_title (right);
gtk_plot_axis_hide_title (left);
gtk_plot_axis_hide_title (bottom);
/*gtk_plot_axis_set_title (left, "Y");
gtk_plot_axis_set_title (bottom, "X");*/
gtk_plot_set_legends_border (GTK_PLOT (line_plot),
GTK_PLOT_BORDER_LINE, 3);
line_plot_move_about ();
}
static void
add_surface_plot (void)
{
GtkPlotAxis *xy, *xz, *yx, *yz, *zx, *zy;
GtkPlotAxis *top, *left, *bottom;
surface_plot = gtk_plot3d_new_with_size (NULL, PROPORTION3D, PROPORTION3D);
gtk_widget_show (surface_plot);
g_signal_connect (G_OBJECT (surface_plot),
"destroy",
G_CALLBACK (gtk_widget_destroyed),
&surface_plot);
plot_child = gtk_plot_canvas_plot_new (GTK_PLOT (surface_plot));
gtk_plot_canvas_put_child (GTK_PLOT_CANVAS (plot_canvas),
plot_child,
0.0,
PROPORTION3D_OFFSET,
0.8,
1.0-PROPORTION3D_OFFSET);
xy = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_XY);
xz = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_XZ);
yx = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_YX);
yz = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_YZ);
zx = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_ZX);
zy = gtk_plot3d_get_side (GTK_PLOT3D (surface_plot), GTK_PLOT_SIDE_ZY);
gtk_plot_axis_show_title (xy);
gtk_plot_axis_show_title (xz);
gtk_plot_axis_show_title (yx);
gtk_plot_axis_show_title (yz);
gtk_plot_axis_show_title (zx);
gtk_plot_axis_show_title (zy);
top = gtk_plot_get_axis (GTK_PLOT (surface_plot), GTK_PLOT_AXIS_TOP);
bottom = gtk_plot_get_axis (GTK_PLOT (surface_plot), GTK_PLOT_AXIS_BOTTOM);
left = gtk_plot_get_axis (GTK_PLOT (surface_plot), GTK_PLOT_AXIS_LEFT);
gtk_plot_axis_set_title (bottom, "X");
gtk_plot_axis_set_title (left, "Y");
gtk_plot_axis_set_title (top, "Z");
gtk_plot_set_legends_border (GTK_PLOT (surface_plot),
GTK_PLOT_BORDER_LINE, 3);
gtk_plot_legends_move (GTK_PLOT (surface_plot), 0.93, 0.05);
}
static void
ensure_window (void)
{
GtkWidget *menu, *menubar, *item;
/* ensure we don't whack things, just paranoia */
whack_window_after_plot = FALSE;
if (graph_window != NULL) {
/* FIXME: present is evil in that it takes focus away */
gtk_widget_show (graph_window);
return;
}
graph_window = gtk_dialog_new_with_buttons
(_("Plot") /* title */,
GTK_WINDOW (genius_window) /* parent */,
0 /* flags */,
GTK_STOCK_STOP,
RESPONSE_STOP,
GTK_STOCK_CLOSE,
GTK_RESPONSE_CLOSE,
NULL);
g_signal_connect (G_OBJECT (graph_window),
"destroy",
G_CALLBACK (gtk_widget_destroyed),
&graph_window);
g_signal_connect (G_OBJECT (graph_window),
"delete_event",
G_CALLBACK (dialog_delete_event),
NULL);
g_signal_connect (G_OBJECT (graph_window),
"response",
G_CALLBACK (dialog_response),
NULL);
menubar = gtk_menu_bar_new ();
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (graph_window)->vbox),
GTK_WIDGET (menubar), FALSE, TRUE, 0);
menu = gtk_menu_new ();
item = gtk_menu_item_new_with_mnemonic (_("_Graph"));
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
gtk_menu_shell_append (GTK_MENU_SHELL (menubar), item);
item = gtk_menu_item_new_with_mnemonic (_("_Print..."));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (plot_print_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
plot_print_item = item;
item = gtk_menu_item_new_with_mnemonic (_("_Export postscript..."));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (plot_exportps_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
plot_exportps_item = item;
item = gtk_menu_item_new_with_mnemonic (_("E_xport encapsulated postscript..."));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (plot_exporteps_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
plot_exporteps_item = item;
item = gtk_menu_item_new_with_mnemonic (_("_Export PNG..."));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (plot_exportpng_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
plot_exportpng_item = item;
menu = gtk_menu_new ();
item = gtk_menu_item_new_with_mnemonic (_("_Zoom"));
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
gtk_menu_shell_append (GTK_MENU_SHELL (menubar), item);
item = gtk_menu_item_new_with_mnemonic (_("Zoom _out"));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (plot_zoomout_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
plot_zoomout_item = item;
item = gtk_menu_item_new_with_mnemonic (_("Zoom _in"));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (plot_zoomin_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
plot_zoomin_item = item;
item = gtk_menu_item_new_with_mnemonic (_("_Fit dependent axis"));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (plot_zoomfit_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
plot_zoomfit_item = item;
menu = gtk_menu_new ();
item = gtk_menu_item_new_with_mnemonic (_("_View"));
gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), menu);
gtk_menu_shell_append (GTK_MENU_SHELL (menubar), item);
surface_menu_item = item;
item = gtk_menu_item_new_with_mnemonic (_("_Reset angles"));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (reset_angles_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
item = gtk_menu_item_new_with_mnemonic (_("_Top view"));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (top_view_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
item = gtk_menu_item_new_with_mnemonic (_("R_otate axis..."));
g_signal_connect (G_OBJECT (item), "activate",
G_CALLBACK (rotate_cb), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
plot_canvas = gtk_plot_canvas_new (WIDTH, HEIGHT, 1.0);
GTK_PLOT_CANVAS_UNSET_FLAGS (GTK_PLOT_CANVAS (plot_canvas),
GTK_PLOT_CANVAS_DND_FLAGS);
g_signal_connect (G_OBJECT (plot_canvas), "select_region",
G_CALLBACK (plot_select_region),
NULL);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (graph_window)->vbox),
GTK_WIDGET (plot_canvas), TRUE, TRUE, 0);
gtk_widget_show_all (graph_window);
}
static void
clear_graph (void)
{
int i;
if (plot_child != NULL) {
if (plot_canvas != NULL)
gtk_plot_canvas_remove_child (GTK_PLOT_CANVAS (plot_canvas),
plot_child);
surface_plot = NULL;
line_plot = NULL;
plot_child = NULL;
}
for (i = 0; i < MAXFUNC; i++) {
line_data[i] = NULL;
}
parametric_data = NULL;
surface_data = NULL;
}
static void
get_ticks (double start, double end, double *tick, int *prec)
{
int incs;
double len = end-start;
int tries = 0;
*tick = pow (10, floor (log10(len)));
incs = floor (len / *tick);
while (incs < 3) {
*tick /= 2.0;
incs = floor (len / *tick);
/* sanity */
if (tries ++ > 100) {
*tick = len / 5;
*prec = - (int) log10 (*tick) + 1;
return;
}
}
while (incs > 6) {
*tick *= 2.0;
incs = floor (len / *tick);
/* sanity */
if (tries ++ > 200) {
*tick = len / 5;
*prec = - (int) log10 (*tick) + 1;
return;
}
}
if (*tick >= 0.99) {
*prec = 0;
} else {
*prec = - (int) log10 (*tick) + 1;
}
}
static void
plot_setup_axis (void)
{
int xprec, yprec;
double xtick, ytick;
GtkPlotAxis *x, *y;
get_ticks (plotx1, plotx2, &xtick, &xprec);
get_ticks (ploty1, ploty2, &ytick, &yprec);
gtk_plot_freeze (GTK_PLOT (line_plot));
gtk_plot_set_range (GTK_PLOT (line_plot),
plotx1, plotx2, ploty1, ploty2);
gtk_plot_set_ticks (GTK_PLOT (line_plot), GTK_PLOT_AXIS_X, xtick, 9);
gtk_plot_set_ticks (GTK_PLOT (line_plot), GTK_PLOT_AXIS_Y, ytick, 9);
x = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_TOP);
y = gtk_plot_get_axis (GTK_PLOT (line_plot), GTK_PLOT_AXIS_BOTTOM);
gtk_plot_axis_set_labels_style (x,
GTK_PLOT_LABEL_FLOAT,
xprec /* precision */);
gtk_plot_axis_set_labels_style (y,
GTK_PLOT_LABEL_FLOAT,
yprec /* precision */);
/* FIXME: implement logarithmic scale
gtk_plot_set_xscale (GTK_PLOT (line_plot), GTK_PLOT_SCALE_LOG10);
gtk_plot_set_yscale (GTK_PLOT (line_plot), GTK_PLOT_SCALE_LOG10);
*/
gtk_plot_thaw (GTK_PLOT (line_plot));
}
static void
surface_setup_axis (void)
{
int xprec, yprec, zprec;
double xtick, ytick, ztick;
GtkPlotAxis *x, *y, *z;
get_ticks (surfacex1, surfacex2, &xtick, &xprec);
get_ticks (surfacey1, surfacey2, &ytick, &yprec);
get_ticks (surfacez1, surfacez2, &ztick, &zprec);
x = gtk_plot3d_get_axis (GTK_PLOT3D (surface_plot), GTK_PLOT_AXIS_X);
y = gtk_plot3d_get_axis (GTK_PLOT3D (surface_plot), GTK_PLOT_AXIS_Y);
z = gtk_plot3d_get_axis (GTK_PLOT3D (surface_plot), GTK_PLOT_AXIS_Z);
gtk_plot_axis_freeze (x);
gtk_plot_axis_freeze (y);
gtk_plot_axis_freeze (z);
gtk_plot3d_set_xrange (GTK_PLOT3D (surface_plot), surfacex1, surfacex2);
gtk_plot_axis_set_ticks (x, xtick, 1);
gtk_plot3d_set_yrange (GTK_PLOT3D (surface_plot), surfacey1, surfacey2);
gtk_plot_axis_set_ticks (y, ytick, 1);
gtk_plot3d_set_zrange (GTK_PLOT3D (surface_plot), surfacez1, surfacez2);
gtk_plot_axis_set_ticks (z, ztick, 1);
gtk_plot_axis_set_labels_style (x,
GTK_PLOT_LABEL_FLOAT,
xprec /* precision */);
gtk_plot_axis_set_labels_style (y,
GTK_PLOT_LABEL_FLOAT,
yprec /* precision */);
gtk_plot_axis_set_labels_style (z,
GTK_PLOT_LABEL_FLOAT,
zprec /* precision */);
gtk_plot_axis_thaw (x);
gtk_plot_axis_thaw (y);
gtk_plot_axis_thaw (z);
}
/* FIXME: perhaps should be smarter ? */
static void
surface_setup_steps (void)
{
gtk_plot_surface_set_xstep (GTK_PLOT_SURFACE (surface_data), (surfacex2-surfacex1)/30);
gtk_plot_surface_set_ystep (GTK_PLOT_SURFACE (surface_data), (surfacey2-surfacey1)/30);
gtk_plot_data_set_gradient (surface_data,
surfacez1,
surfacez2,
10 /* nlevels */,
0 /* nsublevels */);
}
static void
plot_axis (void)
{
plot_in_progress ++;
plot_window_setup ();
plot_maxy = - G_MAXDOUBLE/2;
plot_miny = G_MAXDOUBLE/2;
plot_maxx = - G_MAXDOUBLE/2;
plot_minx = G_MAXDOUBLE/2;
if (plot_mode == MODE_LINEPLOT ||
plot_mode == MODE_LINEPLOT_PARAMETRIC) {
plot_setup_axis ();
} else if (plot_mode == MODE_SURFACE) {
surface_setup_axis ();
surface_setup_steps ();
/* FIXME: this doesn't work (crashes) must fix in GtkExtra, then
we can always just autoscale stuff
gtk_plot3d_autoscale (GTK_PLOT3D (surface_plot));
*/
}
if (plot_canvas != NULL) {
gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
}
plot_in_progress --;
plot_window_setup ();
}
static double
call_func3 (GelCtx *ctx,
GelEFunc *func,
GelETree *arg,
GelETree *arg2,
GelETree *arg3,
gboolean *ex,
GelETree **func_ret)
{
GelETree *ret;
double retd;
GelETree *args[4];
args[0] = arg;
args[1] = arg2;
args[2] = arg3;
args[3] = NULL;
ret = funccall (ctx, func, args, 3);
/* FIXME: handle errors! */
if (error_num != 0)
error_num = 0;
/* only do one level of indirection to avoid infinite loops */
if (ret != NULL && ret->type == FUNCTION_NODE) {
if (ret->func.func->nargs == 3) {
GelETree *ret2;
ret2 = funccall (ctx, ret->func.func, args, 3);
gel_freetree (ret);
ret = ret2;
/* FIXME: handle errors! */
if (error_num != 0)
error_num = 0;
} else if (func_ret != NULL) {
*func_ret = ret;
#ifdef HUGE_VAL
return HUGE_VAL;
#else
return 0;
#endif
}
}
if (ret == NULL || ret->type != VALUE_NODE) {
*ex = TRUE;
gel_freetree (ret);
#ifdef HUGE_VAL
return HUGE_VAL;
#else
return 0;
#endif
}
retd = mpw_get_double (ret->val.value);
if (error_num != 0) {
*ex = TRUE;
error_num = 0;
#ifdef HUGE_VAL
retd = HUGE_VAL;
#endif
}
gel_freetree (ret);
return retd;
}
static double
call_func2 (GelCtx *ctx,
GelEFunc *func,
GelETree *arg,
GelETree *arg2,
gboolean *ex,
GelETree **func_ret)
{
GelETree *ret;
double retd;
GelETree *args[3];
args[0] = arg;
args[1] = arg2;
args[2] = NULL;
ret = funccall (ctx, func, args, 2);
/* FIXME: handle errors! */
if (error_num != 0)
error_num = 0;
/* only do one level of indirection to avoid infinite loops */
if (ret != NULL && ret->type == FUNCTION_NODE) {
if (ret->func.func->nargs == 2) {
GelETree *ret2;
ret2 = funccall (ctx, ret->func.func, args, 2);
gel_freetree (ret);
ret = ret2;
/* FIXME: handle errors! */
if (error_num != 0)
error_num = 0;
} else if (func_ret != NULL) {
*func_ret = ret;
#ifdef HUGE_VAL
return HUGE_VAL;
#else
return 0;
#endif
}
}
if (ret == NULL || ret->type != VALUE_NODE) {
*ex = TRUE;
gel_freetree (ret);
#ifdef HUGE_VAL
return HUGE_VAL;
#else
return 0;
#endif
}
retd = mpw_get_double (ret->val.value);
if (error_num != 0) {
*ex = TRUE;
error_num = 0;
#ifdef HUGE_VAL
retd = HUGE_VAL;
#endif
}
gel_freetree (ret);
return retd;
}
static double
call_func (GelCtx *ctx,
GelEFunc *func,
GelETree *arg,
gboolean *ex,
GelETree **func_ret)
{
GelETree *ret;
double retd;
GelETree *args[2];
args[0] = arg;
args[1] = NULL;
ret = funccall (ctx, func, args, 1);
/* FIXME: handle errors! */
if (error_num != 0)
error_num = 0;
/* only do one level of indirection to avoid infinite loops */
if (ret != NULL && ret->type == FUNCTION_NODE) {
if (ret->func.func->nargs == 1) {
GelETree *ret2;
ret2 = funccall (ctx, ret->func.func, args, 1);
gel_freetree (ret);
ret = ret2;
/* FIXME: handle errors! */
if (error_num != 0)
error_num = 0;
} else if (func_ret != NULL) {
*func_ret = ret;
#ifdef HUGE_VAL
return HUGE_VAL;
#else
return 0;
#endif
}
}
if (ret == NULL || ret->type != VALUE_NODE) {
*ex = TRUE;
gel_freetree (ret);
#ifdef HUGE_VAL
return HUGE_VAL;
#else
return 0;
#endif
}
retd = mpw_get_double (ret->val.value);
if (error_num != 0) {
*ex = TRUE;
error_num = 0;
#ifdef HUGE_VAL
retd = HUGE_VAL;
#endif
}
gel_freetree (ret);
return retd;
}
static void
call_func_z (GelCtx *ctx,
GelEFunc *func,
GelETree *arg,
double *retx,
double *rety,
gboolean *ex,
GelETree **func_ret)
{
GelETree *ret;
GelETree *args[2];
args[0] = arg;
args[1] = NULL;
ret = funccall (ctx, func, args, 1);
/* FIXME: handle errors! */
if (error_num != 0)
error_num = 0;
/* only do one level of indirection to avoid infinite loops */
if (ret != NULL && ret->type == FUNCTION_NODE) {
if (ret->func.func->nargs == 1) {
GelETree *ret2;
ret2 = funccall (ctx, ret->func.func, args, 1);
gel_freetree (ret);
ret = ret2;
/* FIXME: handle errors! */
if (error_num != 0)
error_num = 0;
} else if (func_ret != NULL) {
*func_ret = ret;
#ifdef HUGE_VAL
*retx = HUGE_VAL;
*rety = HUGE_VAL;
#else
*retx = 0.0;
*rety = 0.0;
#endif
return;
}
}
if (ret == NULL || ret->type != VALUE_NODE) {
*ex = TRUE;
gel_freetree (ret);
#ifdef HUGE_VAL
*retx = HUGE_VAL;
*rety = HUGE_VAL;
#else
*retx = 0.0;
*rety = 0.0;
#endif
return;
}
mpw_get_complex_double (ret->val.value, retx, rety);
if (error_num != 0) {
*ex = TRUE;
error_num = 0;
#ifdef HUGE_VAL
*retx = HUGE_VAL;
*rety = HUGE_VAL;
#else
*retx = 0.0;
*rety = 0.0;
#endif
}
gel_freetree (ret);
}
static double
plot_func_data (GtkPlot *plot, GtkPlotData *data, double x, gboolean *error)
{
static int hookrun = 0;
gboolean ex = FALSE;
int i;
double y;
if (error != NULL)
*error = FALSE;
if G_UNLIKELY (interrupted) {
if (error != NULL)
*error = TRUE;
return 0.0;
}
for (i = 0; i < MAXFUNC; i++) {
if (data == line_data[i])
break;
}
if G_UNLIKELY (i == MAXFUNC) {
if (error != NULL)
*error = TRUE;
return 0.0;
}
mpw_set_d (plot_arg->val.value, x);
y = call_func (plot_ctx, plot_func[i], plot_arg, &ex, NULL);
if G_UNLIKELY (ex) {
if (error != NULL)
*error = TRUE;
} else {
if G_UNLIKELY (y > plot_maxy)
plot_maxy = y;
if G_UNLIKELY (y < plot_miny)
plot_miny = y;
}
if (y > ploty2 || y < ploty1) {
if (error != NULL)
*error = TRUE;
}
if (hookrun++ >= 10) {
hookrun = 0;
if (evalnode_hook != NULL) {
(*evalnode_hook)();
if G_UNLIKELY (interrupted) {
if (error != NULL)
*error = TRUE;
return y;
}
}
}
return y;
}
static double
surface_func_data (GtkPlot *plot, GtkPlotData *data, double x, double y, gboolean *error)
{
static int hookrun = 0;
gboolean ex = FALSE;
double z, size;
GelETree *func_ret = NULL;
if (error != NULL)
*error = FALSE;
if G_UNLIKELY (interrupted) {
if (error != NULL)
*error = TRUE;
return 0.0;
}
/* complex function */
if (surface_func->nargs == 1) {
mpw_set_d_complex (plot_arg->val.value, x, y);
z = call_func (plot_ctx, surface_func, plot_arg, &ex,
&func_ret);
} else if (surface_func->nargs == 2) {
mpw_set_d (plot_arg->val.value, x);
mpw_set_d (plot_arg2->val.value, y);
z = call_func2 (plot_ctx, surface_func, plot_arg, plot_arg2,
&ex, &func_ret);
} else {
mpw_set_d (plot_arg->val.value, x);
mpw_set_d (plot_arg2->val.value, y);
mpw_set_d_complex (plot_arg3->val.value, x, y);
z = call_func3 (plot_ctx, surface_func, plot_arg, plot_arg2,
plot_arg3, &ex, &func_ret);
}
if (func_ret != NULL) {
/* complex function */
if (func_ret->func.func->nargs == 1) {
mpw_set_d_complex (plot_arg->val.value, x, y);
z = call_func (plot_ctx, func_ret->func.func, plot_arg, &ex,
NULL);
} else if (func_ret->func.func->nargs == 2) {
mpw_set_d (plot_arg->val.value, x);
mpw_set_d (plot_arg2->val.value, y);
z = call_func2 (plot_ctx, func_ret->func.func, plot_arg, plot_arg2,
&ex, NULL);
} else {
mpw_set_d (plot_arg->val.value, x);
mpw_set_d (plot_arg2->val.value, y);
mpw_set_d_complex (plot_arg3->val.value, x, y);
z = call_func3 (plot_ctx, func_ret->func.func, plot_arg, plot_arg2,
plot_arg3, &ex, NULL);
}
}
if G_UNLIKELY (ex) {
if (error != NULL)
*error = TRUE;
} else {
if G_UNLIKELY (z > plot_maxy)
plot_maxy = z;
if G_UNLIKELY (z < plot_miny)
plot_miny = z;
}
size = surfacez1 - surfacez2;
if (z > (surfacez2+size*0.2) || z < (surfacez1-size*0.2)) {
if (error != NULL)
*error = TRUE;
}
if (hookrun++ >= 10) {
hookrun = 0;
if (evalnode_hook != NULL) {
(*evalnode_hook)();
if G_UNLIKELY (interrupted) {
if (error != NULL)
*error = TRUE;
return z;
}
}
}
return z;
}
static char *
label_func (int i, GelEFunc *func, const char *var, const char *name)
{
char *text = NULL;
if (name != NULL) {
return g_strdup (name);
} else if (func->id != NULL) {
text = g_strdup_printf ("%s(%s)", func->id->token, var);
} else if (func->type == GEL_USER_FUNC) {
int old_style, len;
GelOutput *out = gel_output_new ();
D_ENSURE_USER_BODY (func);
gel_output_setup_string (out, 0, NULL);
/* FIXME: the push/pop of style is UGLY */
old_style = calcstate.output_style;
calcstate.output_style = GEL_OUTPUT_NORMAL;
gel_print_etree (out, func->data.user, TRUE /* toplevel */);
calcstate.output_style = old_style;
text = gel_output_snarf_string (out);
gel_output_unref (out);
len = strlen (text);
if (len > 2 &&
text[0] == '(' &&
text[len-1] == ')') {
text[len-1] = '\0';
strcpy (text, &text[1]);
len-=2;
}
/* only print bodies of short functions */
if (len > 64) {
g_free (text);
text = NULL;
}
}
if (text == NULL) {
if (i < 0)
text = g_strdup_printf (_("Function"));
else
text = g_strdup_printf (_("Function #%d"), i+1);
}
return text;
}
#define GET_DOUBLE(var,argnum,func) \
{ \
if (a[argnum]->type != VALUE_NODE) { \
gel_errorout (_("%s: argument number %d not a number"), func, argnum+1); \
return NULL; \
} \
var = mpw_get_double (a[argnum]->val.value); \
}
static gboolean
get_limits_from_matrix (GelETree *m, double *x1, double *x2, double *y1, double *y2)
{
GelETree *t;
if (m->type != MATRIX_NODE ||
gel_matrixw_elements (m->mat.matrix) != 4) {
gel_errorout (_("Graph limits not given as a 4-vector"));
return FALSE;
}
t = gel_matrixw_vindex (m->mat.matrix, 0);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*x1 = mpw_get_double (t->val.value);
t = gel_matrixw_vindex (m->mat.matrix, 1);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*x2 = mpw_get_double (t->val.value);
t = gel_matrixw_vindex (m->mat.matrix, 2);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*y1 = mpw_get_double (t->val.value);
t = gel_matrixw_vindex (m->mat.matrix, 3);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*y2 = mpw_get_double (t->val.value);
/* FIXME: what about errors */
if (error_num != 0) {
error_num = 0;
return FALSE;
}
if (*x1 > *x2) {
double s = *x1;
*x1 = *x2;
*x2 = s;
}
if (*y1 > *y2) {
double s = *y1;
*y1 = *y2;
*y2 = s;
}
/* sanity */
if (*x2 - *x1 < MINPLOT)
*x2 = *x1 + MINPLOT;
if (*y2 - *y1 < MINPLOT)
*y2 = *y1 + MINPLOT;
return TRUE;
}
static GelETree *
make_matrix_from_limits (void)
{
GelETree *n;
GelMatrixW *m;
/*make us a new empty node*/
GET_NEW_NODE (n);
n->type = MATRIX_NODE;
m = n->mat.matrix = gel_matrixw_new ();
n->mat.quoted = FALSE;
gel_matrixw_set_size (m, 4, 1);
gel_matrixw_set_index (m, 0, 0) = gel_makenum_d (defx1);
gel_matrixw_set_index (m, 1, 0) = gel_makenum_d (defx2);
gel_matrixw_set_index (m, 2, 0) = gel_makenum_d (defy1);
gel_matrixw_set_index (m, 3, 0) = gel_makenum_d (defy2);
return n;
}
static gboolean
get_limits_from_matrix_surf (GelETree *m, double *x1, double *x2, double *y1, double *y2, double *z1, double *z2)
{
GelETree *t;
if (m->type != MATRIX_NODE ||
gel_matrixw_elements (m->mat.matrix) != 6) {
gel_errorout (_("Graph limits not given as a 6-vector"));
return FALSE;
}
t = gel_matrixw_vindex (m->mat.matrix, 0);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*x1 = mpw_get_double (t->val.value);
t = gel_matrixw_vindex (m->mat.matrix, 1);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*x2 = mpw_get_double (t->val.value);
t = gel_matrixw_vindex (m->mat.matrix, 2);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*y1 = mpw_get_double (t->val.value);
t = gel_matrixw_vindex (m->mat.matrix, 3);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*y2 = mpw_get_double (t->val.value);
t = gel_matrixw_vindex (m->mat.matrix, 4);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*z1 = mpw_get_double (t->val.value);
t = gel_matrixw_vindex (m->mat.matrix, 5);
if (t->type != VALUE_NODE) {
gel_errorout (_("Graph limits not given as numbers"));
return FALSE;
}
*z2 = mpw_get_double (t->val.value);
/* FIXME: what about errors */
if (error_num != 0) {
error_num = 0;
return FALSE;
}
if (*x1 > *x2) {
double s = *x1;
*x1 = *x2;
*x2 = s;
}
if (*y1 > *y2) {
double s = *y1;
*y1 = *y2;
*y2 = s;
}
if (*z1 > *z2) {
double s = *z1;
*z1 = *z2;
*z2 = s;
}
/* sanity */
if (*x2 - *x1 < MINPLOT)
*x2 = *x1 + MINPLOT;
if (*y2 - *y1 < MINPLOT)
*y2 = *y1 + MINPLOT;
if (*z2 - *z1 < MINPLOT)
*z2 = *z1 + MINPLOT;
return TRUE;
}
static GelETree *
make_matrix_from_limits_surf (void)
{
GelETree *n;
GelMatrixW *m;
/*make us a new empty node*/
GET_NEW_NODE (n);
n->type = MATRIX_NODE;
m = n->mat.matrix = gel_matrixw_new ();
n->mat.quoted = FALSE;
gel_matrixw_set_size (m, 6, 1);
gel_matrixw_set_index (m, 0, 0) = gel_makenum_d (surf_defx1);
gel_matrixw_set_index (m, 1, 0) = gel_makenum_d (surf_defx2);
gel_matrixw_set_index (m, 2, 0) = gel_makenum_d (surf_defy1);
gel_matrixw_set_index (m, 3, 0) = gel_makenum_d (surf_defy2);
gel_matrixw_set_index (m, 4, 0) = gel_makenum_d (surf_defz1);
gel_matrixw_set_index (m, 5, 0) = gel_makenum_d (surf_defz2);
return n;
}
static gboolean
parametric_get_value (double *x, double *y, double t)
{
static int hookrun = 0;
gboolean ex = FALSE;
mpw_set_d (plot_arg->val.value, t);
if (parametric_func_z != NULL) {
call_func_z (plot_ctx, parametric_func_z, plot_arg, x, y, &ex, NULL);
} else {
*x = call_func (plot_ctx, parametric_func_x, plot_arg, &ex, NULL);
if G_LIKELY ( ! ex)
*y = call_func (plot_ctx, parametric_func_y, plot_arg, &ex, NULL);
}
if G_UNLIKELY (ex) {
*x = 0.0;
*y = 0.0;
return FALSE;
} else {
if G_UNLIKELY (*y > plot_maxy)
plot_maxy = *y;
if G_UNLIKELY (*y < plot_miny)
plot_miny = *y;
if G_UNLIKELY (*x > plot_maxx)
plot_maxx = *x;
if G_UNLIKELY (*x < plot_minx)
plot_minx = *x;
}
/* FIXME: sanity on x/y ??? */
if (hookrun++ >= 10) {
hookrun = 0;
if (evalnode_hook != NULL) {
(*evalnode_hook)();
}
}
return TRUE;
}
static void
plot_functions (void)
{
char *colors[] = {
"darkblue",
"darkgreen",
"darkred",
"magenta",
"black",
"darkorange",
"blue",
"green",
"red",
"brown",
"yellow", /* should never get here, but just for sanity */
"orange",
NULL };
int i;
int color_i;
ensure_window ();
clear_graph ();
add_line_plot ();
gtk_widget_hide (surface_menu_item);
GTK_PLOT_CANVAS_SET_FLAGS (GTK_PLOT_CANVAS (plot_canvas),
GTK_PLOT_CANVAS_CAN_SELECT);
plot_in_progress ++;
plot_window_setup ();
if (evalnode_hook != NULL)
(*evalnode_hook)();
/* sanity */
if (plotx2 < plotx1) {
double t = plotx2;
plotx2 = plotx1;
plotx1 = t;
}
if (ploty2 < ploty1) {
double t = ploty2;
ploty2 = ploty1;
ploty1 = t;
}
/* sanity */
if (plotx2 - plotx1 < MINPLOT)
plotx2 = plotx1 + MINPLOT;
/* sanity */
if (ploty2 - ploty1 < MINPLOT)
ploty2 = ploty1 + MINPLOT;
plot_maxy = - G_MAXDOUBLE/2;
plot_miny = G_MAXDOUBLE/2;
plot_setup_axis ();
if G_UNLIKELY (plot_arg == NULL) {
plot_ctx = eval_get_context ();
}
if G_UNLIKELY (plot_arg == NULL) {
mpw_t xx;
mpw_init (xx);
plot_arg = gel_makenum_use (xx);
}
color_i = 0;
for (i = 0; i < MAXFUNC && plot_func[i] != NULL; i++) {
GdkColor color;
char *label;
line_data[i] = GTK_PLOT_DATA
(gtk_plot_data_new_function (plot_func_data));
gtk_plot_add_data (GTK_PLOT (line_plot),
line_data[i]);
gtk_widget_show (GTK_WIDGET (line_data[i]));
gdk_color_parse (colors[color_i++], &color);
gdk_color_alloc (gdk_colormap_get_system (), &color);
gtk_plot_data_set_line_attributes (line_data[i],
GTK_PLOT_LINE_SOLID,
0, 0, 2, &color);
label = label_func (i, plot_func[i], "x", plot_func_name[i]);
gtk_plot_data_set_legend (line_data[i], label);
g_free (label);
}
if ((parametric_func_x != NULL && parametric_func_y != NULL) ||
(parametric_func_z != NULL)) {
GdkColor color;
char *label;
int len;
double *x, *y, *dx, *dy;
double t;
parametric_data = GTK_PLOT_DATA (gtk_plot_data_new ());
/* could be one off, will adjust later */
len = MAX(ceil (((plott2 - plott1) / plottinc)) + 2,1);
x = g_new0 (double, len);
y = g_new0 (double, len);
dx = g_new0 (double, len);
dy = g_new0 (double, len);
t = plott1;
for (i = 0; i < len; i++) {
parametric_get_value (&(x[i]), &(y[i]), t);
if G_UNLIKELY (interrupted) {
break;
}
t = t + plottinc;
if (t >= plott2) {
i++;
parametric_get_value (&(x[i]), &(y[i]), plott2);
i++;
break;
}
}
/* how many actually went */
len = MAX(1,i);
gtk_plot_data_set_points (parametric_data, x, y, dx, dy, len);
gtk_plot_add_data (GTK_PLOT (line_plot), parametric_data);
gtk_widget_show (GTK_WIDGET (parametric_data));
gdk_color_parse (colors[color_i++], &color);
gdk_color_alloc (gdk_colormap_get_system (), &color);
gtk_plot_data_set_line_attributes (parametric_data,
GTK_PLOT_LINE_SOLID,
0, 0, 2, &color);
if (parametric_name != NULL) {
label = g_strdup (parametric_name);
} else if (parametric_func_z) {
label = label_func (-1, parametric_func_z, "t", NULL);
} else {
char *l1, *l2;
l1 = label_func (-1, parametric_func_x, "t", NULL);
l2 = label_func (-1, parametric_func_y, "t", NULL);
label = g_strconcat (l1, ", ", l2, NULL);
g_free (l1);
g_free (l2);
}
gtk_plot_data_set_legend (parametric_data, label);
g_free (label);
}
line_plot_move_about ();
/* could be whacked by closing the window or some such */
if (plot_canvas != NULL) {
gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
}
plot_in_progress --;
plot_window_setup ();
}
static void
plot_surface_functions (void)
{
ensure_window ();
clear_graph ();
add_surface_plot ();
gtk_widget_show (surface_menu_item);
GTK_PLOT_CANVAS_UNSET_FLAGS (GTK_PLOT_CANVAS (plot_canvas),
GTK_PLOT_CANVAS_CAN_SELECT);
plot_in_progress ++;
plot_window_setup ();
if (evalnode_hook != NULL)
(*evalnode_hook)();
/* sanity */
if (surfacex2 == surfacex1)
surfacex2 = surfacex1 + MINPLOT;
if (surfacey2 == surfacey1)
surfacey2 = surfacey1 + MINPLOT;
if (surfacez2 == surfacez1)
surfacez2 = surfacez1 + MINPLOT;
plot_maxy = - G_MAXDOUBLE/2;
plot_miny = G_MAXDOUBLE/2;
surface_setup_axis ();
gtk_plot3d_reset_angles (GTK_PLOT3D (surface_plot));
gtk_plot3d_rotate_x (GTK_PLOT3D (surface_plot), 60.0);
gtk_plot3d_rotate_z (GTK_PLOT3D (surface_plot), 30.0);
if G_UNLIKELY (plot_arg == NULL) {
plot_ctx = eval_get_context ();
}
if G_UNLIKELY (plot_arg == NULL) {
mpw_t xx;
mpw_init (xx);
plot_arg = gel_makenum_use (xx);
}
if G_UNLIKELY (plot_arg2 == NULL) {
mpw_t xx;
mpw_init (xx);
plot_arg2 = gel_makenum_use (xx);
}
if G_UNLIKELY (plot_arg3 == NULL) {
mpw_t xx;
mpw_init (xx);
plot_arg3 = gel_makenum_use (xx);
}
if (surface_func != NULL) {
char *label;
surface_data = GTK_PLOT_DATA
(gtk_plot_surface_new_function (surface_func_data));
gtk_plot_surface_use_amplitud (GTK_PLOT_SURFACE (surface_data), FALSE);
gtk_plot_surface_use_height_gradient (GTK_PLOT_SURFACE (surface_data), TRUE);
gtk_plot_surface_set_mesh_visible (GTK_PLOT_SURFACE (surface_data), TRUE);
gtk_plot_data_gradient_set_visible (GTK_PLOT_DATA (surface_data), TRUE);
gtk_plot_data_move_gradient (GTK_PLOT_DATA (surface_data),
0.93, 0.15);
gtk_plot_axis_hide_title (GTK_PLOT_DATA (surface_data)->gradient);
gtk_plot_add_data (GTK_PLOT (surface_plot),
surface_data);
surface_setup_steps ();
gtk_widget_show (GTK_WIDGET (surface_data));
label = label_func (-1, surface_func, /* FIXME: correct variable */ "...", surface_func_name);
gtk_plot_data_set_legend (surface_data, label);
g_free (label);
}
/* FIXME: this doesn't work (crashes) must fix in GtkExtra
gtk_plot3d_autoscale (GTK_PLOT3D (surface_plot));
*/
/* could be whacked by closing the window or some such */
if (plot_canvas != NULL) {
gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
gtk_widget_queue_draw (GTK_WIDGET (plot_canvas));
}
plot_in_progress --;
plot_window_setup ();
}
/*exact answer callback*/
static void
double_spin_cb(GtkAdjustment *adj, double *data)
{
*data = adj->value;
}
static void
entry_activate (void)
{
if (plot_dialog != NULL)
gtk_dialog_response (GTK_DIALOG (plot_dialog),
RESPONSE_PLOT);
}
static GtkWidget *
create_range_spinboxes (const char *title, double *val1, double *val2,
double *by)
{
GtkWidget *b, *w;
GtkAdjustment *adj;
b = gtk_hbox_new (FALSE, GNOME_PAD);
w = gtk_label_new(title);
gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
adj = (GtkAdjustment *)gtk_adjustment_new (*val1,
-G_MAXFLOAT,
G_MAXFLOAT,
1,
10,
100);
w = gtk_spin_button_new (adj, 1.0, 5);
g_signal_connect (G_OBJECT (w), "activate", entry_activate, NULL);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (w), TRUE);
gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (w), GTK_UPDATE_ALWAYS);
gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (w), FALSE);
gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (adj), "value_changed",
G_CALLBACK (double_spin_cb), val1);
w = gtk_label_new(_("to:"));
gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
adj = (GtkAdjustment *)gtk_adjustment_new (*val2,
-G_MAXFLOAT,
G_MAXFLOAT,
1,
10,
100);
w = gtk_spin_button_new (adj, 1.0, 5);
g_signal_connect (G_OBJECT (w), "activate", entry_activate, NULL);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (w), TRUE);
gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (w), GTK_UPDATE_ALWAYS);
gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (w), FALSE);
gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (adj), "value_changed",
G_CALLBACK (double_spin_cb), val2);
if (by != NULL) {
w = gtk_label_new(_("by:"));
gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
adj = (GtkAdjustment *)gtk_adjustment_new (*by,
-G_MAXFLOAT,
G_MAXFLOAT,
1,
10,
100);
w = gtk_spin_button_new (adj, 1.0, 5);
g_signal_connect (G_OBJECT (w), "activate", entry_activate, NULL);
gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (w), TRUE);
gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (w), GTK_UPDATE_ALWAYS);
gtk_spin_button_set_snap_to_ticks (GTK_SPIN_BUTTON (w), FALSE);
gtk_box_pack_start (GTK_BOX (b), w, FALSE, FALSE, 0);
g_signal_connect (G_OBJECT (adj), "value_changed",
G_CALLBACK (double_spin_cb), by);
}
return b;
}
static GtkWidget *
create_expression_box (const char *label,
GtkWidget **entry,
GtkWidget **status)
{
GtkWidget *b;
b = gtk_hbox_new (FALSE, GNOME_PAD);
gtk_box_pack_start (GTK_BOX (b),
gtk_label_new (label), FALSE, FALSE, 0);
*entry = gtk_entry_new ();
g_signal_connect (G_OBJECT (*entry), "activate",
entry_activate, NULL);
gtk_box_pack_start (GTK_BOX (b), *entry, TRUE, TRUE, 0);
*status = gtk_image_new ();
gtk_box_pack_start (GTK_BOX (b), *status, FALSE, FALSE, 0);
return b;
}
static GtkWidget *
create_lineplot_box (void)
{
GtkWidget *mainbox, *frame;
GtkWidget *box, *b, *w;
int i;
mainbox = gtk_vbox_new (FALSE, GNOME_PAD);
gtk_container_set_border_width (GTK_CONTAINER (mainbox), GNOME_PAD);
function_notebook = gtk_notebook_new ();
gtk_box_pack_start (GTK_BOX (mainbox), function_notebook, FALSE, FALSE, 0);
/*
* Line plot entries
*/
box = gtk_vbox_new(FALSE,GNOME_PAD);
gtk_container_set_border_width (GTK_CONTAINER (box), GNOME_PAD);
w = gtk_label_new (_("Type in function names or expressions involving "
"the x variable in the boxes below to graph "
"them"));
gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
for (i = 0; i < MAXFUNC; i++) {
b = create_expression_box ("y=",
&(plot_entries[i]),
&(plot_entries_status[i]));
gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
}
gtk_notebook_append_page (GTK_NOTEBOOK (function_notebook),
box,
gtk_label_new_with_mnemonic (_("_Functions / Expressions")));
/*
* Parametric plot entries
*/
box = gtk_vbox_new(FALSE,GNOME_PAD);
gtk_container_set_border_width (GTK_CONTAINER (box), GNOME_PAD);
w = gtk_label_new (_("Type in function names or expressions involving "
"the t variable in the boxes below to graph "
"them. Either fill in both boxes with x= and y= "
"in front of them giving the x and y coordinates "
"separately, or alternatively fill in the z= box "
"giving x and y as the real and imaginary part of "
"a complex number."));
gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
/* x */
b = create_expression_box ("x=",
¶metric_entry_x,
¶metric_status_x);
gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
/* y */
b = create_expression_box ("y=",
¶metric_entry_y,
¶metric_status_y);
gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
w = gtk_label_new (_("or"));
gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
/* z */
b = create_expression_box ("z=",
¶metric_entry_z,
¶metric_status_z);
gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
/* just spacing */
gtk_box_pack_start (GTK_BOX (box), gtk_label_new (""), FALSE, FALSE, 0);
/* t range */
b = create_range_spinboxes (_("Parameter t from:"),
&spint1,
&spint2,
&spintinc);
gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
gtk_notebook_append_page (GTK_NOTEBOOK (function_notebook),
box,
gtk_label_new_with_mnemonic (_("_Parametric")));
frame = gtk_frame_new (_("Plot Window"));
gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
box = gtk_vbox_new(FALSE,GNOME_PAD);
gtk_container_set_border_width (GTK_CONTAINER (box), GNOME_PAD);
gtk_container_add (GTK_CONTAINER (frame), box);
/*
* X range
*/
b = create_range_spinboxes (_("X from:"), &spinx1, &spinx2, NULL);
gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
/*
* Y range
*/
b = create_range_spinboxes (_("Y from:"), &spiny1, &spiny2, NULL);
gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
return mainbox;
}
static GtkWidget *
create_surface_box (void)
{
GtkWidget *mainbox, *frame;
GtkWidget *box, *b, *w;
mainbox = gtk_vbox_new (FALSE, GNOME_PAD);
gtk_container_set_border_width (GTK_CONTAINER (mainbox), GNOME_PAD);
frame = gtk_frame_new (_("Function / Expression"));
gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
box = gtk_vbox_new(FALSE,GNOME_PAD);
gtk_container_set_border_width (GTK_CONTAINER (box), GNOME_PAD);
gtk_container_add (GTK_CONTAINER (frame), box);
w = gtk_label_new (_("Type a function name or an expression involving "
"the x and y variables (or the z variable which will be z=x+iy) "
"in the boxes below to graph them. Functions with one argument only "
"will be passed a complex number."));
gtk_misc_set_alignment (GTK_MISC (w), 0.0, 0.5);
gtk_label_set_line_wrap (GTK_LABEL (w), TRUE);
gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
b = gtk_hbox_new (FALSE, GNOME_PAD);
gtk_box_pack_start (GTK_BOX (box), b, FALSE, FALSE, 0);
surface_entry = gtk_entry_new ();
g_signal_connect (G_OBJECT (surface_entry), "activate",
entry_activate, NULL);
gtk_box_pack_start (GTK_BOX (b), surface_entry, TRUE, TRUE, 0);
surface_entry_status = gtk_image_new ();
gtk_box_pack_start (GTK_BOX (b), surface_entry_status, FALSE, FALSE, 0);
frame = gtk_frame_new (_("Plot Window"));
gtk_box_pack_start (GTK_BOX (mainbox), frame, FALSE, FALSE, 0);
box = gtk_vbox_new(FALSE,GNOME_PAD);
gtk_container_set_border_width (GTK_CONTAINER (box), GNOME_PAD);
gtk_container_add (GTK_CONTAINER (frame), box);
/*
* X range
*/
b = create_range_spinboxes (_("X from:"), &surf_spinx1, &surf_spinx2,
NULL);
gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
/*
* Y range
*/
b = create_range_spinboxes (_("Y from:"), &surf_spiny1, &surf_spiny2,
NULL);
gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
/*
* Z range
*/
b = create_range_spinboxes (_("Z from:"), &surf_spinz1, &surf_spinz2,
NULL);
gtk_box_pack_start (GTK_BOX(box), b, FALSE, FALSE, 0);
return mainbox;
}
static GtkWidget *
create_plot_dialog (void)
{
plot_notebook = gtk_notebook_new ();
gtk_notebook_append_page (GTK_NOTEBOOK (plot_notebook),
create_lineplot_box (),
gtk_label_new_with_mnemonic (_("Function _line plot")));
gtk_notebook_append_page (GTK_NOTEBOOK (plot_notebook),
create_surface_box (),
gtk_label_new_with_mnemonic (_("_Surface plot")));
return plot_notebook;
}
static gboolean
is_letter_or_underscore (char l)
{
if ((l >= 'a' && l <= 'z') ||
(l >= 'A' && l <= 'Z') ||
l == '_')
return TRUE;
else
return FALSE;
}
static gboolean
is_letter_underscore_or_number (char l)
{
if ((l >= 'a' && l <= 'z') ||
(l >= 'A' && l <= 'Z') ||
(l >= '0' && l <= '9') ||
l == '_')
return TRUE;
else
return FALSE;
}
static gboolean
is_identifier (const char *e)
{
int i;
if ( ! is_letter_or_underscore (e[0]))
return FALSE;
for (i = 1; e[i] != '\0'; i++) {
if ( ! is_letter_underscore_or_number (e[i]))
return FALSE;
}
return TRUE;
}
static GelEFunc *
function_from_expression (const char *e, const char *var, gboolean *ex)
{
GelEFunc *f = NULL;
GelETree *value;
char *ce;
if (ve_string_empty (e))
return NULL;
ce = g_strstrip (g_strdup (e));
if (is_identifier (ce) && strcmp (ce, var) != 0) {
f = d_lookup_global (d_intern (ce));
g_free (ce);
if (f != NULL) {
f = d_copyfunc (f);
f->context = -1;
} else {
*ex = TRUE;
}
return f;
}
value = gel_parseexp (ce,
NULL /* infile */,
FALSE /* exec_commands */,
FALSE /* testparse */,
NULL /* finished */,
NULL /* dirprefix */);
g_free (ce);
/* FIXME: if "x" (var) not used try to evaluate and if it returns a function use that */
if (value != NULL) {
f = d_makeufunc (NULL /* id */,
value,
g_slist_append (NULL, d_intern (var)),
1,
NULL /* extra_dict */);
}
if (f == NULL)
*ex = TRUE;
return f;
}
static GelEFunc *
function_from_expression2 (const char *e, gboolean *ex)
{
GelEFunc *f = NULL;
GelETree *value;
char *ce;
gboolean got_x, got_y, got_z;
if (ve_string_empty (e))
return NULL;
ce = g_strstrip (g_strdup (e));
if (is_identifier (ce) &&
strcmp (ce, "x") != 0 &&
strcmp (ce, "y") != 0 &&
strcmp (ce, "z") != 0) {
f = d_lookup_global (d_intern (ce));
g_free (ce);
if (f != NULL) {
f = d_copyfunc (f);
f->context = -1;
} else {
*ex = TRUE;
}
return f;
}
value = gel_parseexp (ce,
NULL /* infile */,
FALSE /* exec_commands */,
FALSE /* testparse */,
NULL /* finished */,
NULL /* dirprefix */);
g_free (ce);
/* FIXME: funcbody? I think it must be done. */
got_x = eval_find_identifier (value, d_intern ("x"), TRUE /*funcbody*/);
got_y = eval_find_identifier (value, d_intern ("y"), TRUE /*funcbody*/);
got_z = eval_find_identifier (value, d_intern ("z"), TRUE /*funcbody*/);
/* FIXME: if "x" or "y" or "z" not used try to evaluate and if it returns a function use that */
if (value != NULL) {
if ( ! got_x && ! got_y && got_z) {
f = d_makeufunc (NULL /* id */,
value,
g_slist_append (NULL, d_intern ("z")),
1,
NULL /* extra_dict */);
} else if ( ! got_z) {
GSList *l = g_slist_append (NULL, d_intern ("x"));
l = g_slist_append (l, d_intern ("y"));
f = d_makeufunc (NULL /* id */,
value,
l,
2,
NULL /* extra_dict */);
} else {
GSList *l = g_slist_append (NULL, d_intern ("x"));
l = g_slist_append (l, d_intern ("y"));
l = g_slist_append (l, d_intern ("z"));
f = d_makeufunc (NULL /* id */,
value,
l,
3,
NULL /* extra_dict */);
}
}
if (f == NULL)
*ex = TRUE;
return f;
}
static void
surface_from_dialog (void)
{
GelEFunc *func = { NULL };
double x1, x2, y1, y2, z1, z2;
gboolean last_info;
gboolean last_error;
const char *error_to_print = NULL;
gboolean ex;
const char *str;
plot_mode = MODE_SURFACE;
last_info = genius_setup.info_box;
last_error = genius_setup.error_box;
genius_setup.info_box = TRUE;
genius_setup.error_box = TRUE;
ex = FALSE;
str = gtk_entry_get_text (GTK_ENTRY (surface_entry));
func = function_from_expression2 (str, &ex);
if (func != NULL) {
gtk_image_set_from_stock
(GTK_IMAGE (surface_entry_status),
GTK_STOCK_YES,
GTK_ICON_SIZE_MENU);
} else if (ex) {
gtk_image_set_from_stock
(GTK_IMAGE (surface_entry_status),
GTK_STOCK_DIALOG_WARNING,
GTK_ICON_SIZE_MENU);
} else {
gtk_image_set_from_pixbuf
(GTK_IMAGE (surface_entry_status),
NULL);
}
if (func == NULL) {
error_to_print = _("No functions to plot or no functions "
"could be parsed");
goto whack_copied_funcs;
}
x1 = surf_spinx1;
x2 = surf_spinx2;
y1 = surf_spiny1;
y2 = surf_spiny2;
z1 = surf_spinz1;
z2 = surf_spinz2;
if (x1 > x2) {
double s = x1;
x1 = x2;
x2 = s;
}
if (y1 > y2) {
double s = y1;
y1 = y2;
y2 = s;
}
if (z1 > z2) {
double s = z1;
z1 = z2;
z2 = s;
}
if (x1 == x2) {
error_to_print = _("Invalid X range");
goto whack_copied_funcs;
}
if (y1 == y2) {
error_to_print = _("Invalid Y range");
goto whack_copied_funcs;
}
if (z1 == z2) {
error_to_print = _("Invalid Z range");
goto whack_copied_funcs;
}
surfacex1 = x1;
surfacex2 = x2;
surfacey1 = y1;
surfacey2 = y2;
surfacez1 = z1;
surfacez2 = z2;
if (surface_func != NULL) {
d_freefunc (surface_func);
surface_func = NULL;
}
g_free (surface_func_name);
surface_func_name = NULL;
surface_func = func;
func = NULL;
/* setup name when the functions don't have their own name */
if (surface_func->id == NULL)
surface_func_name = g_strdup (str);
plot_mode = MODE_SURFACE;
plot_surface_functions ();
if (interrupted)
interrupted = FALSE;
gel_printout_infos ();
genius_setup.info_box = last_info;
genius_setup.error_box = last_error;
return;
whack_copied_funcs:
if (func != NULL) {
d_freefunc (func);
func = NULL;
}
gel_printout_infos ();
genius_setup.info_box = last_info;
genius_setup.error_box = last_error;
if (error_to_print != NULL)
genius_display_error (genius_window, error_to_print);
}
static void
line_plot_clear_funcs (void)
{
int i;
for (i = 0; i < MAXFUNC && plot_func[i] != NULL; i++) {
d_freefunc (plot_func[i]);
plot_func[i] = NULL;
g_free (plot_func_name[i]);
plot_func_name[i] = NULL;
}
d_freefunc (parametric_func_x);
parametric_func_x = NULL;
d_freefunc (parametric_func_y);
parametric_func_y = NULL;
d_freefunc (parametric_func_z);
parametric_func_z = NULL;
g_free (parametric_name);
parametric_name = NULL;
}
static GelEFunc *
get_func_from_entry (GtkWidget *entry, GtkWidget *status,
const char *var, gboolean *ex)
{
GelEFunc *f;
const char *str = gtk_entry_get_text (GTK_ENTRY (entry));
f = function_from_expression (str, var, ex);
if (f != NULL) {
gtk_image_set_from_stock
(GTK_IMAGE (status),
GTK_STOCK_YES,
GTK_ICON_SIZE_MENU);
} else if (*ex) {
gtk_image_set_from_stock
(GTK_IMAGE (status),
GTK_STOCK_DIALOG_WARNING,
GTK_ICON_SIZE_MENU);
f = NULL;
} else {
gtk_image_set_from_pixbuf
(GTK_IMAGE (status),
NULL);
f = NULL;
}
return f;
}
static void
plot_from_dialog (void)
{
int funcs = 0;
gboolean got_param = FALSE;
GelEFunc *func[MAXFUNC] = { NULL };
GelEFunc *funcpx = NULL;
GelEFunc *funcpy = NULL;
GelEFunc *funcpz = NULL;
double x1, x2, y1, y2;
int i;
gboolean last_info;
gboolean last_error;
const char *error_to_print = NULL;
int function_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (function_notebook));
plot_mode = MODE_LINEPLOT;
last_info = genius_setup.info_box;
last_error = genius_setup.error_box;
genius_setup.info_box = TRUE;
genius_setup.error_box = TRUE;
if (function_page == 0) {
for (i = 0; i < MAXFUNC; i++) {
GelEFunc *f;
gboolean ex = FALSE;
f = get_func_from_entry (plot_entries[i],
plot_entries_status[i],
"x",
&ex);
if (f != NULL) {
func[funcs++] = f;
}
}
} else {
gboolean exx = FALSE;
gboolean exy = FALSE;
gboolean exz = FALSE;
funcpx = get_func_from_entry (parametric_entry_x,
parametric_status_x,
"t",
&exx);
funcpy = get_func_from_entry (parametric_entry_y,
parametric_status_y,
"t",
&exy);
funcpz = get_func_from_entry (parametric_entry_z,
parametric_status_z,
"t",
&exz);
if (((funcpx || exx) || (funcpy || exy)) && (funcpz || exz)) {
error_to_print = _("Only specify x and y, or z, not all at once.");
goto whack_copied_funcs;
}
if ((funcpz == NULL && funcpx != NULL && funcpy != NULL) ||
(funcpz != NULL && funcpx == NULL && funcpy == NULL)) {
got_param = TRUE;
}
}
if (funcs == 0 && ! got_param) {
error_to_print = _("No functions to plot or no functions "
"could be parsed");
goto whack_copied_funcs;
}
if (function_page == 1) {
if (spint1 >= spint2 ||
spintinc <= 0.0) {
error_to_print = _("Invalid t range");
goto whack_copied_funcs;
}
}
x1 = spinx1;
x2 = spinx2;
y1 = spiny1;
y2 = spiny2;
if (x1 > x2) {
double s = x1;
x1 = x2;
x2 = s;
}
if (y1 > y2) {
double s = y1;
y1 = y2;
y2 = s;
}
if (x1 == x2) {
error_to_print = _("Invalid X range");
goto whack_copied_funcs;
}
if (y1 == y2) {
error_to_print = _("Invalid Y range");
goto whack_copied_funcs;
}
plotx1 = x1;
plotx2 = x2;
ploty1 = y1;
ploty2 = y2;
if (function_page == 1) {
plott1 = spint1;
plott2 = spint2;
plottinc = spintinc;
}
line_plot_clear_funcs ();
if (function_page == 0) {
for (i = 0; i < MAXFUNC && func[i] != NULL; i++) {
plot_func[i] = func[i];
func[i] = NULL;
/* setup name when the functions don't have their own name */
if (plot_func[i]->id == NULL)
plot_func_name[i] = g_strdup (gtk_entry_get_text (GTK_ENTRY (plot_entries[i])));
}
plot_mode = MODE_LINEPLOT;
} else {
parametric_func_x = funcpx;
parametric_func_y = funcpy;
parametric_func_z = funcpz;
if (funcpz != NULL) {
parametric_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (parametric_entry_z)));
} else {
parametric_name = g_strconcat (gtk_entry_get_text (GTK_ENTRY (parametric_entry_x)),
",",
gtk_entry_get_text (GTK_ENTRY (parametric_entry_y)),
NULL);
}
plot_mode = MODE_LINEPLOT_PARAMETRIC;
}
plot_functions ();
if (interrupted)
interrupted = FALSE;
gel_printout_infos ();
genius_setup.info_box = last_info;
genius_setup.error_box = last_error;
return;
whack_copied_funcs:
for (i = 0; i < MAXFUNC && func[i] != NULL; i++) {
d_freefunc (func[i]);
func[i] = NULL;
}
d_freefunc (funcpx);
funcpx = NULL;
d_freefunc (funcpy);
funcpy = NULL;
d_freefunc (funcpz);
funcpz = NULL;
gel_printout_infos ();
genius_setup.info_box = last_info;
genius_setup.error_box = last_error;
if (error_to_print != NULL)
genius_display_error (genius_window, error_to_print);
}
static void
plot_dialog_response (GtkWidget *w, int response, gpointer data)
{
if (response == GTK_RESPONSE_CLOSE ||
response == GTK_RESPONSE_DELETE_EVENT) {
gtk_widget_destroy (plot_dialog);
} else if (response == RESPONSE_PLOT) {
int pg = gtk_notebook_get_current_page (GTK_NOTEBOOK (plot_notebook));
if (pg == 0 /* line plot */)
plot_from_dialog ();
else if (pg == 1 /* surface plot */)
surface_from_dialog ();
}
}
void
genius_plot_dialog (void)
{
GtkWidget *insides;
if (plot_dialog != NULL) {
gtk_window_present (GTK_WINDOW (plot_dialog));
return;
}
plot_dialog = gtk_dialog_new_with_buttons
(_("Create Plot") /* title */,
GTK_WINDOW (genius_window) /* parent */,
0 /* flags */,
GTK_STOCK_CLOSE,
GTK_RESPONSE_CLOSE,
_("_Plot"),
RESPONSE_PLOT,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (plot_dialog),
RESPONSE_PLOT);
gtk_dialog_set_has_separator (GTK_DIALOG (plot_dialog), FALSE);
g_signal_connect (G_OBJECT (plot_dialog),
"destroy",
G_CALLBACK (gtk_widget_destroyed),
&plot_dialog);
g_signal_connect (G_OBJECT (plot_dialog),
"response",
G_CALLBACK (plot_dialog_response),
NULL);
insides = create_plot_dialog ();
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (plot_dialog)->vbox),
insides, TRUE, TRUE, 0);
gtk_widget_show_all (plot_dialog);
gtk_widget_grab_focus (plot_entries[0]);
}
static GelETree *
SurfacePlot_op (GelCtx *ctx, GelETree * * a, int *exception)
{
double x1, x2, y1, y2, z1, z2;
int i;
GelEFunc *func = NULL;
i = 0;
if (a[i] != NULL && a[i]->type != FUNCTION_NODE) {
gel_errorout (_("%s: argument not a function"), "SurfacePlot");
goto whack_copied_funcs;
}
func = d_copyfunc (a[i]->func.func);
func->context = -1;
i++;
if (a[i] != NULL && a[i]->type == FUNCTION_NODE) {
gel_errorout (_("%s: only one function supported"), "SurfacePlot");
goto whack_copied_funcs;
}
/* Defaults */
x1 = surf_defx1;
x2 = surf_defx2;
y1 = surf_defy1;
y2 = surf_defy2;
z1 = surf_defz1;
z2 = surf_defz2;
if (a[i] != NULL) {
if (a[i]->type == MATRIX_NODE) {
if ( ! get_limits_from_matrix_surf (a[i], &x1, &x2, &y1, &y2, &z1, &z2))
goto whack_copied_funcs;
i++;
} else {
GET_DOUBLE(x1, i, "SurfacePlot");
i++;
if (a[i] != NULL) {
GET_DOUBLE(x2, i, "SurfacePlot");
i++;
if (a[i] != NULL) {
GET_DOUBLE(y1, i, "SurfacePlot");
i++;
if (a[i] != NULL) {
GET_DOUBLE(y2, i, "SurfacePlot");
i++;
if (a[i] != NULL) {
GET_DOUBLE(z1, i, "SurfacePlot");
i++;
if (a[i] != NULL) {
GET_DOUBLE(z2, i, "SurfacePlot");
i++;
}
}
}
}
}
/* FIXME: what about errors */
if (error_num != 0) {
error_num = 0;
goto whack_copied_funcs;
}
}
}
if (x1 > x2) {
double s = x1;
x1 = x2;
x2 = s;
}
if (y1 > y2) {
double s = y1;
y1 = y2;
y2 = s;
}
if (z1 > z2) {
double s = z1;
z1 = z2;
z2 = s;
}
if (x1 == x2) {
gel_errorout (_("%s: invalid X range"), "SurfacePlot");
goto whack_copied_funcs;
}
if (y1 == y2) {
gel_errorout (_("%s: invalid Y range"), "SurfacePlot");
goto whack_copied_funcs;
}
if (z1 == z2) {
gel_errorout (_("%s: invalid Z range"), "SurfacePlot");
goto whack_copied_funcs;
}
if (surface_func != NULL) {
d_freefunc (surface_func);
}
g_free (surface_func_name);
surface_func_name = NULL;
surface_func = func;
func = NULL;
surfacex1 = x1;
surfacex2 = x2;
surfacey1 = y1;
surfacey2 = y2;
surfacez1 = z1;
surfacez2 = z2;
plot_mode = MODE_SURFACE;
plot_surface_functions ();
if (interrupted)
return NULL;
else
return gel_makenum_null ();
whack_copied_funcs:
if (func != NULL) {
d_freefunc (func);
func = NULL;
}
return NULL;
}
static GelETree *
LinePlot_op (GelCtx *ctx, GelETree * * a, int *exception)
{
double x1, x2, y1, y2;
int funcs = 0;
GelEFunc *func[MAXFUNC] = { NULL };
int i;
for (i = 0;
i < MAXFUNC && a[i] != NULL && a[i]->type == FUNCTION_NODE;
i++) {
func[funcs] = d_copyfunc (a[i]->func.func);
func[funcs]->context = -1;
funcs++;
}
if (a[i] != NULL && a[i]->type == FUNCTION_NODE) {
gel_errorout (_("%s: only up to 10 functions supported"), "LinePlot");
goto whack_copied_funcs;
}
if (funcs == 0) {
gel_errorout (_("%s: argument not a function"), "LinePlot");
goto whack_copied_funcs;
}
/* Defaults */
x1 = defx1;
x2 = defx2;
y1 = defy1;
y2 = defy2;
if (a[i] != NULL) {
if (a[i]->type == MATRIX_NODE) {
if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
goto whack_copied_funcs;
i++;
} else {
GET_DOUBLE(x1, i, "LinePlot");
i++;
if (a[i] != NULL) {
GET_DOUBLE(x2, i, "LinePlot");
i++;
if (a[i] != NULL) {
GET_DOUBLE(y1, i, "LinePlot");
i++;
if (a[i] != NULL) {
GET_DOUBLE(y2, i, "LinePlot");
i++;
}
}
}
/* FIXME: what about errors */
if (error_num != 0) {
error_num = 0;
goto whack_copied_funcs;
}
}
}
if (x1 > x2) {
double s = x1;
x1 = x2;
x2 = s;
}
if (y1 > y2) {
double s = y1;
y1 = y2;
y2 = s;
}
if (x1 == x2) {
gel_errorout (_("%s: invalid X range"), "LinePlot");
goto whack_copied_funcs;
}
if (y1 == y2) {
gel_errorout (_("%s: invalid Y range"), "LinePlot");
goto whack_copied_funcs;
}
line_plot_clear_funcs ();
for (i = 0; i < MAXFUNC && func[i] != NULL; i++) {
plot_func[i] = func[i];
func[i] = NULL;
}
plotx1 = x1;
plotx2 = x2;
ploty1 = y1;
ploty2 = y2;
plot_mode = MODE_LINEPLOT;
plot_functions ();
if (interrupted)
return NULL;
else
return gel_makenum_null ();
whack_copied_funcs:
for (i = 0; i < MAXFUNC && func[i] != NULL; i++) {
d_freefunc (func[i]);
func[i] = NULL;
}
return NULL;
}
static GelETree *
LinePlotParametric_op (GelCtx *ctx, GelETree * * a, int *exception)
{
double x1, x2, y1, y2, t1, t2, tinc;
GelEFunc *funcx = NULL;
GelEFunc *funcy = NULL;
int i;
if (a[0] == NULL || a[1] == NULL ||
a[0]->type != FUNCTION_NODE ||
a[1]->type != FUNCTION_NODE) {
gel_errorout (_("%s: First two arguments must be functions"), "LinePlotParametric");
return NULL;
}
funcx = d_copyfunc (a[0]->func.func);
funcx->context = -1;
funcy = d_copyfunc (a[1]->func.func);
funcy->context = -1;
/* Defaults */
x1 = defx1;
x2 = defx2;
y1 = defy1;
y2 = defy2;
t1 = deft1;
t2 = deft2;
tinc = deftinc;
i = 2;
/* Get t limits */
if (a[i] != NULL) {
GET_DOUBLE(t1, i, "LinePlotParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(t2, i, "LinePlotParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(tinc, i, "LinePlotParametric");
i++;
}
}
/* FIXME: what about errors */
if (error_num != 0) {
error_num = 0;
goto whack_copied_funcs;
}
}
/* Get window limits */
if (a[i] != NULL) {
if (a[i]->type == MATRIX_NODE) {
if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
goto whack_copied_funcs;
i++;
} else {
GET_DOUBLE(x1, i, "LinePlotParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(x2, i, "LinePlotParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(y1, i, "LinePlotParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(y2, i, "LinePlotParametric");
i++;
}
}
}
/* FIXME: what about errors */
if (error_num != 0) {
error_num = 0;
goto whack_copied_funcs;
}
}
}
if (x1 > x2) {
double s = x1;
x1 = x2;
x2 = s;
}
if (y1 > y2) {
double s = y1;
y1 = y2;
y2 = s;
}
if (x1 == x2) {
gel_errorout (_("%s: invalid X range"), "LinePlotParametric");
goto whack_copied_funcs;
}
if (y1 == y2) {
gel_errorout (_("%s: invalid Y range"), "LinePlotParametric");
goto whack_copied_funcs;
}
if (t1 >= t2 || tinc <= 0) {
gel_errorout (_("%s: invalid T range"), "LinePlotParametric");
goto whack_copied_funcs;
}
line_plot_clear_funcs ();
parametric_func_x = funcx;
parametric_func_y = funcy;
plotx1 = x1;
plotx2 = x2;
ploty1 = y1;
ploty2 = y2;
plott1 = t1;
plott2 = t2;
plottinc = tinc;
plot_mode = MODE_LINEPLOT_PARAMETRIC;
plot_functions ();
if (interrupted)
return NULL;
else
return gel_makenum_null ();
whack_copied_funcs:
d_freefunc (funcx);
funcx = NULL;
d_freefunc (funcy);
funcy = NULL;
return NULL;
}
static GelETree *
LinePlotCParametric_op (GelCtx *ctx, GelETree * * a, int *exception)
{
double x1, x2, y1, y2, t1, t2, tinc;
GelEFunc *func = NULL;
int i;
if (a[0] == NULL ||
a[0]->type != FUNCTION_NODE) {
gel_errorout (_("%s: First argument must be functions"), "LinePlotCParametric");
return NULL;
}
func = d_copyfunc (a[0]->func.func);
func->context = -1;
/* Defaults */
x1 = defx1;
x2 = defx2;
y1 = defy1;
y2 = defy2;
t1 = deft1;
t2 = deft2;
tinc = deftinc;
i = 1;
/* Get t limits */
if (a[i] != NULL) {
GET_DOUBLE(t1, i, "LinePlotCParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(t2, i, "LinePlotCParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(tinc, i, "LinePlotCParametric");
i++;
}
}
/* FIXME: what about errors */
if (error_num != 0) {
error_num = 0;
goto whack_copied_funcs;
}
}
/* Get window limits */
if (a[i] != NULL) {
if (a[i]->type == MATRIX_NODE) {
if ( ! get_limits_from_matrix (a[i], &x1, &x2, &y1, &y2))
goto whack_copied_funcs;
i++;
} else {
GET_DOUBLE(x1, i, "LinePlotCParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(x2, i, "LinePlotCParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(y1, i, "LinePlotCParametric");
i++;
if (a[i] != NULL) {
GET_DOUBLE(y2, i, "LinePlotCParametric");
i++;
}
}
}
/* FIXME: what about errors */
if (error_num != 0) {
error_num = 0;
goto whack_copied_funcs;
}
}
}
if (x1 > x2) {
double s = x1;
x1 = x2;
x2 = s;
}
if (y1 > y2) {
double s = y1;
y1 = y2;
y2 = s;
}
if (x1 == x2) {
gel_errorout (_("%s: invalid X range"), "LinePlotCParametric");
goto whack_copied_funcs;
}
if (y1 == y2) {
gel_errorout (_("%s: invalid Y range"), "LinePlotCParametric");
goto whack_copied_funcs;
}
if (t1 >= t2 || tinc <= 0) {
gel_errorout (_("%s: invalid T range"), "LinePlotCParametric");
goto whack_copied_funcs;
}
line_plot_clear_funcs ();
parametric_func_z = func;
plotx1 = x1;
plotx2 = x2;
ploty1 = y1;
ploty2 = y2;
plott1 = t1;
plott2 = t2;
plottinc = tinc;
plot_mode = MODE_LINEPLOT_PARAMETRIC;
plot_functions ();
if (interrupted)
return NULL;
else
return gel_makenum_null ();
whack_copied_funcs:
d_freefunc (func);
func = NULL;
return NULL;
}
static GelETree *
LinePlotClear_op (GelCtx *ctx, GelETree * * a, int *exception)
{
line_plot_clear_funcs ();
/* This will just clear the window */
plot_mode = MODE_LINEPLOT;
plot_functions ();
if (interrupted)
return NULL;
else
return gel_makenum_null ();
}
static gboolean
get_line_numbers (GelETree *a, double **x, double **y, int *len)
{
int i;
GelMatrixW *m;
g_return_val_if_fail (a->type == MATRIX_NODE, FALSE);
m = a->mat.matrix;
if ( ! gel_is_matrix_value_only_real (m)) {
gel_errorout (_("%s: Line should be given as a real, n by 2 matrix "
"with columns for x and y, n>=2"),
"LinePlotDrawLine");
return FALSE;
}
if (gel_matrixw_width (m) == 2 &&
gel_matrixw_height (m) >= 2) {
*len = gel_matrixw_height (m);
*x = g_new (double, *len);
*y = g_new (double, *len);
for (i = 0; i < *len; i++) {
GelETree *t = gel_matrixw_index (m, 0, i);
(*x)[i] = mpw_get_double (t->val.value);
t = gel_matrixw_index (m, 1, i);
(*y)[i] = mpw_get_double (t->val.value);
}
} else if (gel_matrixw_width (m) == 1 &&
gel_matrixw_height (m) % 2 == 0 &&
gel_matrixw_height (m) >= 4) {
*len = gel_matrixw_height (m) / 2;
*x = g_new (double, *len);
*y = g_new (double, *len);
for (i = 0; i < *len; i++) {
GelETree *t = gel_matrixw_index (m, 0, 2*i);
(*x)[i] = mpw_get_double (t->val.value);
t = gel_matrixw_index (m, 0, (2*i) + 1);
(*y)[i] = mpw_get_double (t->val.value);
}
} else if (gel_matrixw_height (m) == 1 &&
gel_matrixw_width (m) % 2 == 0 &&
gel_matrixw_width (m) >= 4) {
*len = gel_matrixw_width (m) / 2;
*x = g_new (double, *len);
*y = g_new (double, *len);
for (i = 0; i < *len; i++) {
GelETree *t = gel_matrixw_index (m, 2*i, 0);
(*x)[i] = mpw_get_double (t->val.value);
t = gel_matrixw_index (m, (2*i) + 1, 0);
(*y)[i] = mpw_get_double (t->val.value);
}
} else {
gel_errorout (_("%s: Line should be given as a real, n by 2 matrix "
"with columns for x and y, n>=2"),
"LinePlotDrawLine");
return FALSE;
}
return TRUE;
}
static GelETree *
LinePlotDrawLine_op (GelCtx *ctx, GelETree * * a, int *exception)
{
int len;
int nextarg;
double *x, *y, *dx, *dy;
GdkColor color;
int thickness;
GtkPlotData *data;
int i;
ensure_window ();
if (plot_mode != MODE_LINEPLOT &&
plot_mode != MODE_LINEPLOT_PARAMETRIC) {
plot_mode = MODE_LINEPLOT;
clear_graph ();
}
if (line_plot == NULL) {
add_line_plot ();
plot_setup_axis ();
}
if (a[0]->type == MATRIX_NODE) {
if ( ! get_line_numbers (a[0], &x, &y, &len))
return FALSE;
nextarg = 1;
} else {
double x1, y1, x2, y2;
if G_UNLIKELY (gel_count_arguments (a) < 4) {
gel_errorout (_("%s: Wrong number of arguments"),
"LinePlotDrawLine");
return NULL;
}
GET_DOUBLE(x1, 0, "LinePlotDrawLine");
GET_DOUBLE(y1, 1, "LinePlotDrawLine");
GET_DOUBLE(x2, 2, "LinePlotDrawLine");
GET_DOUBLE(y2, 3, "LinePlotDrawLine");
len = 2;
x = g_new (double, 2);
x[0] = x1;
x[1] = x2;
y = g_new (double, 2);
y[0] = y1;
y[1] = y2;
nextarg = 4;
}
gdk_color_parse ("black", &color);
thickness = 2;
for (i = nextarg; a[i] != NULL; i++) {
if G_LIKELY (a[i]->type == STRING_NODE ||
a[i]->type == IDENTIFIER_NODE) {
GelToken *id;
static GelToken *colorid = NULL;
static GelToken *thicknessid = NULL;
if (colorid == NULL) {
colorid = d_intern ("color");
thicknessid = d_intern ("thickness");
}
if (a[i]->type == STRING_NODE)
id = d_intern (a[i]->str.str);
else
id = a[i]->id.id;
if (id == colorid) {
if G_UNLIKELY (a[i+1] == NULL) {
gel_errorout (_("%s: No color specified"),
"LinePlotDrawLine");
g_free (x);
g_free (y);
return NULL;
}
/* FIXME: helper routine for getting color */
if (a[i+1]->type == STRING_NODE) {
gdk_color_parse (a[i+1]->str.str, &color);
} else if (a[i+1]->type == IDENTIFIER_NODE) {
gdk_color_parse (a[i+1]->id.id->token, &color);
} else {
gel_errorout (_("%s: Color must be a string"),
"LinePlotDrawLine");
g_free (x);
g_free (y);
return NULL;
}
i++;
} else if (id == thicknessid) {
if G_UNLIKELY (a[i+1] == NULL) {
gel_errorout (_("%s: No thicnkess specified"),
"LinePlotDrawLine");
g_free (x);
g_free (y);
return NULL;
}
if G_UNLIKELY ( ! check_argument_positive_integer (a, i+1,
"LinePlotDrawLine")) {
g_free (x);
g_free (y);
return NULL;
}
thickness = gel_get_nonnegative_integer (a[i+1]->val.value,
"LinePlotDrawLine");
i++;
} else {
gel_errorout (_("%s: Unknown style"), "LinePlotDrawLine");
g_free (x);
g_free (y);
return NULL;
}
} else {
gel_errorout (_("%s: Bad parameter"), "LinePlotDrawLine");
g_free (x);
g_free (y);
return NULL;
}
}
data = GTK_PLOT_DATA (gtk_plot_data_new ());
dx = g_new0 (double, len);
dy = g_new0 (double, len);
gtk_plot_data_set_points (data, x, y, dx, dy, len);
gtk_plot_add_data (GTK_PLOT (line_plot), data);
gtk_plot_data_hide_legend (data);
gdk_color_alloc (gdk_colormap_get_system (), &color);
gtk_plot_data_set_line_attributes (data,
GTK_PLOT_LINE_SOLID,
0, 0, thickness, &color);
gtk_widget_show (GTK_WIDGET (data));
gtk_plot_canvas_paint (GTK_PLOT_CANVAS (plot_canvas));
gtk_plot_canvas_refresh (GTK_PLOT_CANVAS (plot_canvas));
return gel_makenum_null ();
}
static GelETree *
set_LinePlotWindow (GelETree * a)
{
double x1, x2, y1, y2;
if ( ! get_limits_from_matrix (a, &x1, &x2, &y1, &y2))
return NULL;
plotx1 = defx1 = x1;
plotx2 = defx2 = x2;
ploty1 = defy1 = y1;
ploty2 = defy2 = y2;
return make_matrix_from_limits ();
}
static GelETree *
get_LinePlotWindow (void)
{
return make_matrix_from_limits ();
}
static GelETree *
set_SurfacePlotWindow (GelETree * a)
{
double x1, x2, y1, y2, z1, z2;
if ( ! get_limits_from_matrix_surf (a, &x1, &x2, &y1, &y2, &z1, &z2))
return NULL;
surf_defx1 = x1;
surf_defx2 = x2;
surf_defy1 = y1;
surf_defy2 = y2;
surf_defz1 = z1;
surf_defz2 = z2;
return make_matrix_from_limits_surf ();
}
static GelETree *
get_SurfacePlotWindow (void)
{
return make_matrix_from_limits_surf ();
}
void
gel_add_graph_functions (void)
{
GelEFunc *f;
GelToken *id;
new_category ("plotting", N_("Plotting"), TRUE /* internal */);
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"));
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"));
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"));
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"));
FUNC (LinePlotClear, 0, "", "plotting", N_("Show the line plot window and clear out functions"));
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"));
PARAMETER (LinePlotWindow, N_("Line plotting window (limits) as a 4-vector of the form [x1,x2,y1,y2]"));
PARAMETER (SurfacePlotWindow, N_("Surface plotting window (limits) as a 6-vector of the form [x1,x2,y1,y2,z1,z2]"));
}