LinuxQuestions.org
Share your knowledge at the LQ Wiki.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-14-2024, 01:18 PM   #1
hazel
LQ Guru
 
Registered: Mar 2016
Location: Harrow, UK
Distribution: LFS, AntiX, Slackware
Posts: 7,583
Blog Entries: 19

Rep: Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454
Drawing arcs in cairo


I'm trying to teach myself basic cairo programming but I have got stuck on arcs. The official cairo manual says:
Quote:
Angles are measured in radians. An angle of 0.0 is in the direction of the positive X axis (in user space). An angle of M_PI/2.0 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.
This seems to me contradictory, both in itself and with my experiments. If (+)M_PI/2 is the positive Y-axis, then an arc from 0 (the positive X axis) to M_PI/2 should go anti-clockwise, i.e. above the X-axis. But it does in fact go clockwise underneath the X-axis. What am I missing?

Last edited by hazel; 03-14-2024 at 01:22 PM.
 
Old 03-14-2024, 03:01 PM   #2
Michael Uplawski
Senior Member
 
Registered: Dec 2015
Posts: 1,622
Blog Entries: 40

Rep: Reputation: Disabled
Quote:
Originally Posted by hazel View Post
I'm trying to teach myself basic cairo programming but I have got stuck on arcs. The official cairo manual says:

This seems to me contradictory, both in itself and with my experiments. If (+)M_PI/2 is the positive Y-axis, then an arc from 0 (the positive X axis) to M_PI/2 should go anti-clockwise, i.e. above the X-axis. But it does in fact go clockwise underneath the X-axis. What am I missing?
I am not sure, Hazel, but the direction of the Y-axis may be downwards.
 
Old 03-14-2024, 03:36 PM   #3
boughtonp
Senior Member
 
Registered: Feb 2007
Location: UK
Distribution: Debian
Posts: 3,604

Rep: Reputation: 2547Reputation: 2547Reputation: 2547Reputation: 2547Reputation: 2547Reputation: 2547Reputation: 2547Reputation: 2547Reputation: 2547Reputation: 2547Reputation: 2547

Taking "positive X axis" = east/right and "positive Y axis" = south/down you get...
Quote:
Angles are measured in radians. An angle of 0.0 is [east]. An angle of M_PI/2.0 radians (90 degrees) is [south]. Angles increase in the direction from the [east] toward the [south]. So with the default transformation matrix, angles increase in a clockwise direction.
...which seems consistent to me.

 
Old 03-14-2024, 04:41 PM   #4
astrogeek
Moderator
 
Registered: Oct 2008
Distribution: Slackware [64]-X.{0|1|2|37|-current} ::12<=X<=15, FreeBSD_12{.0|.1}
Posts: 6,266
Blog Entries: 24

Rep: Reputation: 4195Reputation: 4195Reputation: 4195Reputation: 4195Reputation: 4195Reputation: 4195Reputation: 4195Reputation: 4195Reputation: 4195Reputation: 4195Reputation: 4195
Indeed, as already said, on most (all?) computer displays, the origin (0,0) is the upper left corner and positive Y-axis is downwards.

I have wrestled with that off and on over the years as it does reverse the conventional meaning of polar rotations.
 
1 members found this post helpful.
Old 03-14-2024, 08:36 PM   #5
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,138
Blog Entries: 6

Rep: Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827
Quote:
I'm trying to teach myself basic cairo programming but I have got stuck on arcs
Did that a while back hazel. Here is a working example that you can pick apart. I updated to current gtk3 syntax, and compiled with no errors.

clock.c
Code:
//gcc -lm clock.c -o clock $(pkg-config --cflags --libs gtk+-3.0)

#include <math.h>
#include <cairo.h>
#include <gtk/gtk.h>
#include <time.h>

#define WINDOW_WIDTH  800
#define WINDOW_HEIGHT 800

#define WHITE   255, 255, 255
#define RED     255, 0, 0
#define CYAN    0, 255, 255
#define WIDTH   3

