cshapes

What is the simplest most effective way to write a code in C that creates a symmetric five-pointed star made out of asterisks?


Visual Studio Code. mingw64 compiler. I'm trying to write a code in C language that is able to recreate a five-pointed symmetric star made out of asterisks like the one provided in the image Five-pointed symmetric star. The star in the image has a total of 16 rows of asterisks. I tried several ways already and the result displayed in the terminal doesn't even look like a star. Try 1 Try 2 Try 3

At this point I'm only left with writing a hundred prints with spaces in it.

This was the last version of the code I tried to write and gave a rhombus as a result, but honestly more than correcting my code I'd like to know if there's a simpler much more efficient way of creating this star pattern. Like how someone would familiar with working with c would do it. Thank you, I'm just a beginner with C language.

#include <stdio.h>

int main() {
    int i, j, k;
    int size = 16; 
    int mid = size / 2; 

    for (i = 1; i <= mid; i++) {
        // Print leading spaces
        for (j = i; j < mid; j++) {
            printf(" ");
        }
        
        for (k = 1; k <= (2 * i - 1); k++) {
            printf("*");
        }
        
        printf("\n");
    }

    
    for (i = 1; i <= mid; i++) {
        
        for (j = 1; j < i; j++) {
            printf(" ");
        }
        
        for (k = (2 * (mid - i + 1) - 1); k > 0; k--) {
            printf("*");
        }
        
        printf("\n");
    }
    
    return 0;
}

This is the image of the star I would like to recreate posted as text(? Doesn't look like a star this way, it's just a bunch of asterisks without order, but if you guys requested it here it is.

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

and the image of the rhombus I got with my last code posted as text as well. (oh now, this one looks just like in the terminal)

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

Solution

  • A simple way is to use vector math. If you define your star as a sequence of vertices in a specific order (e.g. anti-clockwise), then for any arbitrary point you can determine what "side" of each line segment connecting those vertices it lies on.

    To do that, calculate the vector from one vertex to another, and then cross that with the vector from your point to that same vertex. To lie within the 5-pointed star, at least 4 of these cross products must have the same sign (or be zero).

    Now, you can do an "inside" test for any arbitrary point in the star. That means you can calculate it in scanlines, compatible with standard output.

    Example:

    #include <stdio.h>
    
    int main() {
        int size = 16, aspectx = 5, aspecty = 2;
        int width = size * aspectx / aspecty - 1;
        int height = size;
    
        int verts[5][2] = {
            { width/2, 0 },                 // top
            { width/8, height-1 },          // bottom-left
            { width-1, height*3/8 },        // right
            { 0, height*3/8 },              // left
            { width-1-width/8, height-1 },  // bottom-right
        };
    
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                // To be within star, a point must be on the same side of
                // at least 4 lines. Use cross product to determine that.
                int left_count = 0;
                for (int v = 0; v < 5; v++) {
                    int vx = verts[(v+1)%5][0] - verts[v][0];
                    int vy = verts[(v+1)%5][1] - verts[v][1];
                    int dx = x - verts[v][0];
                    int dy = y - verts[v][1];
                    int cross = vx * dy - vy * dx;
                    if (cross <= 0) left_count++;
                }
                putchar(left_count >= 4 ? '*' : ' ');
            }
            putchar('\n');
        }
    }
    

    Output:

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

    This is a very basic example. You can pre-compute the line segments if you want. Notice that I've used a separate width and height, allowing control over the aspect ratio. To account for rectangular characters, I'm using a pixel aspect of 2.5 here. You may want to adjust that, depending on what font you're using to render it.

    If you want to avoid trailing spaces, you should write a scanline into a buffer, and then trim off the spaces before printing.


    Notes on precision:

    You won't always get a "perfect" star. To begin with, we're using integer math here. I've also approximated the positions of vertices, not calculated them properly. There will be inaccuracies that lead to wonky output that's hard to correct. To be more accurate, you can use floating-point arithmetic. Just be aware that any lines which are not at 90 or 45 degrees are difficult to represent cleanly at arbitrary sizes as text.

    With normalized vectors, you'll then be able to determine the actual distance of a point from the line which will allow you to add "thickness" and even draw the star as lines / outline. You can even do anti-aliasing this way. This might be desirable if you're drawing much larger stars, or outputting an image instead of text.