Dibujar arcos y círculos
Con Cairo, la misma función se usa para dibujar arcos, círculos, o elipses: Cairo::Context::arc(). Esta función toma cinco argumentos. Los dos primeros son las coordenadas del punto central del arco, el tercer argumento es el radio del arco, y los últimos dos argumentos definen el ángulo de inicio y fin del arco. Todos los ángulos se definen en radianes, por lo que dibujar un círculo es lo mismo que dibujar un arco de 0 a 2 * M_PI radianes. Un ángulo de 0 está en la dirección del eje positivo de X (en espacio de usuario). Un ángulo de M_PI/2 radianes (90 grados) está en la dirección del eje positivo de Y (en espacio de usuario). Los ángulos se incrementan en la dirección del eje positivo de X hacia el eje positivo de Y, por lo que con la matriz de transformación predeterminada, los ángulos incrementan en la dirección de las agujas del reloj (recuerde que el eje positivo de Y apunta hacia abajo).
Para dibujar una elipse, puede escalar la matriz de transformación actual en cantidades diferentes en las direcciones de X e Y. Por ejemplo, para dibujar una elipse con un centro en x, y, y de tamaño width, height:
context->save(); context->translate(x, y); context->scale(width / 2.0, height / 2.0); context->arc(0.0, 0.0, 1.0, 0.0, 2 * M_PI); context->restore();
- 17.4.1. Ejemplo
17.4.1. Ejemplo
Aquí hay un programa simple de ejemplo que dibuja un arco, un círculo, y una elipse en un área de dibujo.
File: myarea.h (For use with gtkmm 3, not gtkmm 2)
#ifndef GTKMM_EXAMPLE_MYAREA_H #define GTKMM_EXAMPLE_MYAREA_H #include <gtkmm/drawingarea.h> class MyArea : public Gtk::DrawingArea { public: MyArea(); virtual ~MyArea(); protected: //Override default signal handler: virtual bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr); }; #endif // GTKMM_EXAMPLE_MYAREA_H
File: myarea.cc (For use with gtkmm 3, not gtkmm 2)
#include "myarea.h" #include <cairomm/context.h> #include <cmath> MyArea::MyArea() { } MyArea::~MyArea() { } bool MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr) { // This is where we draw on the window Gtk::Allocation allocation = get_allocation(); const int width = allocation.get_width(); const int height = allocation.get_height(); const int lesser = MIN(width, height); // coordinates for the center of the window int xc, yc; xc = width / 2; yc = height / 2; cr->set_line_width(lesser * 0.02); // outline thickness changes // with window size // first draw a simple unclosed arc cr->save(); cr->arc(width / 3.0, height / 4.0, lesser / 4.0, -(M_PI / 5.0), M_PI); cr->close_path(); // line back to start point cr->set_source_rgb(0.0, 0.8, 0.0); cr->fill_preserve(); cr->restore(); // back to opaque black cr->stroke(); // outline it // now draw a circle cr->save(); cr->arc(xc, yc, lesser / 4.0, 0.0, 2.0 * M_PI); // full circle cr->set_source_rgba(0.0, 0.0, 0.8, 0.6); // partially translucent cr->fill_preserve(); cr->restore(); // back to opaque black cr->stroke(); // and finally an ellipse double ex, ey, ew, eh; // center of ellipse ex = xc; ey = 3.0 * height / 4.0; // ellipse dimensions ew = 3.0 * width / 4.0; eh = height / 3.0; cr->save(); cr->translate(ex, ey); // make (ex, ey) == (0, 0) cr->scale(ew / 2.0, eh / 2.0); // for width: ew / 2.0 == 1.0 // for height: eh / 2.0 == 1.0 cr->arc(0.0, 0.0, 1.0, 0.0, 2 * M_PI); // 'circle' centered at (0, 0) // with 'radius' of 1.0 cr->set_source_rgba(0.8, 0.0, 0.0, 0.7); cr->fill_preserve(); cr->restore(); // back to opaque black cr->stroke(); return true; }
File: main.cc (For use with gtkmm 3, not gtkmm 2)
#include "myarea.h" #include <gtkmm/application.h> #include <gtkmm/window.h> int main(int argc, char** argv) { Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example"); Gtk::Window win; win.set_title("DrawingArea"); MyArea area; win.add(area); area.show(); return app->run(win); }
Hay un par de cosas que debe tener en cuenta acerca de este código de ejemplo. Nuevamente, la única diferencia real entre este ejemplo y los anteriores es la función on_draw(), por lo que nos limitaremos a trabajar esa función. Además, la primera parte de la función es casi idéntica a la de los ejemplos previos, por lo que se omitirá ese fragmento.
Tenga en cuenta que, en este caso, se ha expresado casi todo en términos de anchura y altura de la ventana, incluyendo la anchura de las líneas. Es por esto que, cuando cambie el tamaño de la ventana, todo se escalará a ella. Además, tenga en cuenta que hay tres secciones de dibujo en la función, y cada una está envuelta en un par save()/restore() para volver a un estado conocido después de cada dibujo.
La sección para dibujar un arco presenta una nueva función, close_path(). Esta función, en efecto, dibujará una línea recta desde el punto actual de vuelta al primer punto en el camino. Sin embargo, hay una diferencia significativa entre llamar a close_path() y dibujar una línea manualmente al punto de inicio. Si usa close_path(), las líneas se juntarán suavemente. Si usa line_to() en su lugar, las líneas terminarán en el mismo lugar, pero Cairo no las juntará de manera especial.
La función Cairo::Context::arc_negative() es exactamente la misma que Cairo::Context::arc(), pero los ángulos van en la dirección opuesta.