blob: 53519ebdddb76ea0d5cc2d5b10b4246e9e9077a9 [file] [log] [blame] [edit]
#ifndef GRAPHICS_H
#define GRAPHICS_H
#include <iostream>
#include <string>
#include <cstdint>
#include <cfloat> // For FLT_MAX
#include "easygl_constants.h"
#include "graphics_types.h"
#include "Surface.h"
// 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
#define PI 3.141592654
/************** ESSENTIAL FUNCTIONS: SET UP AND CLOSE DOWN ******************/
/* This is the main routine for the graphics. When event_loop is
* called, it will continue executing until the Proceed button is
* pressed. It will handle many "events" (mouse clicks, button presses, mouse
* zooms and pans, etc.) for you, and will pass on other events to callback
* functions you have passed in for your program to handle.
* 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 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).
* act_on_keypress() is passed both the ASCII (char) code of the key_pressed,
* and an integer "keysym" which is a longer code defined in X11/keysymdef.h
* that has codes for keys like arrow keys that have no ASCII equivalent.
* act_on_mousebutton() is passed the coordinates (in user coordinates) of
* the mouse when the button was pressed, along with a t_event_buttonPressed
* structure that stores which buttons was pressed and if Shift or Ctrl was
* held down, etc.
*/
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, int keysym),
void (*drawscreen) ());
/* Opens up the graphics; the window will have the UTF-8 string window_name
* in its title bar and have the specified background colour.
*/
void init_graphics(const std::string& window_name, int cindex_background);
void init_graphics(const std::string& window_name, const t_color& background);
void change_graphics_background(const t_color& background); // <Addition/Mod: Charles>
/* Sets world coordinates of the graphics window so that the
* lower-left corner of the screen has world coordinate (xl, yb) and the
* top-right corner has world coordinate (xr, yt), or if you prefer you
* can call the other version with a bounding_box giving the coordinates.
* 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. If the aspect ratio (height/width) of
* the coordinates you specify does not match the screen window aspect ratio,
* either the visible height or visible width of your world will be increased
* so that 1-world-unit vertically is the same distance as 1-world-unit
* horizontally when rendered on the screen.
*/
void set_visible_world(float xl, float yb, float xr, float yt);
void set_visible_world(const t_bound_box& bounds);
/**
* Returns a rectangle with the bounds of the drawn world -- i.e. what are
* the edges in your chosen (world) coordinate system that match the edges of
* the window? Useful for figuring out how zoomed in or out you are.
* Also useful for getting the currently visible rectangle,
* to manipulate it, or restore it later, by passing it back
* to set_visible_world(..)
*/
t_bound_box get_visible_world();
/* Returns a rectangle with the screen coordinates -- i.e. top left is (0,0) --
* defining the graphics area into which you can draw. This area doesn't include
* the menu area with buttons or the status message area.
*/
t_bound_box get_visible_screen();
/* Closes the graphics windows, frees all its memory and so on. */
void close_graphics();
/************************* USER INTERACTION FUNCTIONS ******************/
/* Changes the status bar message (at the bottom of the main window) to the
* UTF-8 string msg.
*/
void update_message(const std::string& msg);
/* Creates a button on the menu bar below the button with text (UTF-8)
* prev_button_text. The button will have text (UTF-8) button_text,
* and when clicked will call function button_func.
* button_func is a function that accepts a void function as
* an argument; this argument is set to the drawscreen routine
* as passed into the event loop.
*/
void create_button(const char *prev_button_text, const char *button_text,
void (*button_func) (void (*drawscreen) ()));
/* Destroys the button with the given text; i.e. removes it from
* the display.
*/
void destroy_button(const char *button_text);
/* Control what buttons are active (default: all enabled) and
* whether mouse movements and keyboard presses 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);
/***************************** DRAWING ROUTINES *********************/
/****
* The following routines draw anything you want to the biggest part of the
* screen (the graphics sub-window), or if you have set the mode to PostScript
* they write to a file to generate the printable file.
*
* The calls below split into ones that set "graphics attributes" like colour
* and line widths, and rendering functions that actually draw text, lines, etc.
* All the graphics attribute calls are "sticky" -- they affect all subsequent
* drawing until you change the attribute again.
****/
/* Clears the screen. Should normally be the first call in your
* screen redrawing function.
*/
void clearscreen();
/**
* Set the current draw colour to the supplied index colour from color_types,
* the specified (rgba) t_colour, or rgba value. alpha (a) = 0 means transparent,
* while a = 255 means opaque (the default). Affects all drawing functions,
* including text. Note: postscript does not support transparency, so
* the alpha value is ignored in postscript output.
*/
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, uint_fast8_t a = 255);
/**
* 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. Slower than the other setcolor variations.
*/
void setcolor_by_name(std::string cname);
/* Get the current color */
t_color getcolor();
/* Sets the line style to the specified line_style, SOLID or DASHED
* capstyle can be BUTT or ROUND, default is BUTT
*/
void setlinestyle(int linestyle, int capstyle = BUTT);
/* 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(int degrees);
int gettextrotation();
/*
* Set both the point size and rotation of the text in one call.
* Slightly more effecient then calling setfontsize() and settextrotation()
* separately, 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, int degrees);
/* All the drawing functions below use current coordinate system set (either
* world (the default) or screen.
*/
/* Draws a line from (x1, y1) to (x2, y2) or p1 to p2 */
void drawline(float x1, float y1, float x2, float y2);
void drawline(const t_point& p1, const t_point& p2);
/* Draws the rectangle with diagonally-opposite corners
* at (x1, y1) and (x2, y2) or bottomleft and upperright points, or
* the bound_box rect. All 3 functions are equivalent (use whichever is most
* convenient), and draw with the current line style, colour and width.
*/
void drawrect(float x1, float y1, float x2, float y2);
void drawrect(const t_point& bottomleft, const t_point& upperright);
void drawrect(const t_bound_box& rect);
/* Draws a filled rectangle with the specified corners. */
void fillrect(float x1, float y1, float x2, float y2);
void fillrect(const t_point& bottomleft, const t_point& upperright);
void fillrect(const t_bound_box& rect);
/* Draws a filled polygon with the specified points as its boundary.
* The first and last points passed in are connected to close the 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(
float xc, float yc, float radx, float rady, float startang, float angextent);
void drawellipticarc(
const 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);
void fillellipticarc(const t_point& center, float radx, float rady, float startang, float angextent);
/*
* These functions all draw UTF-8 text within some sort of bounding box;
* they are all very closely related and just give slightly different
* interfaces so you can choose whichever one is most convenient.
*
* The text is drawn centered around the point (xc,yc), text_center, or
* in the case of drawtext_in, the centre of the bbox parameter.
*
* - SPECIFIC PAREMETER SPECIFICATION:
*
* If text won't fit in bounds specified, the text isn't drawn.
* This is useful for avoiding text going everywhere at high zoom levels.
*
* boundx and boundy specify a width and height bound, respectively, whereas
* bounds and bbox specify a box in which the text completely fit, or it won't
* be drawn. boundx and boundy default to FLT_MAX, meaning always draw the
* text.
*
* 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.
*
* tolerance, effectively, makes the given bounding box bigger, on
* all sides by that amount.
*
* For debugging purposes, if you would like to see exactly where the
* bounds of the text are and the center of the text define SHOW_TEXT_BBOX
* in your build, or uncomment it in drawtext(..) in graphics.c .
*
* - 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(float xc, float yc, const std::string& text, float boundx=FLT_MAX, float boundy=FLT_MAX);
float gettextwidth(const std::string& text); // In the coodirnate system current drawing at // <Addition/Mod: Charles>
void drawtextleftaligned(float xc, float yc, const std::string& text, float boundx = FLT_MAX, float boundy = FLT_MAX); // <Addition/Mod: Charles>
void drawtextrightaligned(float xc, float yc, const std::string& text, float boundx = FLT_MAX, float boundy = FLT_MAX); // <Addition/Mod: Charles>
void drawtext(const t_point& text_center, const std::string& text, float boundx=FLT_MAX, float boundy=FLT_MAX);
void drawtext(const t_point& text_center, const std::string& text, const t_bound_box& bounds);
void drawtext(const t_point& text_center, const std::string& text, const t_bound_box& bounds, float tolerance);
void drawtext_in(const t_bound_box& bbox, const std::string& text);
void drawtext_in(const t_bound_box& bbox, const std::string& text, float tolerance);
/**
* World coordinates = in the scene; will automatically pan and zoom
* Screen coordinates = for any UI overlays; will be fixed in one spot on the window
*/
void set_coordinate_system(t_coordinate_system coord);
/*************************** ADVANCED FUNCTIONS ***************************/
/* Empties event queue. Can be useful with non-interactive graphics (animation)
* to make sure things display.
*/
void flushinput();
/* DRAW_NORMAL is the default mode (overwrite, also known as copy_pen).
* Can use DRAW_XOR for fast rubber-banding.
*/
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);
/* 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();
/**
* @brief Changes what all the drawX functions draws to.
*
* By default, drawX will draw straight to the screen. By setting this to
* draw off screen, all drawX commands will draw to an offscreen pixmap.
* When you've finished drawing everything to this pixmap, copy it to the
* screen using copy_off_screen_buffer_to_screen(). Drawing to an offscreen
* buffer like this (double-buffering) can reduce screen flashing when you
* pan the graphics, making the drawing seem smoother.
*
* @param[in] draw_mode ON_SCREEN || OFF_SCREEN
*/
void set_drawing_buffer(t_draw_to draw_mode);
/**
* @brief Copies the offscreen buffer to the main screen.
*
* Call this function when you want to display the offscreen pixmap to the
* main screen. This routine does nothing if the set drawing_buffer was ON_SCREEN.
*/
void copy_off_screen_buffer_to_screen();
/**
* @brief Loads png from file.
*
* @return A Surface you can draw to the screen.
*/
Surface load_png_from_file(const char* file_path);
/**
* @brief Draws a Surface.
*
* @param[in] x x coordinate (world or screen) of upper-left corner
* @param[in] y y coordinate (world or screen) of upper-left corner
* @param[in] surface Surface loaded from load_png_from_file(const char*)
*/
void draw_surface(const Surface& surface, float x, float y);
void draw_surface(const Surface& surface, t_point upper_left);
/**************** 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 true iff the area of your (world) coordinate system
* that is currently visible on the screen is below 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.
*/
bool LOD_min_dim_test(float dim_threshold);
/**
* screen_area_threshold is in (screen pixels)^2. I suggest something around 3
*
* Iff the _screen_ area of the rectangle (passed in as world coordinates)
* is less than screen_area_threshold then this function returns false. When
* this function returns false it means that bounding box passed in will
* occupy less than (screen_area_threshold pixels)^2 on the screen.
*/
bool LOD_screen_area_test(t_bound_box test, float screen_area_threshold);
/**
* returns the screen coordinates (i.e. pixel location) of the world coordinate 'point'
*/
t_point world_to_scrn(const t_point& point);
/**
* returns the screen coordinates (i.e. pixel locations) of the world coordinate bounding box
* 'box'
*/
t_bound_box world_to_scrn(const t_bound_box& box);
/**
* returns the world coordinates of the screen (i.e. pixel) location 'point'
*/
t_point scrn_to_world(const t_point& point);
/**
* returns the world coordinates of the screen (i.e. pixel) bounding box 'box'
*/
t_bound_box scrn_to_world(const t_bound_box& box);
/***************** POSTSCRIPT PRINTING 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.
*
* Limitation: Postscript output does not support transparency (object will
* be opaque) or draw_surface of pngs/bitmaps (will not be drawn).
*
* 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();
/******************** DEBUGGING FUNCTIONS **********************************/
/* Data structure below is for debugging the easygl API itself; you normally
* will never use it or the functions below.
*/
/* t_report 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;
/* For debugging only. Get window size etc. */
void get_report_structure(t_report*);
/**************** Extra functions available only in WIN32 (and Cygwin). *******/
#if defined(WIN32) || defined(CYGWIN)
/* VB: TODO: I should make any generally useful functions below work in
* X11 as well, and probably delete anything else.
*/
/* 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);
/* Error message reporter */
void WIN32_DELETE_ERROR();
#endif // WIN32 or CYGWIN
#endif // GRAPHICS_H