Bargain23 Bargain23 - 9 months ago 33
C Question

C - How to draw outline of adjacent strips of rectangles in asterisks

The output should be something like this:

*********************
* *
* *
* *
* *
********** ******** *
* * * *
* * * *
* **** *
* *
* *
* *
* *
* *
* *


The input is a list of coordinates (begin, height) where begin stands for the left x-coordinate and height is the height of a strip. What I coded:

//Input: ((1, 10), (10, 7), (13, 16), (20, 15), (40, 0))
int beginArr[NUM_STRIPS];//sorted array of the x-coordinates
int heights[NUM_STRIPS]; //height/y-coordinates
//Assume that the tallest strip is 40
for(int i = 0; i < 40; i++){
for(int j = 0; j < beginArr[NUM_STRIPS-1]; j++){
for(int k = 0; k < NUM_STRIPS-1; k++){
if(isElementOf(beginArr, j) && i >= heights[k] && i <= heights[k+1]){
printf("*");
}else if(isElementOf(heights, i) && j >= beginArr[k] && j <= beginArr[k+1]){
printf("*");
}else{
printf(" ");
}
}
}
printf("\n");
}


My code is embarrassingly incorrect, since it does not take into account where and when does a new strip begin or terminate. The logic part is where I'm stuck so any help is greatly appreciated.

Edit:

The x,y coordinates can be imagined as a 2d array. In the case of the above example:

int xy[][2] = {{1, 10}, {10, 7}, {13, 16}, {20, 15}, {40, 0}};


From how I've interpreted this, the leftmost strip rectangle begins at x = 1 (
{1, 10}
) and maintains a height of 10 until x = 10 (
{10, 7}
), which means there well be asterisks from x = 1 through 10 at the height = 10. The second strip begins at x = 10 (
{10, 7}
) and maintains a height of 7 until x = 13 (
{13, 16}
), and so on.

Answer Source

Here's what I came up with. Inline comments should make it pretty clear what's going on here, hopefully.

I've filled the rectangles, leaving it as an exercise for you to either fill only their beginnings and endings into the data, or to only draw the rectangles that are next to empty space.

You'll also have to change the inputs.

I think the big difference between what I'm doing and what you're doing is that I'm taking a few different passes through the data, the first to compute each rectangle's width ( making future calculations simpler and more straightforward ) and the size of the plot, and the second to fill out a plot in memory. Notice I plot it in reverse, and then draw the top lines first, to simplify the logic to plotting, but this could be changed. Finally, I plot the data into memory first, and then draw it on the screen, which simplifies the logic about how many spaces to insert before drawing an asterisk.

The output, with useful info to stderr:

$ gcc -o rects t.c --std=c99 && ./rects
Plot is 20 by 16
Strip 1/8, 2 x 3 @ 0
Strip 2/8, 3 x 10 @ 2
Strip 3/8, 4 x 16 @ 5
Strip 4/8, 2 x 7 @ 9
Strip 5/8, 4 x 1 @ 11
Strip 6/8, 2 x 12 @ 15
Strip 7/8, 3 x 6 @ 17
Strip 8/8, 0 x 0 @ 20
_____****___________
_____****___________
_____****___________
_____****___________
_____****______**___
_____****______**___
__*******______**___
__*******______**___
__*******______**___
__*********____**___
__*********____*****
__*********____*****
__*********____*****
***********____*****
***********____*****
********************

And finally, the good stuff:

#include <stdio.h>
#include <stdlib.h>

/*
instead of drawing from the top down,
wouldn't it be easier to plot the image into
memory upside down, and then print the lines
in reverse?
*/

// the strip is assumed to end when the next strip starts
// width can therefore be precomputed which will
// simplify the display logic significantly.
typedef struct {
    unsigned int start;
    unsigned int height;
    unsigned int width;
} strip;

// for simplicity, I'll assume these are in order of
// starting position.  They could be sorted to avoid
// this assumption
int main(int argc, char **argv) {
    // we could compute this if we were
    // taking dynamic input
    unsigned int striplen = 8;
    strip strips[] = {
        { 0,  3, 0 },
        { 2, 10, 0 },
        { 5, 16, 0 },
        { 9,  7, 0 },
        { 11, 1, 0 },
        { 15,12, 0 },
        { 17, 6, 0 },
        { 20, 0, 0 }
    };
        // the width of the last strip is 0 and its height must be 0 to 'finish' the strips
        // otherwise we'd actually need to konw the widths of each strip ( which would
        // be a more complete model for the data, but we'll make do without it  by
        // making the assumption that the last strip is 0)

    // we'll discover the "size" of the full plot
    // and set up the widths
    unsigned int plot_width = 0;
    unsigned int plot_height = 0;
    unsigned int laststart = 0;
    for( unsigned int i = 0; i < striplen; i++){
        if( plot_height < strips[i].height ) {
            plot_height = strips[i].height;
        }
        if( i > 0 ){
            // set the width of the previous strip from the difference of their
            // starting positions
            strips[i-1].width = strips[i].start - strips[i-1].start;
            // we can now add the width to the total.  Since the
            // width and height of the last strip are required to be 0,
            // we don't need to worry about the fact that the last width isn't
            // taken into account.
            plot_width += strips[i-1].width;
        }
    }
    // plot the rectangles filled, because it's easier to deal with
    // their intersections that way.
    // when we draw them, we can use the in-memory plot to decide
    // whether an asterisk is an edge or fill
  fprintf( stderr, "Plot is %u by %u\n", plot_width, plot_height );
    char* plot_data = calloc( plot_height * plot_width, sizeof(char) );
    for( unsigned int i = 0; i < striplen; i++){
    fprintf( stderr, "Strip %u/%u, %u x %u @ %u \n", i+1, striplen, strips[i].width, strips[i].height, strips[i].start );
        for( unsigned int x = strips[i].start; x < strips[i].start + strips[i].width; x++){
            for( unsigned int y = 0; y < strips[i].height; y++){
                plot_data[plot_width * y + x] = '*';
            }
        }
    }
    // now we can finally draw it, in reverse order to make it right side up
    for( signed int y = plot_height - 1; y >= 0; y--){
        for( unsigned int x = 0; x < plot_width; x++){
            printf("%c", ( plot_data[y * plot_width + x] == 0 ? '_' : '*' ) );
      }
      printf("\n");
  }
    return 0;
}