Tracé des arcs et des cercles

With Cairo, the same function is used to draw arcs, circles, or ellipses: Cairo::Context::arc(). This function takes five arguments. The first two are the coordinates of the center point of the arc, the third argument is the radius of the arc, and the final two arguments define the start and end angle of the arc. All angles are defined in radians, so drawing a circle is the same as drawing an arc from 0 to 2 * M_PI radians. An angle of 0 is in the direction of the positive X axis (in user-space). An angle of M_PI/2 radians (90 degrees) is in the direction of the positive Y axis (in user-space). Angles increase in the direction from the positive X axis toward the positive Y axis. So with the default transformation matrix, angles increase in a clockwise direction. (Remember that the positive Y axis points downwards.)

To draw an ellipse, you can scale the current transformation matrix by different amounts in the X and Y directions. For example, to draw an ellipse with center at x, y and size 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();

XVI.IV.I. Exemple

Voici un exemple de programme simple traçant un arc, un cercle et une ellipse sur une zone de dessin.

Figure XVI.5 Zone de dessin ‑ Arcs

Source Code

File: myarea.h (For use with gtkmm 4)

#ifndef GTKMM_EXAMPLE_MYAREA_H
#define GTKMM_EXAMPLE_MYAREA_H

#include <gtkmm/drawingarea.h>

class MyArea : public Gtk::DrawingArea
{
public:
  MyArea();
  virtual ~MyArea();

protected:
  void on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height);
};

#endif // GTKMM_EXAMPLE_MYAREA_H

File: main.cc (For use with gtkmm 4)

#include "myarea.h"
#include <gtkmm/application.h>
#include <gtkmm/window.h>

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();

protected:
  MyArea m_area;
};

ExampleWindow::ExampleWindow()
{
  set_title("DrawingArea");
  set_child(m_area);
}

int main(int argc, char** argv)
{
  auto app = Gtk::Application::create("org.gtkmm.example");

  return app->make_window_and_run<ExampleWindow>(argc, argv);
}

File: myarea.cc (For use with gtkmm 4)

#include "myarea.h"
#include <cairomm/context.h>
#include <cmath>

MyArea::MyArea()
{
  set_draw_func(sigc::mem_fun(*this, &MyArea::on_draw));
}

MyArea::~MyArea()
{
}

void MyArea::on_draw(const Cairo::RefPtr<Cairo::Context>& cr, int width, int height)
{
  const int lesser = std::min(width, height);

  // coordinates for the center of the window
  const int xc = width / 2;
  const int 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();
}

There are a couple of things to note about this example code. Again, the only real difference between this example and the previous ones is the on_draw() function, so we'll limit our focus to that function. In addition, the first part of the function is nearly identical to the previous examples, so we'll skip that portion.

Notez que dans ce cas, nous avons pratiquement tout exprimé en termes de hauteur et de largeur de la fenêtre, y compris la largeur des lignes. En raison de cela, quand vous redimensionnez la fenêtre, tous les objets se mettent à l'échelle. Également, il y a trois parties concernant les tracés dans la fonction et chacune est entourée avec une paire save()/restore() pour revenir à un état bien défini après chaque tracé.

La partie concernant le tracé de l'arc comporte une nouvelle fonction : close_path(). Cette fonction a pour effet de provoquer le tracé d'une droite entre le point de fin et de début du tracé. Il y a toutefois une différence significative entre l'appel de close_path() et le tracé à la main d'une ligne jusqu'au point de départ. Si vous utilisez close_path(), les lignes seront proprement jointes. Si vous utilisez line_to() à la place, les lignes se rejoindront, mais Cairo ne fera rien de spécial pour les joindre.

Tracé en sens inverse des aiguilles d'une montre

La fonction Cairo::Context::arc_negative() est identique à Cairo::Context::arc() mais les angles tournent dans le sens opposé.