/* Implementation of psplot.h */ /* Copyright © 1995 UNICAMP. See notice at the end of this file. */ /* Last edited on 2007-12-26 13:27:32 by stolfi */ #include #include #include #include #include #include #include #include #include /* ERROR MESSAGES */ #define psplot_error(msg) \ psplot_do_error(__FILE__, __LINE__, msg) /* USEFUL CONSTANTS */ #define MM_PER_POINT (25.4/72.0) /* ENCAPSULATED FIGURES */ PSPLOT *psplot_new_figure(FILE *file, double x_size, double y_size) { PSPLOT *p = psplot_new_descriptor(); if (file == NULL) { psplot_error("null file"); } p->file = file; p->epsformat = 1; p->x_full_mm = x_size; p->y_full_mm = y_size; pswrite_eps_header(p->file, 0.0, x_size, 0.0, y_size, p->date); psplot_do_begin_context(p, 0.0, x_size, 0.0, y_size, 1); return p; } /* MULTIPAGE DOCUMENTS */ PSPLOT *psplot_new_document( FILE *file, char *title, double x_page_size, double y_page_size ) { PSPLOT *p = psplot_new_descriptor(); if (file == NULL) { psplot_error("null file"); } if (x_page_size == 0.0) { x_page_size = letter_x_size; } if (y_page_size == 0.0) { y_page_size = letter_y_size; } p->file = file; p->epsformat = 0; p->x_full_mm = x_page_size; p->y_full_mm = y_page_size; pswrite_ps_header(p->file, x_page_size, y_page_size, p->date); p->page_started = 0; p->n_pages = 0; return p; } PSPLOT *psplot_new_descriptor(void) /* Creates and intializes a new PSPLOT descriptor. */ { PSPLOT *p = (PSPLOT *)malloc(sizeof(PSPLOT)); if (p == NULL) { psplot_error("out of memory"); } p->date = today(); p->title = NULL; p->n_pages = 0; p->page_started = 0; p->page_state_defined = 0; p->g = NULL; return p; } void psplot_begin_page(PSPLOT *p, char *page_label) { if (p->epsformat) { psplot_error("illegal in eps file"); } if (p->page_started) { psplot_end_page(p); } p->n_pages++; psplot_do_begin_page(p, p->n_pages, page_label); pswrite_page_headline(p->file, p->n_pages, page_label, p->title); { char *foot_string = (p->n_pages == 1 ? p->date : NULL); pswrite_page_footline(p->file, foot_string); } } void psplot_end_page(PSPLOT *p) { if (p->epsformat) { psplot_error("illegal in eps file"); } psplot_do_end_page(p); } /* TERMINATION */ void psplot_close(PSPLOT *p) { if (p->epsformat) { psplot_do_end_page(p); pswrite_eps_trailer(p->file); } else { if (p->page_started) { psplot_end_page(p); } pswrite_ps_trailer(p->file, p->n_pages); } fclose(p->file); p->file = NULL; } /* CONTEXTS */ void psplot_begin_context(PSPLOT *p) { if (! p->page_started) { psplot_error("must call begin_page first"); } psplot_do_begin_context(p, ...); } void psplot_end_context(PSPLOT *p) { if (p->epsformat) { psplot_error("illegal in eps file"); } if (! p->page_started) { psplot_error("must call begin_page first"); } psplot_do_end_context(p); } /* INTERNAL OPERATIONS */ void psplot_do_begin_page(PSPLOT *p, int page_num, char *page_label) /* Starts a new page in an encapsulated figure or multipage document. Increments $n_pages$ and initializes the [p] fields to default values. Also writes out the "%%BeginPage" directive. */ { if (p->page_started) { psplot_error("begin_page called twice"); } assert(! p->page_state_defined); assert(! p->page_started); p->page_started = 1; p->n_pages++; pswrite_begin_page(p->file, page_num, page_label); /* Set default page state fields. */ psplot_set_text_color(p, &(psplot_Black)); psplot_set_font(p, "Courier", 8.0); psplot_set_line_color(p, &(psplot_Black)); psplot_set_area_color(p, &(psplot_MediumGray)); psplot_set_line_width(p, 0.0); psplot_set_line_solid(p); pswrite_round_join_and_caps(p->file); { double x_min_PS = 25.4; double x_max_PS = x_size - 25.4; double y_min_PS = 25.4; double y_max_PS = y_size - 25.4; psplot_do_begin_context(p, x_min_PS, x_max_PS, y_min_PS, y_max_PS, 1); } p->page_state_defined = 1; } void psplot_do_end_page(PSPLOT *p) /* Ends a page in an encapsulated figure or in a multipage document, undefines all [p] fields. */ { if (! p->page_started) { psplot_error("end_page without begin_page"); } assert(p_page_state_defined); while (p->g != NULL) { psplot_end_context(p); } if (! p->epsformat) { pswrite_showpage(p->file); } pswrite_end_page(p->file); p->page_state_defined = 0; p->page_started = 0; } void psplot_do_begin_context( PSPLOT *p, double x_min_PS, double x_max_PS, double y_min_PS, double y_max_PS ) /* Starts a new context in an encapsulated figure or multipage document. Increments $n_pages$ and initializes the [p] fields in an encapsulated figure or in a multipage document. Also writes out the "%%BeginPage" directive. Also prints the footer line with $page_label$ and $date$ (if not NULL). If the $page_label$ is NULL, it defaults to the $page_num$. */ { if (p->page_started != 1) { psplot_error("begin_page not called"); } if (p->drawing_started != 0) { psplot_error("begin_drawing called twice"); } assert(p->page_state_defined); assert(! p->drawing_state_defined); p->drawing_started = 1; pswrite_begin_context(p->file, x_min_PS, x_max_PS, y_min_PS, y_max_PS); p->x_min_PS = x_min_PS; p->x_max_PS = x_max_PS; p->y_min_PS = y_min_PS; p->y_max_PS = y_max_PS; /* Set default scales: */ psplot_set_scale(p, psplot_Axis_X, 0.0, 1.0); psplot_set_scale(p, psplot_Axis_Y, 0.0, 1.0); /* Place caption just below drawing area: */ if (! p->epsformat) { p->y_caption_PS = -1.0; } psplot_set_grid_size(1,1); p->drawing_state_defined = 1; } void psplot_do_end_context(PSPLOT *p) /* Closes the current drawing in an encapsulated figure or multipage document. */ { if (p->drawing_started != 1) { psplot_error("end_drawing without begin_drawing"); } pswrite_end_drawing(p->file); p->drawing_started = 0; p->drawing_state_defined = 0; } /* GENERAL OPERATIONS */ /* SCALES */ void psplot_set_scale(PSPLOT *p, psplot_Axis axis, double cmin, double cmax) { if (! p->page_started) { psplot_error("must call begin_page first"); } switch (axis) { case psplot_Axis_X: p->x_min = cmin; p->x_max = cmax; p->x_scale = (p->x_max_PS - p->x_min_PS)/(p->x_max - p->x_min); break; case psplot_Axis_Y: p->y_min = cmin; p->y_max = cmax; p->y_scale = (p->y_max_PS - p->y_min_PS)/(p->y_max - p->y_min); break; } } psplot_Range psplot_get_range(PSPLOT *p, psplot_Axis axis) { if (! p->page_started) { psplot_error("must call begin_page first"); } switch (axis) { case psplot_Axis_X: return (psplot_Range){p->x_min, p->x_max}; break; case psplot_Axis_Y: return (psplot_Range){p->y_min, p->y_max}; break; } } double psplot_get_scale_factor(PSPLOT *p, psplot_Axis axis) { if (! p->page_started) { psplot_error("must call begin_page first"); } switch (axis) { case psplot_Axis_X: return p->x_scale; break; case psplot_Axis_Y: return p->y_scale; break; } } /* COLORS */ psplot_Color Gray(float v) { return (psplot_Color){v, v, v}; } #define psplot_color_eq(ca,cb) \ ( (ca[0] != cb[0]) | \ (ca[1] != cb[1]) | \ (ca[2] != cb[2]) ) #define psplot_color_is_visible(c) \ ( (c[0] >= 0) | \ (c[1] >= 0) | \ (c[2] >= 0) ) void psplot_set_line_color(PSPLOT *p, psplot_Color *color) { if (! p->page_started) { psplot_error("must call begin_page first"); } { int changed = (! p->page_state_defined) || (! psplot_color_eq(color, p->draw_color); if (changed) { int visible = psplot_color_is_visible(color); if (visible) { pswrite_set_line_color(p->file, color[0], color[1], color[2]); } p->draw_color = color; p->do_draw = visible; } } } void psplot_set_area_color(PSPLOT *p, psplot_Color *color) { if (! p->page_started) { psplot_error("must call begin_page first"); } { int changed = (! p->page_state_defined) || (! psplot_color_eq(color, p->area_color); if (changed) { int visible = psplot_color_is_visible(color); if (visible) { pswrite_set_area_color(p->file, color[0], color[1], color[2]); } p->area_color = color; p->do_area = visible; } } } void psplot_set_text_color(PSPLOT *p, psplot_Color *color) { if (! p->page_started) { psplot_error("must call begin_page first"); } { int changed = (! p->page_state_defined) || (! psplot_color_eq(color, p->text_color); if (changed) { int visible = psplot_color_is_visible(color); if (visible) { pswrite_set_text_color(p->file, color[0], color[1], color[2]); } p->text_color = color; p->do_text = visible; } } } /* LINE DRAWINGS */ void psplot_set_line_width(PSPLOT *p, float width) { if (! p->page_started) { psplot_error("must call begin_page first"); } if ((! p->page_state_defined) || (width != p->line_width)) { pswrite_set_line_width(p->file, width); p->line_width = width } } void psplot_set_line_solid(PSPLOT *p) { if (! p->page_started) { psplot_error("must call begin_page first"); } if ((! p->page_state_defined) || (! p->line_solid)) { pswrite_set_dash_pattern(p->file, NULL, 0, 0.0); p->line_solid = 1; p->dash_pattern = NULL; } } void psplot_set_line_dashed(PSPLOT *p, float *pattern, int n, float skip) { if (! p->page_started) { psplot_error("must call begin_page first"); } if (psplot_dash_pattern_is_solid(pattern, n)) { psplot_set_line_solid(p); } else if ( (! p->page_state-defined) || (p->line_solid) || (! psplot_same_dash_pattern( p->dash_pattern, p->dash_pattern_n, p->dash_skip, pattern, n, skip ) ) { pswrite_set_line_dash_pattern(p->file, pattern, n, skip); p->dash_pattern = pattern; p->dash_pattern_n = n; p->dash_skip = skip; p->line_solid = 0; } } } void psplot_segment(PSPLOT *p, double xa, double ya, double xb, double yb) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw) { double psxa = p->x_min_PS + p->x_scale * (xa - p->x_min); double psya = p->y_min_PS + p->y_scale * (ya - p->y_min); double psxb = p->x_min_PS + p->x_scale * (xb - p->x_min); double psyb = p->y_min_PS + p->y_scale * (yb - p->y_min); pswrite_segment(p->file, psxa, psya, psxb, psyb); fflush(psfile); } } void psplot_curve( PSPLOT *p, double xa, double ya, double xb, double yb, double xc, double yc, double xd, double yd ) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw) { double psxa = p->x_min_PS + p->x_scale * (xa - p->x_min); double psya = p->y_min_PS + p->y_scale * (ya - p->y_min); double psxb = p->x_min_PS + p->x_scale * (xb - p->x_min); double psyb = p->y_min_PS + p->y_scale * (yb - p->y_min); double psxc = p->x_min_PS + p->x_scale * (xc - p->x_min); double psyc = p->y_min_PS + p->y_scale * (yc - p->y_min); double psxd = p->x_min_PS + p->x_scale * (xd - p->x_min); double psyd = p->y_min_PS + p->y_scale * (yd - p->y_min); pswrite_curve(p->file, psxa, psya, psxb, psyb, psxc, psyc, psxd, psyd); fflush(psfile); } } void psplot_coord_line(PSPLOT *p, psplot_Axis axis, double coord) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw) { double pscoord; switch (axis) { case psplot_Axis_X: pscoord = p-> x_min_PS + p->x_scale * (coord - p->x_min); pswrite_coord_line(p->file, pswrite_axis_X, pscoord); break; case psplot_Axis_Y: pscoord = p-> y_min_PS + p->y_scale * (coord - p->y_min); pswrite_coord_line(p->file, pswrite_axis_Y, pscoord); break; } } } void psplot_solid_dot(PSPLOT *p, double xc, double yc, float relsize) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw) { double psxc = p-> x_min_PS + p->x_scale * (xc - p->x_min); double psyc = p-> y_min_PS + p->y_scale * (yc - p->y_min); pswrite_solid_dot(p->file, psxc, psyc, relsize); } } void psplot_filled_dot(PSPLOT *p, double xc, double yc, float relsize) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw || p->do_area) { double psxc = p-> x_min_PS + p->x_scale * (xc - p->x_min); double psyc = p-> y_min_PS + p->y_scale * (yc - p->y_min); pswrite_filled_dot(p->file, psxc, psyc, relsize) } } void psplot_arrowhead( PSPLOT *p, double xa, double ya, double xb, double yb, float width, float length, double fraction ) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw) { double psxa = p->x_scale * (xa - p->x_min); double psya = p->y_scale * (ya - p->y_min); double psxb = p->x_scale * (xb - p->x_min); double psyb = p->y_scale * (yb - p->y_min); double dx = psxb - psxa; double dy = psyb - psya; double d = Math.sqrt(dx*dx + dy*dy); double noitcarf = 1.0 - fraction; double psxt = noitcarf* psxa + fraction*psxb; double psyt = noitcarf* psya + fraction*psyb; double psw = FLOAT(width, LONG)/2.0/d; double psh = FLOAT(length, LONG)/d; double psxu = - psh * dx + psw * dy; double psyu = - psh * dy - psw * dx; double psxv = - psh * dx - psw * dy; double psyv = - psh * dy + psw * dx double psxc = p-> x_min_PS + p->x_scale * (xc - p->x_min); double psyc = p-> y_min_PS + p->y_scale * (yc - p->y_min); pswrite_arrowhead(p->file, psxt, psyt, psxu, psyu, psxv, psyv) } } void psplot_frame(PSPLOT *p) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw) { pswrite_frame(p->file); } } /* AREA PAINTING */ void psplot_rectangle(PSPLOT *p, double xlo, double xhi, double ylo, double yhi) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw || p->do_area) { double psxlo = p->x_min_PS + p->x_scale * (xlo - p->x_min); double psxhi = p->x_min_PS + p->x_scale * (xhi - p->x_min); double psylo = p->y_min_PS + p->y_scale * (ylo - p->y_min); double psyhi = p->y_min_PS + p->y_scale * (yhi - p->y_min); pswrite_rectangle(p->file, psxlo, psxhi, psylo, psyhi, p->do_area, p->do_draw); fflush(psfile); } } void psplot_triangle( PSPLOT *p, double xa, double ya, double xb, double yb, double xc, double yc ) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw || p->do_area) { double psxa = p->x_min_PS + p->x_scale * (xa - p->x_min); double psya = p->y_min_PS + p->y_scale * (ya - p->y_min); double psxb = p->x_min_PS + p->x_scale * (xb - p->x_min); double psyb = p->y_min_PS + p->y_scale * (yb - p->y_min); double psxc = p->x_min_PS + p->x_scale * (xc - p->x_min); double psyc = p->y_min_PS + p->y_scale * (yc - p->y_min); pswrite_triangle(p->file, psxa, psya, psxb, psyb, psxc, psyc, p->area, p->draw); fflush(psfile); } } void psplot_circle(PSPLOT *p, double xc, double yc, double radius) { psplot_ellipse(p, xc, yc, radius, 0.0, 0.0, radius); } void psplot_ellipse( PSPLOT *p, double xc, double yc, double xa, double ya, double xb, double yb ) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw || p->do_area) { double psxc = p->x_min_PS + p->x_scale * (xc - p->x_min); double psyc = p->y_min_PS + p->y_scale * (yc - p->y_min); double psxa = p->x_scale * xa; double psya = p->y_scale * ya; double psxb = p->x_scale * xb; double psyb = p->y_scale * yb; pswrite_ellipse(p->file, psxc, psyc, psxa, psya, psxb, psyb, p->area, p->draw); fflush(psfile); } } void plot_lune( PSPLOT *p, double xa, double ya, double ra, double xb, double yb, double rb ) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->x_scale != p->y_scale) { psplot_error("X and Y scales are not equal"); } else if (p->do_draw || p->do_area) { double psxa = p->x_scale * (xa - p->x_min); double psya = p->y_scale * (ya - p->y_min); double psra = p->x_scale * ra; double psxb = p->x_scale * (xb - p->x_min); double psyb = p->y_scale * (yb - p->y_min); double psrb = p->x_scale * rb; pswrite_lune(psfile, psxa, psya, psra, psxb, psyb, psrb, p->area, p->draw); } } void psplot_slice( PSPLOT *p, double xc, double yc, double radius, double start, double stop ) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->x_scale != ps_y_scale) { psplot_error("X and Y scales are not equal"); } else if (p->do_draw || p->do_area) { double psxc = p->x_min_PS + p->x_scale * (xc - p->x_min); double psyc = p->y_min_PS + p->y_scale * (yc - p->y_min); double psr = p->x_scale * radius; pswrite_slice(p->file, psxc, psyc, psr, start, stop, p->area, p->draw); fflush(psfile); } } void psplot_polygon( PSPLOT *p, double x[], double y[], int n ) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw || p->do_area) { int i; if (npoints>6) { fprintf(psfile, "\n"); } pswrite_begin_polygon(p->file); for (i=0; ix_min_PS + p->x_scale * (x[i] - p->x_min); double psyi = p->y_min_PS + p->y_scale * (y[i] - p->y_min); if ((i % 6) == 0) { if (i>0) { fprintf(psfile, "\n"); } } else { fprintf(psfile, " "); } pswrite_polygon_vertex(p->file, psxi, psyi); } if (npoints>6) { fprintf(psfile, "\n"); } pswrite_end_polygon(p->file, n, p->do_draw, p->do_area); } } /* GRID DRAWING/PAINTING */ void psplot_set_grid_size(PSPLOT *p, int xn, int yn) { if (! p->page_started) { psplot_error("must call begin_page first"); } else { if ((xn <= 0) || (yn <= 0)) { psplot_error("bad grid cell count"); } pswrite_set_grid_size(p->file, xn, yn); } } void psplot_grid_cell(PSPLOT *p, int xi, int yi) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw || p->do_area) { pswrite_grid_cell(p->file, xi, yi, p->area, p->draw); } } void psplot_grid_lines(PSPLOT *p) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_draw) { pswrite_grid_lines(p->file); } } /* TEXT */ void psplot_label( PSPLOT *p, char *txt, double x, double y, float xAlign, float yAlign, float angle, char *font, float size ) { if (! p->page_started) { psplot_error("must call begin_page first"); } else if (p->do_text) { double psx = p->x_min_PS + p->x_scale * (x - p->x_min); double psy = p->y_min_PS + p->y_scale * (y - p->y_min); pswrite_label(p->file, txt, psx, psy, x_align, y_align, angle) pswrite_set_font(p->file, font, size); psplot_put_text(psfile, text, "\\267"); } } /* MISCELLANEOUS */ void psplot_comment(PSPLOT *p, txt: TEXT) { pswrite_comment(p->file, txt); } /* INTERNAL OPERATIONS */ int psplot_same_dash_pattern( float *pata, int na, float skipa, float *patb, int nb, float skipb ) /* True iff the two line dash patterns are the same. */ { if ((pata == NULL) == (patb == NULL)) { return 1; } else if ((na == 0) == (nb == 0)) { return 1; } else if ((skipa != skipb) || (na != nb) || ((pata == NULL) != (patb == NULL))) { return 0; } else { for (i = 0; i < na; i++) { if (pata[i] != patb[i]) { return 0; } } return 1; } } int psplot_dash_pattern_is_solid(float pattern[ ], int n) { double sum = 0.0; int i = 1; if (n == 0) { return 1; } if (n == 1) { return 0; } do { sum = sum + (double)(pattern[i]); i = (i + 2) % n; } while(i != 1) return (sum == 0.0) } void psplot_do_set_font(FILE *f, char *font, float size) { if ((! strcmp(font, p->font_name)) || (size != p->font_size)) { pswrite_set_font(p->file, font, size); p->font_name = font; p->font_size = size; } } /****************************************************************************/ /* */ /* Copyright © 1995 Universidade Estadual de Campinas (UNICAMP) */ /* */ /* Authors: */ /* Jorge Stolfi - CS Dept, UNICAMP */ /* */ /* This file can be freely used, distributed, and modified, provided that */ /* this copyright and authorship notice is included in every copy or */ /* derived version. */ /* */ /* DISCLAIMER: This software is offered ``as is'', without any guarantee */ /* as to fitness for any particular purpose. Neither the copyright */ /* holder nor the authors or their employers can be held responsible for */ /* any damages that may result from its use. */ /* */ /****************************************************************************/