550 lines
17 KiB
C++
550 lines
17 KiB
C++
/*
|
|
* Copyright 2019 University of Toronto
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* Authors: Mario Badr, Sameh Attia and Tanner Young-Schultz
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* This example shows you how to create an application using the EZGL library.
|
|
*/
|
|
|
|
#include "ezgl/application.hpp"
|
|
#include "ezgl/graphics.hpp"
|
|
|
|
// Callback functions for event handling
|
|
void act_on_mouse_press(ezgl::application *application, GdkEventButton *event, double x, double y);
|
|
void act_on_mouse_move(ezgl::application *application, GdkEventButton *event, double x, double y);
|
|
void act_on_key_press(ezgl::application *application, GdkEventKey *event, char *key_name);
|
|
void initial_setup(ezgl::application *application, bool new_window);
|
|
void test_button(GtkWidget *widget, ezgl::application *application);
|
|
|
|
/**
|
|
* Draw to the main canvas using the provided graphics object.
|
|
*
|
|
* The graphics object expects that x and y values will be in the main canvas' world coordinate system.
|
|
*/
|
|
void draw_main_canvas(ezgl::renderer *g);
|
|
|
|
/**
|
|
* draw_main_canvas helper functions
|
|
*/
|
|
void draw_rectangle_example(ezgl::renderer *g);
|
|
void draw_arc_example(ezgl::renderer *g);
|
|
void rotated_text_example(ezgl::renderer *g);
|
|
void draw_poly_example(ezgl::renderer *g);
|
|
void draw_text_example(ezgl::renderer *g);
|
|
void draw_line_example(ezgl::renderer *g);
|
|
void screen_coordinates_example(ezgl::renderer *g);
|
|
void draw_png_example(ezgl::renderer *g);
|
|
|
|
static ezgl::rectangle initial_world{{0, 0}, 1100, 1150};
|
|
|
|
/**
|
|
* The start point of the program.
|
|
*
|
|
* This function initializes an ezgl application and runs it.
|
|
*
|
|
* @param argc The number of arguments provided.
|
|
* @param argv The arguments as an array of c-strings.
|
|
*
|
|
* @return the exit status of the application run.
|
|
*/
|
|
int main(int /*argc*/, char **/*argv*/)
|
|
{
|
|
ezgl::application::settings settings;
|
|
|
|
// Path to the resource that contains an XML description of the UI.
|
|
// Note: this is not a file path, it is a resource path.
|
|
settings.main_ui_resource = "/ezgl/main.ui";
|
|
|
|
// Note: the "main.ui" file has a GtkWindow called "MainWindow".
|
|
settings.window_identifier = "MainWindow";
|
|
|
|
// Note: the "main.ui" file has a GtkDrawingArea called "MainCanvas".
|
|
settings.canvas_identifier = "MainCanvas";
|
|
|
|
// Create our EZGL application.
|
|
ezgl::application application(settings);
|
|
|
|
application.add_canvas("MainCanvas", draw_main_canvas, initial_world);
|
|
|
|
// Run the application until the user quits.
|
|
// This hands over all control to the GTK runtime---after this point
|
|
// you will only regain control based on callbacks you have setup.
|
|
// Three callbacks can be provided to handle mouse button presses,
|
|
// mouse movement and keyboard button presses in the graphics area,
|
|
// respectively. Also, an initial_setup function can be passed that will
|
|
// be called before the activation of the application and can be used
|
|
// to create additional buttons, initialize the status message, or
|
|
// connect added widgets to their callback functions.
|
|
// Those callbacks are optional, so we can pass nullptr if
|
|
// we don't need to take any action on those events
|
|
return application.run(initial_setup, act_on_mouse_press, act_on_mouse_move, act_on_key_press);
|
|
}
|
|
|
|
/**
|
|
* The redrawing function for still pictures
|
|
*/
|
|
void draw_main_canvas(ezgl::renderer *g)
|
|
{
|
|
// Draw some rectangles
|
|
draw_rectangle_example(g);
|
|
|
|
// Draw different arcs
|
|
draw_arc_example(g);
|
|
|
|
// Draw some rotated text
|
|
rotated_text_example(g);
|
|
|
|
// Draw different transparent and opaque polys
|
|
draw_poly_example(g);
|
|
|
|
// Draw some text with different sizes
|
|
draw_text_example(g);
|
|
|
|
// Draw wide lines with different end shapes
|
|
draw_line_example(g);
|
|
|
|
// Draw to screen coordinates
|
|
screen_coordinates_example(g);
|
|
|
|
// Draw a small png
|
|
draw_png_example(g);
|
|
}
|
|
|
|
/**
|
|
* Draw some rectangles with different colors
|
|
*/
|
|
void draw_rectangle_example(ezgl::renderer *g)
|
|
{
|
|
const float rectangle_width = 50;
|
|
const float rectangle_height = rectangle_width;
|
|
const ezgl::point2d start_point(150, 30);
|
|
ezgl::rectangle color_rectangle = {start_point, rectangle_width, rectangle_height};
|
|
|
|
// Some of the available colors, a complete list is in ezgl/include/color.hpp
|
|
ezgl::color color_indicies[] = {
|
|
ezgl::GREY_55,
|
|
ezgl::GREY_75,
|
|
ezgl::WHITE,
|
|
ezgl::BLACK,
|
|
ezgl::BLUE,
|
|
ezgl::GREEN,
|
|
ezgl::YELLOW,
|
|
ezgl::CYAN,
|
|
ezgl::RED,
|
|
ezgl::DARK_GREEN,
|
|
ezgl::MAGENTA
|
|
};
|
|
|
|
// format text font and color
|
|
g->set_color(ezgl::BLACK);
|
|
g->format_font("monospace", ezgl::font_slant::normal, ezgl::font_weight::normal, 10);
|
|
|
|
// draw text
|
|
g->draw_text({110, color_rectangle.center_y()}, "colors", 2 * (start_point.x - 110), rectangle_height);
|
|
|
|
for (size_t i = 0; i < sizeof (color_indicies) / sizeof (color_indicies[0]); ++i) {
|
|
// Change the color of next draw calls
|
|
g->set_color(color_indicies[i]);
|
|
|
|
// Draw filled in rectangles
|
|
g->fill_rectangle(color_rectangle);
|
|
|
|
// Increment the start point
|
|
color_rectangle += {rectangle_width, 0};
|
|
}
|
|
|
|
// Draw text
|
|
g->draw_text({400, color_rectangle.center_y()}, "fill_rectangle", DBL_MAX, rectangle_height);
|
|
|
|
/* Draw some rectangles with RGB triplet colors and alpha (transparency) */
|
|
|
|
// Hack to make the colors change once per second
|
|
std::srand(time(0));
|
|
|
|
for (size_t i = 0; i < 3; ++i) {
|
|
// Increment the start point
|
|
color_rectangle += {rectangle_width, 0};
|
|
|
|
// Change the next draw calls color. rgb and alpha values range from 0 to 255
|
|
g->set_color(std::rand() % 256, std::rand() % 256, std::rand() % 256, 255);
|
|
|
|
// Draw filled in rectangles
|
|
g->fill_rectangle(color_rectangle);
|
|
}
|
|
|
|
/* Draw a black border rectangle */
|
|
|
|
// Change the next draw calls color to black
|
|
g->set_color(ezgl::BLACK);
|
|
|
|
// Change the next draw calls line width
|
|
g->set_line_width(1);
|
|
|
|
// Draw a rectangle bordering all the drawn rectangles
|
|
g->draw_rectangle(start_point, color_rectangle.top_right());
|
|
}
|
|
|
|
/**
|
|
* Draw some example lines, shapes, and arcs
|
|
*/
|
|
void draw_arc_example(ezgl::renderer *g)
|
|
{
|
|
float radius = 50;
|
|
|
|
// Draw solid line
|
|
g->set_color(ezgl::BLACK);
|
|
g->draw_text({250, 150}, "draw_line", 150.0, DBL_MAX);
|
|
g->set_line_dash(ezgl::line_dash::none);
|
|
g->draw_line({200, 120}, {200, 200});
|
|
|
|
// Draw dashed line
|
|
g->set_line_dash(ezgl::line_dash::asymmetric_5_3);
|
|
g->draw_line({300, 120}, {300, 200});
|
|
|
|
// Draw elliptic arc
|
|
g->set_color(ezgl::MAGENTA);
|
|
g->draw_text({450, 160}, "draw_elliptic_arc", 150.0, DBL_MAX);
|
|
g->draw_elliptic_arc({550, 160}, 30, 60, 90, 270);
|
|
|
|
// Draw filled in elliptic arc
|
|
g->draw_text({700, 160}, "fill_elliptic_arc", 150.0, DBL_MAX);
|
|
g->fill_elliptic_arc({800, 160}, 30, 60, 90, 270);
|
|
|
|
// Draw arcs
|
|
g->set_color(ezgl::BLUE);
|
|
g->draw_text({190, 300}, "draw_arc", radius * 2, 150);
|
|
g->draw_arc({190, 300}, radius, 0, 270);
|
|
g->draw_arc({300, 300}, radius, 0, -180);
|
|
|
|
// Draw filled in arcs
|
|
g->fill_arc({410, 300}, radius, 90, -90);
|
|
g->fill_arc({520, 300}, radius, 0, 360);
|
|
g->set_color(ezgl::BLACK);
|
|
g->draw_text({520, 300}, "fill_arc", radius * 2, 150);
|
|
g->set_color(ezgl::BLUE);
|
|
g->fill_arc({630, 300}, radius, 90, 180);
|
|
g->fill_arc({740, 300}, radius, 90, 270);
|
|
g->fill_arc({850, 300}, radius, 90, 30);
|
|
}
|
|
|
|
/**
|
|
* Draw some rotated text
|
|
*/
|
|
void rotated_text_example(ezgl::renderer *g)
|
|
{
|
|
const float textsquare_width = 200;
|
|
|
|
ezgl::rectangle textsquare = {{100, 400}, textsquare_width, textsquare_width};
|
|
|
|
g->set_color(ezgl::BLUE);
|
|
g->draw_rectangle(textsquare);
|
|
|
|
g->set_color(ezgl::GREEN);
|
|
g->draw_rectangle(textsquare.center(), {textsquare.right(), textsquare.top()});
|
|
g->draw_rectangle({textsquare.left(), textsquare.bottom()}, textsquare.center());
|
|
|
|
g->set_color(ezgl::RED);
|
|
g->draw_line({textsquare.left(), textsquare.bottom()}, {textsquare.right(), textsquare.top()});
|
|
g->draw_line({textsquare.left(), textsquare.top()}, {textsquare.right(), textsquare.bottom()});
|
|
|
|
g->set_color(0, 0, 0, 100);
|
|
g->set_font_size(14);
|
|
g->draw_text({textsquare.center_x(), textsquare.bottom()}, "0 degrees", textsquare.width(), textsquare.height());
|
|
|
|
g->set_text_rotation(90);
|
|
g->draw_text({textsquare.right(), textsquare.center_y()}, "90 degrees", textsquare.width(), textsquare.height());
|
|
|
|
g->set_text_rotation(180);
|
|
g->draw_text({textsquare.center_x(), textsquare.top()}, "180 degrees", textsquare.width(), textsquare.height());
|
|
|
|
g->set_text_rotation(270);
|
|
g->draw_text({textsquare.left(), textsquare.center_y()}, "270 degrees", textsquare.width(), textsquare.height());
|
|
|
|
g->set_text_rotation(45);
|
|
g->draw_text(textsquare.center(), "45 degrees", textsquare.width(), textsquare.height());
|
|
|
|
g->set_text_rotation(135);
|
|
g->draw_text(textsquare.center(), "135 degrees", textsquare.width(), textsquare.height());
|
|
|
|
// It is probably a good idea to set text rotation back to zero,
|
|
g->set_text_rotation(0);
|
|
}
|
|
|
|
/**
|
|
* Draw some Polygons
|
|
*/
|
|
void draw_poly_example(ezgl::renderer *g)
|
|
{
|
|
g->set_font_size(10);
|
|
g->set_color(ezgl::RED);
|
|
|
|
// Draw a triangle
|
|
g->fill_poly({{500, 400}, {440, 480}, {560, 480}});
|
|
|
|
// Draw a 4-point polygon
|
|
g->fill_poly({{700, 400}, {650, 480}, {750, 480}, {800, 400}});
|
|
|
|
g->set_color(ezgl::BLACK);
|
|
g->draw_text({500, 450}, "fill_poly", 80.0, DBL_MAX);
|
|
g->draw_text({725, 440}, "fill_poly", 100.0, DBL_MAX);
|
|
|
|
g->set_color(ezgl::DARK_GREEN);
|
|
g->set_line_dash(ezgl::line_dash::none);
|
|
ezgl::rectangle rect = {{350, 550}, {650, 670}};
|
|
g->draw_text(rect.center(), "draw_rectangle", rect.width(), rect.height());
|
|
g->draw_rectangle(rect);
|
|
|
|
/* Draw some semi-transparent primitives */
|
|
g->set_font_size(10);
|
|
|
|
g->set_color(255, 0, 0, 255);
|
|
g->fill_rectangle({1000, 400}, {1050, 800});
|
|
|
|
g->set_color(0, 0, 255, 255);
|
|
g->fill_rectangle({1000+50, 400}, {1050+50, 800});
|
|
|
|
g->set_color(0, 255, 0, 255/2); // 50% transparent
|
|
g->fill_rectangle({1000+25, 400-100}, {1050+25, 800-200});
|
|
|
|
g->set_color(255, 100, 255, 255/2);
|
|
g->fill_poly({{465, 380}, {400, 450}, {765, 450}, {850, 380}});
|
|
|
|
g->set_color(100, 100, 255, 255/3);
|
|
g->fill_poly({{550, 420}, {475, 500}, {875, 500}});
|
|
|
|
g->set_color(ezgl::BLACK);
|
|
g->set_text_rotation(90);
|
|
g->draw_text({1000 - 50, 500}, "Partially transparent polys", 500, DBL_MAX);
|
|
g->set_text_rotation(0);
|
|
}
|
|
|
|
/**
|
|
* Draw some example text, with the bounding box functions
|
|
*/
|
|
void draw_text_example(ezgl::renderer *g)
|
|
{
|
|
|
|
const float text_example_width = 800;
|
|
const int num_lines = 2;
|
|
const int max_strings_per_line = 3;
|
|
const int num_strings_per_line[num_lines] = {3, 2};
|
|
|
|
const char* const line_text[num_lines][max_strings_per_line] = {
|
|
{
|
|
"8 Point Text",
|
|
"12 Point Text",
|
|
"18 Point Text"
|
|
},
|
|
{
|
|
"24 Point Text",
|
|
"32 Point Text"
|
|
}
|
|
};
|
|
|
|
const int text_sizes[num_lines][max_strings_per_line] = {
|
|
{8, 12, 15},
|
|
{24, 32}
|
|
};
|
|
|
|
g->set_color(ezgl::BLACK);
|
|
g->set_line_dash(ezgl::line_dash::asymmetric_5_3);
|
|
|
|
for (int i = 0; i < num_lines; ++i) {
|
|
ezgl::rectangle text_bbox = {{100., 710. + i * 60.}, text_example_width / num_strings_per_line[i], 60.};
|
|
|
|
for (int j = 0; j < num_strings_per_line[i]; ++j) {
|
|
g->set_font_size(text_sizes[i][j]);
|
|
g->draw_text(text_bbox.center(), line_text[i][j], text_bbox.width(), text_bbox.height());
|
|
g->draw_rectangle(text_bbox);
|
|
text_bbox = {{text_bbox.left() + text_example_width / num_strings_per_line[i], text_bbox.bottom()} , text_bbox.width(), text_bbox.height()};
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draw wide lines with different end shapes
|
|
*/
|
|
void draw_line_example(ezgl::renderer *g)
|
|
{
|
|
g->set_font_size(10);
|
|
|
|
for (int i = 0; i <= 2; ++i)
|
|
{
|
|
double offsetY = 50*i;
|
|
|
|
g->set_horiz_text_just(ezgl::text_just::left);
|
|
|
|
if (i == 0) {
|
|
g->set_color(ezgl::BLACK);
|
|
g->set_line_cap(ezgl::line_cap::butt); // Butt ends
|
|
g->set_line_dash(ezgl::line_dash::none); // Solid line
|
|
g->draw_text({950, 920+offsetY}, "Butt ends, opaque", 400, DBL_MAX);
|
|
}
|
|
|
|
else if (i == 1) {
|
|
g->set_color(ezgl::GREEN, 255*2/3); // Green line that is 33% transparent)
|
|
g->set_line_cap(ezgl::line_cap::round); // Round ends
|
|
g->set_line_dash(ezgl::line_dash::none); // Solid line
|
|
g->draw_text({950, 920+offsetY}, "Round ends, 33% transparent", 400, DBL_MAX);
|
|
}
|
|
|
|
else {
|
|
g->set_color(ezgl::RED, 255/3); // Red line that is 67% transparent
|
|
g->set_line_cap(ezgl::line_cap::butt); // butt ends
|
|
g->set_line_dash(ezgl::line_dash::asymmetric_5_3); // Dashed line
|
|
g->draw_text({950, 920+offsetY}, "Butt ends, 67% transparent", 400, DBL_MAX);
|
|
}
|
|
|
|
g->set_horiz_text_just(ezgl::text_just::center);
|
|
|
|
g->draw_text({200, 900+offsetY}, "Thin line (width 1)", 200, DBL_MAX);
|
|
g->set_line_width(1);
|
|
g->draw_line({100, 920+offsetY}, {300, 920+offsetY});
|
|
|
|
g->draw_text({500, 900+offsetY}, "Width 3 Line", 200, DBL_MAX);
|
|
g->set_line_width(3);
|
|
g->draw_line({400, 920+offsetY}, {600, 920+offsetY});
|
|
|
|
g->draw_text({800, 900+offsetY}, "Width 6 Line", 200, DBL_MAX);
|
|
g->set_line_width(6);
|
|
g->draw_line({700, 920+offsetY}, {900, 920+offsetY});
|
|
|
|
g->set_line_width(1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Draw to screen coordinates where (0,0) is the top-left corner of the window
|
|
* These coordinates are not transformed so the object will not pan or zoom.
|
|
*/
|
|
void screen_coordinates_example(ezgl::renderer *g)
|
|
{
|
|
// Set the coordinate system to SCREEN
|
|
g->set_coordinate_system(ezgl::SCREEN);
|
|
|
|
g->set_color(255, 0, 0, 255);
|
|
g->set_line_dash(ezgl::line_dash::none);
|
|
g->draw_rectangle({10, 10}, {100, 100});
|
|
g->set_font_size(10);
|
|
g->draw_text({55, 33}, "Screen coord");
|
|
g->draw_text({55, 66}, "Fixed loc");
|
|
|
|
// Set the coordinate system back to WORLD
|
|
g->set_coordinate_system(ezgl::WORLD);
|
|
}
|
|
|
|
/**
|
|
* Draw a small PNG
|
|
*/
|
|
void draw_png_example(ezgl::renderer *g)
|
|
{
|
|
ezgl::surface *png_surface = ezgl::renderer::load_png("small_image.png");
|
|
g->draw_surface(png_surface, {50, 200});
|
|
ezgl::renderer::free_surface(png_surface);
|
|
g->set_font_size(10);
|
|
g->set_color(ezgl::BLACK);
|
|
g->draw_text ({50, 225}, "draw_surface", 200, DBL_MAX);
|
|
}
|
|
|
|
/**
|
|
* Function called before the activation of the application
|
|
* Can be used to create additional buttons, initialize the status message,
|
|
* or connect added widgets to their callback functions
|
|
*/
|
|
void initial_setup(ezgl::application *application, bool /*new_window*/)
|
|
{
|
|
// Update the status bar message
|
|
application->update_message("EZGL Application");
|
|
|
|
// Create a Test button and link it with test_button callback fn.
|
|
application->create_button("Test", 6, test_button);
|
|
}
|
|
|
|
/**
|
|
* Function to handle mouse press event
|
|
* The current mouse position in the main canvas' world coordinate system is returned
|
|
* A pointer to the application and the entire GDK event are also returned
|
|
*/
|
|
void act_on_mouse_press(ezgl::application *application, GdkEventButton *event, double x, double y)
|
|
{
|
|
application->update_message("Mouse Clicked");
|
|
|
|
std::cout << "User clicked the ";
|
|
|
|
if (event->button == 1)
|
|
std::cout << "left ";
|
|
else if (event->button == 2)
|
|
std::cout << "middle ";
|
|
else if (event->button == 3)
|
|
std::cout << "right ";
|
|
|
|
std::cout << "mouse button at coordinates (" << x << "," << y << ") ";
|
|
|
|
if ((event->state & GDK_CONTROL_MASK) && (event->state & GDK_SHIFT_MASK))
|
|
std::cout << "with control and shift pressed ";
|
|
else if (event->state & GDK_CONTROL_MASK)
|
|
std::cout << "with control pressed ";
|
|
else if (event->state & GDK_SHIFT_MASK)
|
|
std::cout << "with shift pressed ";
|
|
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
/**
|
|
* Function to handle mouse move event
|
|
* The current mouse position in the main canvas' world coordinate system is returned
|
|
* A pointer to the application and the entire GDK event are also returned
|
|
*/
|
|
void act_on_mouse_move(ezgl::application */*application*/, GdkEventButton */*event*/, double x, double y)
|
|
{
|
|
std::cout << "Mouse move at coordinates (" << x << "," << y << ") "<< std::endl;
|
|
}
|
|
|
|
/**
|
|
* Function to handle keyboard press event
|
|
* The name of the key pressed is returned (0-9, a-z, A-Z, Up, Down, Left, Right, Shift_R, Control_L, space, Tab, ...)
|
|
* A pointer to the application and the entire GDK event are also returned
|
|
*/
|
|
void act_on_key_press(ezgl::application *application, GdkEventKey */*event*/, char *key_name)
|
|
{
|
|
application->update_message("Key Pressed");
|
|
|
|
std::cout << key_name <<" key is pressed" << std::endl;
|
|
}
|
|
|
|
/**
|
|
* A callback function to test the Test button
|
|
*/
|
|
void test_button(GtkWidget */*widget*/, ezgl::application *application)
|
|
{
|
|
// Update the status bar message
|
|
application->update_message("Test Button Pressed");
|
|
|
|
// Redraw the main canvas
|
|
application->refresh_drawing();
|
|
|
|
// Draw a temporary rectangle border
|
|
ezgl::renderer *g = application->get_renderer();
|
|
g->set_line_width(1);
|
|
g->set_color(ezgl::BLACK);
|
|
g->draw_rectangle({{0, 0}, 1100, 1150});
|
|
application->flush_drawing();
|
|
}
|
|
|