Ziezi
Ziezi

Reputation: 6467

Failing to draw polygrams?

Here is a class that draws stars, as part of Chapter 13 Exercise 19 of Bjarne Stroustrup's C++ Programming: Principles and Practice.

Class Star uses penta-, hexa-, hepta- and octa-gram as basis together with a property (rotational symmetry) that stars with multiple vertices (10, 12, 14, 18, etc) have, to draw n-grams1:

Chapter13Exercise19.cpp

#include "GUI.h"
#include "Simple_window.h"
#include <iostream>
#include "Chapter13Exercise19.h"

int main(){
     // window parameters
     int winWidth = 800;
     int winHeight = 600;
     Point centerPoint((x_max() - winWidth) / 2, (y_max() - winHeight) / 2);
     Simple_window* siw = new Simple_window(centerPoint, winWidth,
                                            winHeight, "Chapter 13 Exercise 19");

 try{
     Point center(siw->x_max()/2, siw->y_max()/2);
     int radius = 150;
     // Currenly: sides > 5, sides =! 13, 17, 19 and multiples
     int sides = 16;

     Graph_lib::Star st(center, radius, sides);
     siw->attach(st);
     siw->wait_for_button();
 }catch(exception& e){
    cerr << e.what() << endl;
    getchar();
 }catch(const std::invalid_argument& e){ 
     cerr << e.what() << endl;
     getchar();
 }catch(...){
    cerr <<"Defaul exception!"<< endl;
    getchar();
 }

}

Chapter13Exercise19.h

#ifndef _Chapter13Exercise19_H_
#define _Chapter13Exercise19_H_
#define PI 3.14159265359

namespace Graph_lib{
class Star: public Lines{
public:
    Star(Point c, int r, int n);
private:
    int vertexNumber;
    Point center;
    int radius;
    vector<Point>starCoordinates;
    // parameters kept as function recursive
    void rotateCoordinate(Point& axisOfRotation, Point& initial, 
                          double angRads, int numberOfRotations);
    void generatePoly();
    void makeStar();
};
#include "Chapter13Exercise19Def.cpp"
} // end of namespace Graph_lib

#endif 

Chapter13Exercise19Def.cpp

// Class Members implementation
Star::Star(Point c, int r, int n)
    : vertexNumber(n), center(c), radius(r)
{ 
    if(n < 5) throw std::invalid_argument("Not enough verteces!");
    generatePoly(); 
    makeStar();
}