static char buffer[256];
static time_t curtime;
static struct tm *loctime;
static int seconds;
static int minutes;
static int hours;
static int radius;
static int movingSecondsEffect = FALSE;

static void quit_application(gpointer app) {
    g_application_quit (G_APPLICATION (app));
}

static void clicked(GtkWindow *window, GdkEventButton *event, gpointer user_data) {
    gtk_window_set_decorated(window, !gtk_window_get_decorated(window));
}

//Hours ticks
void HourTick (GtkWidget *widget, cairo_t *cr, int nHour, int cx, int cy) {
    double dRadians = nHour * 3.14 / 6.0;
    int x1 = cx+(int) ((0.9 * radius * sin (dRadians)));
    int y1 = cy-(int) ((0.9 * radius * cos (dRadians)));
    int x2 = cx+(int) ((1.0 * radius * sin (dRadians)));
    int y2 = cy-(int) ((1.0 * radius * cos (dRadians)));

    cairo_set_source_rgb(cr, CYAN);
    cairo_set_line_width(cr, WIDTH);
    cairo_move_to(cr, x1, y1);
    cairo_line_to(cr, x2, y2);
    cairo_stroke(cr);
}

//Minute ticks
void MinTick (GtkWidget *widget, cairo_t *cr, int nHour, int cx, int cy) {   
    double dRadians2 = nHour * 3.14 / 30.0;
    int a1 = cx+(int) ((0.97 * radius * sin (dRadians2)));
    int b1 = cy-(int) ((0.97 * radius * cos (dRadians2)));
    int a2 = cx+(int) ((1.0 * radius * sin (dRadians2)));
    int b2 = cy-(int) ((1.0 * radius * cos (dRadians2)));

    cairo_set_source_rgb(cr, CYAN);
    cairo_set_line_width(cr, WIDTH);
    cairo_move_to(cr, a1, b1);
    cairo_line_to(cr, a2, b2);
    cairo_stroke(cr);
}   

void DrawSeconds (GtkWidget *widget, cairo_t *cr, int cx, int cy) {
    /* --- Get radians from seconds --- */
    float dRadians = seconds * 3.14 / 30.0;

    /* --- Draw seconds --- */
    cairo_set_source_rgb(cr, RED);
    cairo_set_line_width(cr, WIDTH);
    cairo_move_to(cr, cx, cy);
    cairo_line_to(cr, cx + (0.9 * radius * sin (dRadians)),
                   cy - (0.9 * radius * cos (dRadians)));
    cairo_stroke(cr);
}

void DrawMinutes (GtkWidget *widget, cairo_t *cr, int cx, int cy) {
    /* --- Get radians from seconds --- */
    float dRadians = (minutes * 3.14 / 30.0) + (seconds * 3.14 / 1800.0);

    /* --- Draw seconds --- */
    cairo_set_source_rgb(cr, CYAN);
    cairo_set_line_width(cr, WIDTH * 2);
    cairo_move_to(cr, cx, cy);
    cairo_line_to(cr, cx + (0.8 * radius * sin (dRadians)),
                   cy - (0.8 * radius * cos (dRadians)));
    cairo_stroke(cr);
}

void DrawHours (GtkWidget *widget, cairo_t *cr, int cx, int cy) {
   /* --- Get radians from seconds --- */
   float dRadians = ((hours % 12) * 3.14 / 6.0) + (minutes * 3.14 / 360.0);
    
   /* --- Draw seconds --- */
   cairo_set_source_rgb(cr, CYAN);
   cairo_set_line_width(cr, WIDTH * 3);
   cairo_move_to(cr, cx, cy);
   cairo_line_to(cr, cx + (0.6 * radius * sin (dRadians)),
                   cy - (0.6 * radius * cos (dRadians)));
   cairo_stroke(cr);
}

static gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data) {
    int midx = gtk_widget_get_allocated_width(widget)/2;
    int midy = gtk_widget_get_allocated_height(widget)/2;
    int nHour;
    
    /* --- Draw Tickmarks at each hour/ minute --- */
    for (nHour = 1; nHour <= 60; nHour++) {
        MinTick(widget, cr, nHour, midx, midy);
        if(nHour <= 12) {
            HourTick(widget, cr, nHour, midx, midy);
        }
    }
    
    cairo_set_source_rgb(cr, WHITE);
    cairo_set_font_size (cr, 50);
    cairo_move_to(cr, midx/1.4, midy/2);
    cairo_show_text(cr, buffer);
    radius = MIN (midx, midy) -2 ;
    cairo_move_to(cr, midx , 0);

    DrawSeconds (widget, cr, midx, midy);
    DrawMinutes (widget, cr, midx, midy);
    DrawHours (widget, cr, midx, midy);

    return FALSE;
}

static gint time_handler (GtkWidget *widget) {
    curtime = time(NULL);
    loctime = localtime(&curtime);
    seconds = loctime->tm_sec;
    minutes = loctime->tm_min;
    hours   = loctime->tm_hour;

    strftime(buffer, 256, "%T", loctime);

    gtk_widget_queue_draw(widget);

    return TRUE;
}

static void activate (GtkApplication* app, gpointer user_data) {
    GtkWidget *window;
    GtkWidget *frame;
    GtkWidget *drawing_area;
    GtkWidget *ebox;

    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "Hazels' Cairo Clock");
    gtk_window_set_default_size (GTK_WINDOW (window), WINDOW_WIDTH, WINDOW_HEIGHT);
    gtk_container_set_border_width (GTK_CONTAINER (window), 0);
    gtk_widget_set_app_paintable(window, TRUE);

    GdkScreen *screen = gtk_widget_get_screen(window);
    GdkVisual *visual = gdk_screen_get_rgba_visual(screen);
    gtk_widget_set_visual(window, visual);

    ebox = gtk_event_box_new();
    gtk_container_add(GTK_CONTAINER(window), ebox);

    drawing_area = gtk_drawing_area_new();
    gtk_widget_set_size_request (drawing_area, WINDOW_WIDTH - 20 , WINDOW_HEIGHT - 20);
    gtk_container_add (GTK_CONTAINER (ebox), drawing_area);

    g_signal_connect (drawing_area, "draw", G_CALLBACK (draw_cb), NULL);
    g_signal_connect (drawing_area, "delete-event", G_CALLBACK (gtk_main_quit), NULL);

    gtk_widget_add_events(window, GDK_BUTTON_PRESS_MASK);
    g_signal_connect(window, "button-press-event", G_CALLBACK(clicked), NULL);
    g_signal_connect_swapped(G_OBJECT(window), "destroy",
      G_CALLBACK(quit_application), app);
    g_timeout_add (1000, (GSourceFunc) time_handler, drawing_area);
    gtk_widget_show_all (window);
}

int main (int argc, char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new("org.cairo.clock", G_APPLICATION_DEFAULT_FLAGS);
    
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);

    status = g_application_run(G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}
Edit: Removed 3 functions that werent needed.

Last edited by teckk; 03-14-2024 at 08:47 PM.
 
1 members found this post helpful.
Old 03-15-2024, 01:21 AM   #6
hazel
LQ Guru
 
Registered: Mar 2016
Location: Harrow, UK
Distribution: LFS, AntiX, Slackware
Posts: 7,583

Original Poster
Blog Entries: 19

Rep: Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454
Yes, but why on earth should the "positive" Y-axis be defined as the one that points downwards? The origin represents Y=0, and the Y-values plotted on the axis that points downward are negative. Positive values are on the upward-pointing Y-axis.

@astrogeek: Oh, I get a glimmer now after rereading your post. It's because of the longstanding convention about screen (and window) coordinates starting at the top left-hand corner, where down and right are the only available directions. But the cairo people really should have stated this explicitly as a parallel since it's far from obvious to the naive reader and their explanation makes no sense without it. Or do the words "(in user space)" do precisely that for those familiar with the terminology? (see appendix below)

@teckk I shall copy your program and examine it at leisure.

