blob: 1aad9796d7d9e0964757be29e8dfad04e73e5d09 [file] [log] [blame]
#ifndef GRAPHICS_H
#define GRAPHICS_H
#include <iostream>
#include <string>
#include "easygl_constants.h"
#include <cstdint>
// Set X11 by default, if neither NO_GRAPHICS nor WIN32 are defined
#ifndef NO_GRAPHICS
#ifndef WIN32
#ifndef X11
#define X11
#endif
#endif // !WIN32
#endif // !NO_GRAPHICS
/* Graphics.h
* Originally written by Vaughn Betz (vaughn@eecg.utoronto.ca)
* Win32 port by Paul Leventis (leventi@eecg.utoronto.ca)
* Enhanced version by William Chow (chow@eecg.utoronto.ca)
* Minor updates by Guy Lemieux (lemieux@ece.ubc.ca)
* More updates by Vaughn Betz to make win32 cleaner and more robust.
* More updates and code cleanup by Long Yu Wang (longyu.wang@mail.utoronto.ca)
* More updates and c++ integration - Matthew J.P. Walker (matthewjp.walker@mail.utoronto.ca)
*/
/******* Constants and enums ******************************************/
/* Data structure below is for debugging. Lets you get a bunch
* of info about the low-level graphics state.
* xmult, ymult: world to pixel coordinate multiplier for screen
* ps_xmult, ps_ymult: world to pixel coordinate multiplier for postscript
* xleft, xright, ytop, yleft: current world coordinates of user-graphics display corners
* top_width, top_height: size (in pixels) of top-level window
*/
typedef struct {
float xmult, ymult;
float ps_xmult, ps_ymult;
float xleft, xright, ytop, ybot;
int top_width, top_height;
} t_report;
/**
* A point datatype.
*/
struct t_point {
float x;
float y;
void set(float x, float y);
void set(const t_point& src);
/**
* Behaves like a 2 argument plusequals.
*/
void offset(float x, float y);
/**
* These add the given point to this point in a
* componentwise fashion, ie x = x + rhs.x
*
* Naturally, {+,-} don't modify and {+,-}= do.
*/
t_point operator+ (const t_point& rhs) const;
t_point operator- (const t_point& rhs) const;
t_point operator* (float rhs) const;
t_point& operator+= (const t_point& rhs);
t_point& operator-= (const t_point& rhs);
t_point& operator*= (float rhs);
/**
* Assign that point to this one - copy the components
*/
t_point& operator= (const t_point& src);
t_point();
t_point(const t_point& src);
t_point(float x, float y);
};
t_point operator*(float lhs, const t_point& rhs);
/**
* Represents a rectangle, used as a bounding box.
*/
class t_bound_box {
public:
/**
* These return their respective edge/point's location
*/
const float& left() const;
const float& right() const;
const float& bottom() const;
const float& top() const;
float& left();
float& right();
float& bottom();
float& top();
const t_point& bottom_left() const;
const t_point& top_right() const;
t_point& bottom_left();
t_point& top_right();
/**
* Calculate and return the center
*/
float get_xcenter() const;
float get_ycenter() const;
t_point get_center() const;
/**
* Calculate and return the width/height
* ie. right/top - left/bottom respectively.
*/
float get_width() const;
float get_height() const;
/**
* These behave like the plusequal operator
* They add their x and y values to all corners
*/
void offset(const t_point& make_relative_to);
void offset(float by_x, float by_y);
/**
* Does the given point coinside with this bbox?
* Points on the edges or corners are included.
*/
bool intersects(const t_point& test_pt) const;
bool intersects(float x, float y) const;
/**
* Calculate and return the area of this rectangle.
*/
float area() const;
/**
* These add the given point to this bbox - they
* offset each corner by this point. Usful for calculating
* the location of a box in a higher scope, or for moving
* it around as part of a calculation
*
* Naturally, the {+,-} don't modify and the {+,-}= do.
*/
t_bound_box operator+ (const t_point& rhs) const;
t_bound_box operator- (const t_point& rhs) const;
t_bound_box& operator+= (const t_point& rhs);
t_bound_box& operator-= (const t_point& rhs);
/**
* Assign that box to this one - copy it's left, right, bottom, and top.
*/
t_bound_box& operator= (const t_bound_box& src);
t_bound_box();
t_bound_box(const t_bound_box& src);
t_bound_box(float left, float bottom, float right, float top);
t_bound_box(const t_point& bottomleft, const t_point& topright);
t_bound_box(const t_point& bottomleft, float width, float height);
private:
t_point bottomleft;
t_point topright;
};
/**
* A datatype that holds an RGB triplet, used in this
* graphics library for specifying and holding colours.
*/
struct t_color {
uint_fast8_t red;
uint_fast8_t green;
uint_fast8_t blue;
t_color(uint_fast8_t red, uint_fast8_t green, uint_fast8_t blue);
t_color(const t_color& src);
t_color();
bool operator== (const t_color& rhs) const;
bool operator!= (const t_color& rhs) const;
unsigned long as_rgb_int() const;
/*
* Some useful functions for working with indexed colour,
* but not much else.
*/
t_color(color_types src);
color_types operator=(color_types color_enum);
bool operator== (color_types rhs) const;
bool operator!= (color_types rhs) const;
// bool operator> (color_types rhs) const;
// bool operator< (color_types rhs) const;
};
/************** ESSENTIAL FUNCTIONS ******************/
/* This is the main routine for the graphics. When event_loop is
* called, it will continue executing until the Proceed button is
* pressed.
* Whenever the graphics need to be redrawn, drawscreen will be called;
* you must pass in a function pointer to a routine you write that can
* draw the picture you want.
* You can also pass in event handlers for user input if you wish.
* act_on_mouse_button() will be called whenever the user left-clicks
* in the graphics area.
* act_on_keypress() and act_on_mousemove() will be called whenever a
* keyboard key is pressed or the mouse is moved, respectively, in the
* graphics area. You can turn keypress input and mouse_move input
* on or off using the set_mouse_move_input () and set_keypress_input ()
* functions (default for both: off).
*/
void event_loop (void (*act_on_mousebutton) (float x, float y, t_event_buttonPressed button_info),
void (*act_on_mousemove) (float x, float y),
void (*act_on_keypress) (char key_pressed),
void (*drawscreen) (void));
/* Opens up the graphics; the window will have the UTF-8 string window_name
* in its title bar and have the specified background colour.
* Known bug: can't re-open graphics after closing them.
*/
void init_graphics (const char *window_name, int cindex_background);
void init_graphics (const char *window_name, const t_color& background);
/* Sets world coordinates of the graphics window so that the
* lower-left corner has world coordinate (xl, yb) and the
* top-right corner has world coordinate (xr, yt).
* Call this function before you call event_loop. Do not call it
* in your drawscreen () callback function, since it will undo any
* panning or zooming the user has done.
*/
void init_world(const t_bound_box& bounds);
void init_world (float xl, float yb, float xr, float yt);
/* Closes the graphics */
void close_graphics (void);
/* Changes the status bar message to the UTF-8 string msg. */
void update_message (const char *msg);
/* Destroys the button with the given text; i.e. removes it from
* the display.
*/
void destroy_button (const char *button_text);
/*************** PostScript Routines *****************/
/* Opens file for postscript commands and initializes it. All subsequent
* drawing commands go to this file until close_postscript is called.
* You can generate postscript output by explicitly calling
* this routine, and then calling drawscreen. More commonly you'll
* just click on the "PostScript" button though, and that button
* calls this routine and drawscreen to generate a postscript file
* that exactly matches the graphics area display on the screen.
*
* Warning: not all UTF-8 filenames will work on Windows (this uses fopen)
*/
int init_postscript (const char *fname); /* Returns 1 if successful */
/* Closes file and directs output to screen again. */
void close_postscript (void);
/*************** DRAWING ROUTINES ******************/
/****
* The following routines draw to either the window
* or postscript file, whichever one is active.
****/
/* Clears the screen. Should normally be the first call in your
* screen redrawing function.
*/
void clearscreen (void);
/**
* Set the current draw colour to the supplied index colour from color_types,
* the specified t_colour, or rgb triplet. Affects all drawing functions, including text.
*/
void setcolor (int cindex);
void setcolor (const t_color& new_color);
void setcolor (uint_fast8_t r, uint_fast8_t g, uint_fast8_t b);
/**
* Set the color with a string instead of an enumerated constant.
* Colour names should be the same as their enum name, but all lower
* case, and still no spaces.
*/
void setcolor_by_name (std::string cname);
/* Get the current color */
t_color getcolor(void);
/* Sets the line style to the specified line_style */
void setlinestyle (int linestyle);
/* Sets the line width in pixels (for screen output) or points (1/72 of an inch)
* for PostScript output. A value of 0 means thinnest possible line.
*/
void setlinewidth (int linewidth);
/**
* Sets/gets the font size, in points. 72 points is 1 inch high.
*
* Having performance problems with lots of different text attribute combinations?
* See comments about the font cache in settextattrs(..)
*/
void setfontsize (int pointsize);
int getfontsize();
/*
* Set/get the rotation of text to be drawn. I recommend setting rotation
* back to zero once you are done drawing all rotated text, as most
* text will not be rotated, and setting rotation there may have been omitted.
*
* Having performance problems with lots of different text attribute combinations?
* See comments about the font cache in settextattrs(..)
*/
void settextrotation (float degrees);
float gettextrotation();
/*
* Set both the point size and rotation of the text in one call.
* This should be more effecient then calling setfontsize() and settextrotation()
* sepearatly, if that makes sense for your program.
*
* Also, If you plan on having many (ie > 40) nonzero rotation-pointsize, or
* zero rotation-pointsize, combinations of text visible at the same time, I
* recommend you look into changing the size of the font cache. See comments
* near FONT_CACHE_SIZE_FOR_ROTATED and FONT_CACHE_SIZE_FOR_ZEROS in graphics.c
*/
void settextattrs (int pointsize, float degrees);
/* Draws a line from p1 to p2 or (x1, y1) to (x2, y2) */
void drawline (const t_point& p1, const t_point& p2);
void drawline (float x1, float y1, float x2, float y2);
/* Draws the rectangle rect or a rectangle from bottomleft to upperright or (x1, y1) to (x2, y2)
* using the current line style, colour and width.
*/
void drawrect (const t_bound_box& rect);
void drawrect (const t_point& bottomleft, const t_point& upperright);
void drawrect (float x1, float y1, float x2, float y2);
/* Draws a filled rectangle with the specified corners. */
void fillrect (const t_bound_box& rect);
void fillrect (const t_point& bottomleft, const t_point& upperright);
void fillrect (float x1, float y1, float x2, float y2);
/* Draws a filled polygon */
void fillpoly (t_point *points, int npoints);
/* Draw or fill a circular arc or elliptical arc. Angles in degrees.
* startang is measured from positive x-axis of Window.
* A positive angextent means a counterclockwise arc; a negative
* angextent means clockwise.
*/
void drawarc (float xcen, float ycen, float rad, float startang,
float angextent);
void fillarc (const t_point& center, float rad, float startang, float angextent);
void fillarc (float xcen, float ycen, float rad, float startang,
float angextent);
void drawellipticarc (
const t_point& center, float radx, float rady, float startang, float angextent);
void drawellipticarc (
float xc, float yc, float radx, float rady, float startang, float angextent);
void fillellipticarc (t_point center, float radx, float rady, float startang, float angextent);
void fillellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent);
/*
* These functions all draw UTF-8 text within some sort of bounding box.
* The text is drawn centred around the point (xc,yc), text_center, or
* in the case of drawtext_in, the centre of the bbox parameter.
*
* For debugging purposes, if you would like to see exactly where the
* bounds of the text are, its center, as what well as where boundx
* and boundy really extend to, define SHOW_TEXT_BBOX in your build,
* or uncomment it in drawtext(..) in graphics.c .
*
* - SPECIFIC PAREMETER SPECIFICATION:
* boundx and boundy specify a width and height bound, respectively, whereas
* bounds and bbox specify a box in which the text must fit inside completely.
*
* If you would like to have these functions ignore a particular bound,
* specify a huge value. I recommend FLT_MAX or std::numeric_limits<float>::max(),
* from <cfloat> and <limits> respectively.
*
* If text won't fit in bounds specified, the text isn't drawn.
* Useful for avoiding text going everywhere at high zoom levels.
*
* tolerance, effectively, makes the given bounding box bigger, on
* all sides by that amount.
*
* - NOTES:
* Finally, it should be noted that bounding is done based on the dimensions of
* the final bounding rectangle of the actual rendered text, so the content _will_
* affect the height (and/or width with rotated text), therefore affecting the bounds.
* If you would like to hide text based on zoomlevel and fontsize directly, use
* the Level Of Detail functions for this.
*
* As said before, these functions take UTF-8 encoded strings, so if you would like
* to use non-ASCII characters, make sure your font supports them. Also, note that
* those characters probably won't show up in postscript output, as postscript doesn't
* understand UTF-8. They will be present in the file, but won't be displayed.
*
* Oh, and one more thing, if you would like aligned baselines, see the large comment
* in drawtext(..) in graphics.c .
*/
void drawtext_in (const t_bound_box& bbox, const char* text);
void drawtext_in (const t_bound_box& bbox, const char* text, float tolerance);
void drawtext (const t_point& text_center, const char* text, const t_bound_box& bounds);
void drawtext (
const t_point& text_center, const char* text, const t_bound_box& bounds, float tolerance);
void drawtext (const t_point& text_center, const char* text, float boundx, float boundy);
void drawtext (float xc, float yc, const char* text, float boundx, float boundy);
/* Control what buttons are active (default: all enabled) and
* whether mouse movements and keypresses are sent to callback
* functions (default: disabled).
*/
void set_mouse_move_input (bool turn_on);
void set_keypress_input (bool turn_on);
void enable_or_disable_button (int ibutton, bool enabled);
/*************** ADVANCED FUNCTIONS *****************/
/* Normal users shouldn't have to use draw_message. Should only be
* useful if using non-interactive graphics and you want to redraw
* yourself because of an expose.
*/
void draw_message (void);
/* Empties event queue. Can be useful with non-interactive graphics to make
* sure things display.
*/
void flushinput (void);
/* DRAW_NORMAL is the default mode (overwrite, also known as copy_pen).
* Can use DRAW_XOR for fast rubber-banding.
*/
enum e_draw_mode {DRAW_NORMAL = 0, DRAW_XOR};
void set_draw_mode (enum e_draw_mode draw_mode);
/**
* Change the text on a button. Both strings are UTF-8.
*/
void change_button_text(const char *button_text, const char *new_button_text);
/* For debugging only. Get window size etc. */
void report_structure(t_report*);
/************************************
* Level Of Detail Functions
*
* These functions may be convenient for deciding to not draw
* small details, unless they user is zoomed in past a certain level.
************************************/
/**
* Returns a rectangle with the bounds of the drawn world.
* Also useful for getting the currently visible rectangle,
* to manipulate it, or restore it later, by passing it back
* to init_world(..)
*/
t_bound_box get_visible_world();
/**
* returns true iff the _world_ area of the screen is
* below a area_threshold.
*/
inline bool LOD_area_test(float area_threshold) {
return get_visible_world().area() < area_threshold;
}
/**
* returns true iff the smallest dimension of the visible
* world is less than dim_threshold.
*/
inline bool LOD_min_dim_test(float dim_threshold) {
t_bound_box vis_world = get_visible_world();
return
(
(vis_world.get_height() < vis_world.get_width()) ?
vis_world.get_height() : vis_world.get_width()
) < dim_threshold;
}
/**
* screen_area_threshold is in (screen pixels)^2. I suggest something around 3
*
* Iff the _screen_ area of the rectangle, specified throught the various means,
* is less than screen_area_threshold then these functions return false.
*/
bool LOD_screen_area_test(t_bound_box test, float screen_area_threshold);
inline bool LOD_screen_area_test(float width, float height, float screen_area_threshold) {
return LOD_screen_area_test(t_bound_box(0,0,width,height),screen_area_threshold);
}
inline bool LOD_screen_area_test_square(float width, float screen_area_threshold) {
return LOD_screen_area_test(width,width,screen_area_threshold);
}
/**************** Extra functions available only in WIN32. *******/
#ifdef WIN32
/* VB: TODO: I should make any generally useful functions below work in
* X11 as well, and probably delete anything else.
*/
/* MW: Draw beizer curve. Currently not used, but saving for possible use
* in the future.
*/
void win32_drawcurve(t_point *points, int npoints);
void win32_fillcurve(t_point *points, int npoints);
#endif // WIN32
#endif // GRAPHICS_H