void Star::rotateCoordinate(Point& axisOfRotation, Point& initial,
                            double angRads, int numberOfRotations){
    if(numberOfRotations <= 0) return;
    else{
        double x = cos(angRads) * (initial.x - axisOfRotation.x)
                 - sin(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.x;
        double y = sin(angRads) * (initial.x - axisOfRotation.x) 
                 + cos(angRads) * (initial.y - axisOfRotation.y) + axisOfRotation.y;
        starCoordinates.push_back(Point(x, y));
        rotateCoordinate(axisOfRotation, Point(x,y), angRads, --numberOfRotations);
    }
}

void Star::generatePoly(){ 
    double angRads = (PI / 180.) * (360. / vertexNumber);
    Point initial(center.x, center.y - radius);
    rotateCoordinate(center, initial, angRads, vertexNumber);
}

void Star::makeStar(){
    // every if statement covers Star with n and multiples of n vertexes
    // the inner for loops execute one iteration for the fundamental stars
    // and n iterations for the multiples (rotational symmetry)
    if (vertexNumber % 5 == 0){
        for (size_t it = 0; it < starCoordinates.size() / 5; ++it){
            Lines::add(starCoordinates[it+3], starCoordinates[it]);
            Lines::add(starCoordinates[it], starCoordinates[it+2]);
            Lines::add(starCoordinates[it+2], starCoordinates[it+4]);
            Lines::add(starCoordinates[it+4], starCoordinates[it+1]);
            Lines::add(starCoordinates[it+1], starCoordinates[it+3]);
        }
    }else if (vertexNumber % 3 == 0){
        for (size_t it = 0; it < starCoordinates.size() / 3; ++it){
            Lines::add(starCoordinates[it], starCoordinates[it+2]);
            Lines::add(starCoordinates[it+2], starCoordinates[it+4]);
            Lines::add(starCoordinates[it+4], starCoordinates[it]);
        }
    }else if (vertexNumber % 7 == 0){
        for (size_t it = 0; it < starCoordinates.size() / 7; ++it){
            Lines::add(starCoordinates[it], starCoordinates[it+3]);
            Lines::add(starCoordinates[it+3], starCoordinates[it+6]);
            Lines::add(starCoordinates[it+6], starCoordinates[it+2]);
            Lines::add(starCoordinates[it+2], starCoordinates[it+5]);
            Lines::add(starCoordinates[it+5], starCoordinates[it+1]);
            Lines::add(starCoordinates[it+1], starCoordinates[it+4]);
            Lines::add(starCoordinates[it+4], starCoordinates[it]);
        }
    }else if (vertexNumber % 8 == 0){
        for (size_t it = 0; it < starCoordinates.size() / 8; ++it){
            Lines::add(starCoordinates[it], starCoordinates[it+5]);
            Lines::add(starCoordinates[it+5], starCoordinates[it+2]);
            Lines::add(starCoordinates[it+2], starCoordinates[it+7]);
            Lines::add(starCoordinates[it+7], starCoordinates[it+4]);
            Lines::add(starCoordinates[it+4], starCoordinates[it+1]);
            Lines::add(starCoordinates[it+1], starCoordinates[it+6]);
            Lines::add(starCoordinates[it+6], starCoordinates[it+3]);
            Lines::add(starCoordinates[it+3], starCoordinates[it]);
       }
   } else throw std::invalid_argument("Star vertexes'number not supported!");
}

Result:

While all the basic polygrams (5,6,7,8 vertices) look as they should, in the cases of polygrams with vertices: 10, 12, 14 etc, I get these partly finished drawings.

I am aware that the problem comes from function makeStar() in Chapter13Exercise19Def.cpp, but I can't figure it out what the problem is.

Question:

  1. Why are the drawing coming partly drawn, what is wrong with the implementation of function makeStar()?

  2. Is there another algorithm that can do similar job?


1. currently multiples of 5, 6, 7 and 8.

All the additional files for compilation could be found: here. The FLTK could be found here.

Upvotes: 2

Views: 115

Answers (1)

Amit
Amit

Reputation: 46331

I can't really play with your code as it relies on that visualization library you're using, but I see what the problem is, and I'm pretty confident I know the solution.

The rotateCoordinate recursive function generates a vector of points, evenly spread along a bounding circle of your polygram, ordered in a clockwise direction.

When you then construct the lines that define (draw...) the polygram, you use the points, but you're not using to correct indices. For example, suppose you'd have a polygram {16/2} (2 hexagrams), you'd have 0-15 indexed points, but your for loop to generate the lines would at most reach index 8 (it = 1, index [it + 7]), and that's obviously wrong.

What you should be doing is multiply your constant numbers (the ones you add to it) by the number of polygons in your polygram, so in the case of {16/2} that would be 2.

Try this code:

void Star::makeStar(){
    // every if statement covers Star with n and multiples of n vertexes
    // the inner for loops execute one iteration for the fundamental stars
    // and n iterations for the multiples (rotational symmetry)
    if (vertexNumber % 5 == 0){
        for (size_t it = 0, polygons = starCoordinates.size() / 5; it < polygons; ++it){
            Lines::add(starCoordinates[it + polygons * 3], starCoordinates[it]);
            Lines::add(starCoordinates[it], starCoordinates[it + polygons * 2]);
            Lines::add(starCoordinates[it + polygons * 2], starCoordinates[it + polygons * 4]);
            Lines::add(starCoordinates[it + polygons * 4], starCoordinates[it + polygons * 1]);
            Lines::add(starCoordinates[it + polygons * 1], starCoordinates[it + polygons * 3]);
        }
    }else if (vertexNumber % 6 == 0){ // for polygons multiples of 6
        size_t polygons = starCoordinates.size() / 6;
        for (size_t it = 0, ; it < starCoordinates() / 3; ++it){
            // generated by two triangles 
            Lines::add(starCoordinates[it], starCoordinates[it + polygons * 2]);
            Lines::add(starCoordinates[it + polygons * 2], starCoordinates[it + polygons * 4]);
            Lines::add(starCoordinates[it + polygons * 4], starCoordinates[it]);
        }
    }else if (vertexNumber % 7 == 0){
        for (size_t it = 0, polygons = starCoordinates.size() / 7; it < polygons; ++it){
            Lines::add(starCoordinates[it], starCoordinates[it + polygons * 3]);
            Lines::add(starCoordinates[it + polygons * 3], starCoordinates[it + polygons * 6]);
            Lines::add(starCoordinates[it + polygons * 6], starCoordinates[it + polygons * 2]);
            Lines::add(starCoordinates[it + polygons * 2], starCoordinates[it + polygons * 5]);
            Lines::add(starCoordinates[it + polygons * 5], starCoordinates[it + polygons * 1]);
            Lines::add(starCoordinates[it + polygons * 1], starCoordinates[it + polygons * 4]);
            Lines::add(starCoordinates[it + polygons * 4], starCoordinates[it]);
        }
    }else if (vertexNumber % 8 == 0){
        for (size_t it = 0, polygons = starCoordinates.size() / 8; it < polygons; ++it){
            Lines::add(starCoordinates[it], starCoordinates[it + polygons * 5]);
            Lines::add(starCoordinates[it + polygons * 5], starCoordinates[it + polygons * 2]);
            Lines::add(starCoordinates[it + polygons * 2], starCoordinates[it + polygons * 7]);
            Lines::add(starCoordinates[it + polygons * 7], starCoordinates[it + polygons * 4]);
            Lines::add(starCoordinates[it + polygons * 4], starCoordinates[it + polygons * 1]);
            Lines::add(starCoordinates[it + polygons * 1], starCoordinates[it + polygons * 6]);
            Lines::add(starCoordinates[it + polygons * 6], starCoordinates[it + polygons * 3]);
            Lines::add(starCoordinates[it + polygons * 3], starCoordinates[it]);
       }
   } else throw std::invalid_argument("Star vertexes'number not supported!");
}

Upvotes: 2

Related Questions