Appendix: So I googled "user space" and got some Java stuff like:
Quote:
Originally Posted by O'Reilly, Java Media APIs
User space is the coordinate space in which the user operates. At instantiation, the origin of user space is at the top-left corner of the screen with the x coordinate increasing to the right and the y coordinate increasing downward.

Last edited by hazel; 03-15-2024 at 01:53 AM. Reason: Added appendix
 
Old 03-15-2024, 01:49 AM   #7
Michael Uplawski
Senior Member
 
Registered: Dec 2015
Posts: 1,622
Blog Entries: 40

Rep: Reputation: Disabled
Quote:
Originally Posted by hazel View Post
Yes, but why on earth should the "positive" Y-axis be defined as the one that points downwards? The origin represents Y=0, and the Y-values plotted on the axis that points downward are negative. Positive values are on the upward-pointing Y-axis.
Writing Web-Applications in Europe, we oftentimes had to wonder how on earth Sunday can be considered the first day of the week. In the end, you seal your brain and adapt.
 
1 members found this post helpful.
Old 03-15-2024, 05:16 AM   #8
hazel
LQ Guru
 
Registered: Mar 2016
Location: Harrow, UK
Distribution: LFS, AntiX, Slackware
Posts: 7,583

Original Poster
Blog Entries: 19

Rep: Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454
@teckk I can't get your program to compile. ftr I am using:
Code:
 gcc `pkg-config --cflags cairo,gtk+-3.0  --libs cairo,gtk+-3.0` teckks_clock.c
It complains about not finding G_APPLICATION_DEFAULT_FLAGS. What do I need to add?
 
Old 03-15-2024, 08:08 AM   #9
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,138
Blog Entries: 6

Rep: Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827
How old is your version of gtk3? I'm on 3.24.41

If it is older then Line 182:
Code:
app = gtk_application_new("org.cairo.clock", G_APPLICATION_DEFAULT_FLAGS);
Needs to be:
Code:
app = gtk_application_new("org.cairo.clock", G_APPLICATION_FLAGS_NONE);
"They" change the syntax on those tools kits every now and then.
 
1 members found this post helpful.
Old 03-15-2024, 08:44 AM   #10
hazel
LQ Guru
 
Registered: Mar 2016
Location: Harrow, UK
Distribution: LFS, AntiX, Slackware
Posts: 7,583

Original Poster
Blog Entries: 19

Rep: Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454
Quote:
Originally Posted by teckk View Post
How old is your version of gtk3? I'm on 3.24.41
Mine is 3.24.31. So I made the change and now gcc works OK but then ld spits out some undefined symbol in libm.
Code:
/usr/bin/ld: /tmp/ccpTOwHV.o: undefined reference to symbol 'sin@@GLIBC_2.2.5'
/usr/bin/ld: /lib64/libm.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
Never mind. I can still study the source, which I assume was the point of your post.
 
Old 03-15-2024, 08:56 AM   #11
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,866
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
Add `-lm` to the end of linkage.
 
1 members found this post helpful.
Old 03-15-2024, 10:15 AM   #12
hazel
LQ Guru
 
Registered: Mar 2016
Location: Harrow, UK
Distribution: LFS, AntiX, Slackware
Posts: 7,583

Original Poster
Blog Entries: 19

Rep: Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454
That is absolutely amazing! Thank you Teckk and NevemTeve too. I'll add the program to my fluxbox startup file so that I can use it as active wallpaper. But I also intend to study the code; I'm sure there's a lot that I can learn from it.


Just for interest, here is my version of a tenor clef, completed today based on what I learned from this thread.
Attached Files
File Type: txt alternor.c.txt (1.2 KB, 4 views)

Last edited by hazel; 03-15-2024 at 11:29 AM.
 
Old 03-15-2024, 12:21 PM   #13
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,863

Rep: Reputation: 7311Reputation: 7311Reputation: 7311Reputation: 7311Reputation: 7311Reputation: 7311Reputation: 7311Reputation: 7311Reputation: 7311Reputation: 7311Reputation: 7311
Quote:
Originally Posted by hazel View Post
Yes, but why on earth should the "positive" Y-axis be defined as the one that points downwards?
Historical reasons. But think about a regular book, the first line is at the top, and the Y axis (line numbers) are counted from top to bottom. In a text editor the characters are counted from top left corner. It is exactly the same.
 
