/* -----------------------------------------------------------------------
* Από: migf1
*
* Πολύ απλοϊκό παράδειγμα προγράμματος κονσόλας που παράγει στο 1ο καρτεσιανό
* τεταρτημόριο τα εξής:
* α) το γράφημα μιας συνάρτησης: f(x) = a*x + b
* β) μια γραμμή μεταξύ δυο σημείων: (x1,y1) και (x2,y2)
* γ) έναν κύκλο με κέντρο στο σημείο: (x,y) και ακτίνα radius
* δ) μια έλλειψη που οριοθετείται από παραλληλόγραμμο με αντικριστές κορυφές
* τα σημεία: (x1,y1) πάνω αριστερά και (x2,y2) κάτω δεξιά
*
* Μπορείτε να αλλάξετε τιμές σε οποιοδήποτε από τα x, pt2.x, x2, pt2.y, y2 & radius,
* αλλά σημειώστε πως το πρόγραμμα απεικονίζει μονάχα όσα σημεία παράγονται στην
* περιοχή: (0,0) ... (74,22)
*
* ΣΗΜΕΙΩΣΕΙΣ ΥΛΟΠΟΙΗΣΗΣ:
* Οι αλγόριθμοι για τον σχεδιασμό της γραμμής του κύκλου και της έλλειψης
* είναι ΕΤΟΙΜΕΣ υλοποιήσεις των αλγορίθμων Bresenham, που χρησιμοποιήσα
* ατόφιους (με μόνη αλλαγή την προσαρμογή τους στις δομές δεδομέννων Screen
* και Point του παρόντος προγράμματος).
*
* Δείτε επίσης:
* http://f...content-available-to-author-only...s.at/easyfilter/bresenham.html
* http://e...content-available-to-author-only...a.org/wiki/Midpoint_circle_algorithm
*
* Tο πρόγραμμα δεν κάνει ούτε scaling ούτε κανονικό clipping και απεικονίζει
* ΜΟΝΑΧΑ το 1ο τεταρτημόριο του καρτεσιανού επιπέδου, με το σημείο (0,0) να
* βρίσκεται στην κάτω αριστερή μεριά της οθόνης.
* Ο κύριος σκοπός για τον οποίον γράφτηκε το πρόγραμμα ήταν η παρουσίαση μιας
* απλοϊκής υλοποίησης buffered σχεδιασμού (οι συναρτήσεις αναβοσβήνουν τα
* κατάλληλα pixels μέσα στο buffer, το οποίο κατόπιν τυπώνεται στην οθόνη
* σειρά προς σειρά, μεσω της συνάρτησης: draw_screen() ).
* -----------------------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h> // for exit()
#include <string.h> // for memset()
#define MAXCOLS 75 // μέγιστο πλάτος της οθόνης μας
#define MAXROWS 23 // μέγιστο ύψος της οθόνης μας
#define PIXEL_ON '#' // χαρακτήρας απεικόνισης "αναμμένων" pixels
#define PIXEL_OFF '.' // χαρακτήρας απεικόνισης "σβησμένων" pixels
// έλεγχος για pixels εκτός ορίων
#define VALIDPIXEL(x,y, w,h) ( (y) > -1 && (y) < (h) && (x) > -1 && (x) < (w) )
#define REVAXIS(y,h) (h) - (y) - 1 // προσαρμόζει την συντεταγμένη y ώστε το σημείο
// που της αντιστοιχεί να απεικονιστεί στην
// αντεστραμμένη φορά του άξονά (χρήσιμο για τον
// άξονα Υ που τον θέλουμε να μετράει από κάτω
// προς τα πάνω)
#define ABS(x) ( (x) < 0 ? -(x) : (x) )
typedef struct point { // δομή καρτεσιανού σημείου
int x; // καρτεσιανή συντεταγμένη x του σημείου
int y; // καρτεσιανή συντεταγμένη y του σημείου
} Point;
typedef struct screen { // δομή για το buffer της οθόνης μας
int nrows; // τρέχον πλάτος της οθόνης μας
int ncols; // τρέχον ύψος της οθόνης μας
char pixel[MAXROWS][MAXCOLS]; // το buffer της οθόνης μας
} Screen;
// ------------------------------------------------------------------------------------
// set_pixel:
//
// "Ανάβει" το pixel x,y στο buffer της οθόνης. Θα εμφανιστεί αναμένο όταν αργότερα
// τυπωθεί το buffer μέσω της συνάρτησης: draw_screen
//
void set_pixel( Screen *screen, int x, int y )
{
// απόρριψη pixels που βγαίνουν εκτός οθόνης
if ( !VALIDPIXEL(x,y, screen->ncols, screen->nrows ) )
return;
// προσαρμογή του y ώστε το μέτρημα για την
y = REVAXIS(y, screen->nrows); // απεικόνιση να ξεκινά από κάτω προς τα επανω
screen->pixel[y][x] = PIXEL_ON;
return;
}
// ------------------------------------------------------------------------------------
// Η συνάρτηση που παράγει το 1ο γράφημα, δοκιμάστε και με άλλες τιμές
//
int f( int x )
{
return x/3 + 1;
}
// ------------------------------------------------------------------------------------
// set_f():
//
// "Ανάβει" τα pixels που αντιστοιχούν στο γράφημα της συνάρτησης f(), στο buffer της
// οθόνης (θα εμφανιστούν αναμμένα όταν αργότερα τυπωθεί το buffer μέσω της συνάρτησης:
// draw_screen() )
//
void set_f( Screen *screen )
{
register int x;
for (x=0; x < screen->ncols; x++)
set_pixel( screen, x, f(x) );
return;
}
// ------------------------------------------------------------------------------------
// set_line():
//
// "Ανάβει" τα pixels της γραμμής που οριοθετείται από τα σημεία pt1 και pt2, στο
// buffer της οθόνης (θα εμφανιστούν αναμμένα όταν αργότερα τυπωθεί το buffer μέσω
// της συνάρτησης: draw_screen() )
//
void set_line( Screen *screen, Point pt1, Point pt2)
{
int dx = ABS( pt2.x - pt1.x );
int dy = -ABS( pt2.y - pt1.y );
int sx = (pt1.x < pt2.x) ? 1 : -1;
int sy = (pt1.y < pt2.y) ? 1 : -1;
int err = dx + dy, e2; // error value e_xy
for (;;)
{
set_pixel(screen, pt1.x, pt1.y);
if ( pt1.x == pt2.x && pt1.y == pt2.y )
break;
e2 = 2 * err;
if (e2 >= dy) { // e_xy + e_x > 0
err += dy;
pt1.x += sx;
}
if (e2 <= dx) { // e_xy + e_y < 0 */
err += dx;
pt1.y += sy;
}
}
return;
}
// ------------------------------------------------------------------------------------
// set_circle():
//
// "Ανάβει" τα pixels της περιφέρειας του κύκλου με κέντρο το σημείο: center και ακτίνα
// radius, στο buffer της οθόνης (θα εμφανιστούν αναμμένα όταν αργότερα τυπωθεί το
// buffer μέσω της συνάρτησης: draw_screen() )
//
void set_circle(Screen *screen, Point center, int radius)
{
int x = -radius, y = 0, err = 2-2*radius; // II. Quadrant
do {
set_pixel(screen, center.x-x, center.y+y); // I. Quadrant
set_pixel(screen, center.x-y, center.y-x); // II. Quadrant
set_pixel(screen, center.x+x, center.y-y); // III. Quadrant
set_pixel(screen, center.x+y, center.y+x); // IV. Quadrant
radius = err;
if (radius > x) // e_xy + e_x > 0
err += ++x * 2 + 1;
if (radius <= y)
err += ++y * 2 + 1; // e_xy + e_y < 0
} while (x < 0);
return;
}
// ------------------------------------------------------------------------------------
void set_ellipse( Screen *screen, Point rsta, Point rend )
{
// values of diameter
int a = ABS( rend.x - rsta.x );
int b = ABS( rend.y - rsta.y );
int b1 = b & 1;
// error increment
long dx = 4 * (1-a) * b * b;
long dy = 4 * (b1+1) *a * a;
// error of 1.step
long err = dx + dy + b1 * a * a;
long e2;
if (rsta.x > rend.x) { // if called with swapped points
rsta.x = rend.x;
rend.x += a;
}
if (rsta.y > rend.y) // ... exchange them
rsta.y = rend.y;
// starting pixel
rsta.y += (b+1)/2;
rend.y = rsta.y-b1;
a *= 8 * a;
b1 = 8 * b * b;
do {
set_pixel(screen, rend.x, rsta.y); // I. Quadrant
set_pixel(screen, rsta.x, rsta.y); // II. Quadrant
set_pixel(screen, rsta.x, rend.y); // III. Quadrant
set_pixel(screen, rend.x, rend.y); // IV. Quadrant
e2 = 2 * err;
if (e2 >= dx) { // x step
rsta.x++;
rend.x--;
err += dx += b1;
}
if (e2 <= dy) { // y step
rsta.y++;
rend.y--;
err += dy += a;
}
} while ( rsta.x <= rend.x );
while ( rsta.y - rend.y < b ) { // too early stop of flat ellipses a=1
set_pixel(screen, rsta.x - 1, rsta.y);// -> finish tip of ellipse
set_pixel(screen, rend.x + 1, rsta.y++);
set_pixel(screen, rsta.x - 1, rend.y);
set_pixel(screen, rend.x + 1, rend.y--);
}
return;
}
// ------------------------------------------------------------------------------------
// "Σβήνει" όλα τα pixels στο buffer της οθόνης (α εμφανιστούν σβησμένα όταν αργότερα
// τυπωθεί η οθόνη μέσω της συνάρτησης: draw_screen() )
//
void clear_screen( Screen *screen )
{
// γρήγορος κώδικας
memset( &screen
->pixel
, PIXEL_OFF
, screen
->nrows
* screen
->ncols
* sizeof(char));
/*** DISABLED: αργός εναλλακτικός κώδικας, αλλά ενδεχομένως πιο συμβατός
register int x,y;
for (y=0; y < screen->nrows; y++)
for (x=0; x < screen->ncols; x++)
screen->pixel[y][x] = PIXEL_OFF;
***/
return;
}
// ------------------------------------------------------------------------------------
// draw_screen():
//
// Τυπώνει όλα τα pixels του buffer (αναμμένα και σβηστά) καθώς και την αρίθμηση
// των αξόνων Χ και Υ
//
void draw_screen( Screen screen )
{
register int x, y;
for (y=0; y < screen.nrows; y++)
{
// τύπωμα ετικετών αριστερά από τον άξονα Υ
int ylab = y;
printf("%3d ", REVAXIS
(ylab
, screen.
nrows) );
// τύπωμα των pixels της y-οστής σειράς του buffer
for (x=0; x < screen.ncols; x++)
}
// τύπωμα ετικετών κάτω από τον άξονα Χ
for (x=0; x < screen.ncols; x++)
return;
}
// ------------------------------------------------------------------------------------
int main( void )
{
char s[255+1] = ""; // βοηθητικό string (άνευ σημασίας)
/*
* ορισμός του buffer της οθόνης & αρχικοποίηση των τρεχουσών διαστάσεών του
*/
Screen screen = { .ncols = MAXCOLS, .nrows = MAXROWS };
/*
* Σχεδίαση γραφήματος της συνάρτησης: f(x) = a*x+b
* (πειραματιστείτε με διάφορες τιμές των a και b, στη γραμμή 69)
*/
printf("> press ENTER for graph of: f(x) = x/3+1 ..."); clear_screen( &screen ); // σβήσιμο όλων των pixels στο buffer
set_f( &screen); // σχεδίαση της f(x) στο buffer
draw_screen( screen ); // τύπωμα όλων των pixels του buffer
/*
* Σχεδίαση γραμμής μεταξύ 2 σημείων: pt1 και pt2
* (πειραματιστείτε με διάφορες τιμές)
*/
// τα σημεία που οριοθετούν τη γραμμή
Point pt1 = { .x=2, .y=14 }, pt2 = { .x=64, .y=2};
printf( "> press ENTER for line between points (%d,%d) and (%d,%d)...", pt1.x, pt1.y, pt2.x, pt2.y
);
clear_screen( &screen ); // σβήσιμο όλων των pixels στο buffer
set_line( &screen, pt1, pt2 ); // σχεδίαση της γραμμής στο buffer
draw_screen( screen ); // τύπωμα όλων των pixels του buffer
/*
* Σχεδίαση κύκλου με κέντρο στο σημείο: center και ακτίνα: radius
* (πειραματιστείτε με διάφορες τιμές)
*/
Point center = { .x=30, .y=11 }; // το κέντρο του κύκλου
int radius = 10; // η ακτίνα του κύκλου
printf( "> press ENTER for circle with center (%d,%d) and radius %d...", center.x, center.y, radius
);
clear_screen( &screen ); // σβήσιμο όλων των pixels στο buffer
set_circle( &screen, center, radius ); // σχεδίαση του κύκλου στο buffer
draw_screen( screen ); // τύπωμα όλων των pixels του buffer
/*
* Σχεδίαση έλλειψης που οριοθετείται από παραλληλόγραμο με την
* επάνω αριστερή του γωνία στο σημείο: psta και την κάτω δεξιά
* γωνία του στο σημείο: pend
* (πειραματιστείτε με διάφορες τιμές)
*/
// τα σημεία που οριοθετούν το παραλληλόγραμο
Point psta = { .x=2, .y=20 }, pend = { .x=60, .y=2};
printf( "> press ENTER for ellipsis in rect: (%d,%d)-(%d,%d) ...", psta.x, psta.y, pend.x, pend.y
);
clear_screen( &screen ); // σβήσιμο όλων των pixels στο buffer
set_ellipse( &screen, psta, pend ); // σχεδίαση της έλλειψης στο buffer
draw_screen( screen ); // τύπωμα όλων των pixels του buffer
/*
* Τερματισμός του προγράμματος
*/
printf("> press ENTER to exit...");
}