Σχεδίαση τόξων και κύκλων

Με το Cairo, η ίδια συνάρτηση χρησιμοποιείται για σχεδίαση τόξων, κύκλων ή ελλείψεων: Cairo::Context::arc(). Αυτή συνάρτηση παίρνει πέντε ορίσματα. Τα πρώτα δύο είναι οι συντεταγμένες του κεντρικού σημείου του τόξου, το τρίτο όρισμα είναι η ακτίνα του τόξου και τα τελευταία δυο ορίσματα ορίζουν την αρχή και τη γωνία τέλους του τόξου. Όλες οι γωνίες ορίζονται σε ακτίνια, έτσι η σχεδίαση ενός κύκλου είναι η ίδια με τη σχεδίαση ενός τόξου από 0 έως 2 * M_PI ακτίνια. Μια γωνία 0 είναι η κατεύθυνση του θετικού άξονα Χ (στον χώρο του χρήστη). Μια γωνία M_PI/2 ακτίνια (90 μοίρες) είναι στην κατεύθυνση του θετικού άξονα Υ (στον χώρο του χρήστη). Οι γωνίες αυξάνουν στην κατεύθυνση από τον θετικό άξονα Χ προς τον θετικό άξονα Υ. Έτσι, με τον προεπιλεγμένο πίνακα μετασχηματισμού, οι γωνίες αυξάνουν με δεξιόστροφη κατεύθυνση. (Να θυμάστε ότι θετικός άξονας Υ δείχνει προς τα κάτω.)

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();

17.4.1. Παράδειγμα

Ιδού ένα παράδειγμα απλού προγράμματος που σχεδιάζει ένα τόξο, έναν κύκλο και μια έλλειψη σε μια περιοχή σχεδίασης.

Φιγούρα 17-5Περιοχή σχεδίασης - Τόξα

Source Code

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:
  bool on_draw(const Cairo::RefPtr<Cairo::Context>& cr) override;
};

#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)
{
   auto 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);
}

Υπάρχουν κάποια πράγματα να σημειώσετε για αυτόν τον κώδικα του παραδείγματος. Πάλι, η μόνη πραγματική διαφορά μεταξύ αυτού του παραδείγματος και των προηγούμενων είναι η συνάρτηση on_draw(), έτσι θα περιορίσουμε την εστίασή μας σε αυτήν τη συνάρτηση. Επιπλέον, το πρώτο μέρος της συνάρτησης είναι σχεδόν ταυτόσημο με τα προηγούμενα παραδείγματα, έτσι θα παραλείψουμε αυτό το τμήμα.

Σημειώστε ότι σε αυτήν την περίπτωση, έχουμε εκφράσει σχεδόν καθετί ως προς το ύψος και το πλάτος του παραθύρου, συμπεριλαμβάνοντας το πλάτος των γραμμών. Λόγω αυτού, όταν αυξομειώνετε το παράθυρο, καθετί κλιμακώνεται με το παράθυρο. Επίσης, σημειώστε ότι υπάρχουν τρεις ενότητες σχεδίασης στη συνάρτηση και καθεμιά συσκευάζεται με ένα ζεύγος save()/restore(), έτσι ώστε να είμαστε πίσω σε μια γνωστή κατάσταση μετά από κάθε σχεδίαση.

Η ενότητα για σχεδίαση ενός τόξου εισάγει μια νέα συνάρτηση, close_path(). Αυτή η συνάρτηση θα σχεδιάσει στην πραγματικότητα μια ευθεία γραμμή από το τρέχον σημείο πίσω προς το πρώτο σημείο στη διαδρομή. Υπάρχει μια σημαντική διαφορά μεταξύ κλήσης της close_path() και χειροκίνητης σχεδίασης μιας γραμμής πίσω προς το αρχικό σημείο, όμως. Αν χρησιμοποιήσετε την close_path(), οι γραμμές θα ενωθούν ωραία μαζί. Αν χρησιμοποιήσετε την line_to(), οι γραμμές θα τερματίσουν στο ίδιο σημείο, αλλά το Cairo δεν θα κάνει καμιά ειδική ένωση.

Σχεδίαση αριστερόστροφα

Η συνάρτηση Cairo::Context::arc_negative() είναι ακριβώς η ίδια με την Cairo::Context::arc(), αλλά οι γωνίες πηγαίνουν προς την αντίθετη κατεύθυνση.