Old 03-15-2024, 01:44 PM   #14
teckk
LQ Guru
 
Registered: Oct 2004
Distribution: Arch
Posts: 5,138
Blog Entries: 6

Rep: Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827Reputation: 1827
Yup.

alternor.c
Code:
//gcc -lm alternor.c -o alternor $(pkg-config --cflags --libs gtk+-3.0)

#include <cairo.h>
#include <math.h>

#define NOTEWIDTH 5
#define STEMLENGTH 25

int main (int argc, char *argv[])
{
	cairo_surface_t *surface;
	cairo_t *cr;
	int x, y;
    surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 240, 80);
    cr = cairo_create (surface);

    /* Make surface white */
	cairo_set_source_rgb (cr, 1, 1, 1);
	cairo_paint (cr);
    /* Drawing code goes here */
	x = 120;
	y = 40;

 	cairo_set_line_width (cr, 2);
    cairo_set_source_rgb (cr, 0, 0, 0);

    /*Body of clef...  */ 
	cairo_rectangle (cr, x-1,y-4*NOTEWIDTH, 2,8*NOTEWIDTH);
	cairo_fill (cr);
	
	cairo_move_to (cr, x+3,y-4*NOTEWIDTH);
	cairo_line_to (cr, x+3,y+4*NOTEWIDTH);
	cairo_stroke (cr);
	
	cairo_arc (cr, x+3*NOTEWIDTH,y-2*NOTEWIDTH, 2*NOTEWIDTH,
			-7*M_PI/12,3*M_PI/4);
	cairo_line_to (cr, x+8,y+5);
	
    /*	cairo_arc (cr, x+3*NOTEWIDTH,y+2*NOTEWIDTH, 2*NOTEWIDTH, 
        3*M_PI/4, 3*M_PI/12);  */
            
    cairo_arc (cr, x+3*NOTEWIDTH,y+2*NOTEWIDTH, 2*NOTEWIDTH, -7*M_PI/12,3*M_PI/4);	
	cairo_stroke (cr);

    /* Write output and clean up */
    cairo_surface_write_to_png (surface, "clef.png");
    cairo_surface_destroy (surface);
    cairo_destroy (cr);
        return 0;
}
And that writes to clef.png
Code:
curl -F 'file=@'clef.png'' https://0x0.st
https://0x0.st/HFO1.png
 
Old 03-16-2024, 01:11 AM   #15
hazel
LQ Guru
 
Registered: Mar 2016
Location: Harrow, UK
Distribution: LFS, AntiX, Slackware
Posts: 7,583

Original Poster
Blog Entries: 19

Rep: Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454Reputation: 4454
Thank you, Teckk. I didn't know about that site. This particular symbol btw can be used either as an alto or a tenor clef depending how high it is on the stave, hence the name.

In case anyone is wondering why I want to draw clefs, it is part of a program I wrote years ago as a personal front end to Philip's Music Writer (pmw), which is a brilliantly simple way of producing script music using a text code. pmwScribe gives you a screen stave on which you can pin notes and other musical glyphs and it translates them into pmw code. I wrote it using gtk2/gdk2 and I'm now trying to convert it into gtk3/cairo.

Last edited by hazel; 03-16-2024 at 01:30 AM.
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Cairo build fails at cairo-analysis-surface.lo CME2 Linux - Newbie 2 12-15-2017 08:10 PM
compiling with gcov flags -fprofile-arcs and -ftest-coverage fails. sanjay_mishra Linux - General 1 12-02-2009 03:06 AM
pckage "cairo" required by cairo" not found barunparichha Linux - Software 4 06-25-2008 08:29 AM
Map drawing software Citizen Bleys Linux - General 1 02-13-2002 03:01 PM
Vector Drawing & Publishing Software PuterFreaK Linux - General 3 12-22-2001 01:58 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 02:43 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration