// -------------------------------------------------------------------------- // System Headers // -------------------------------------------------------------------------- #include #include // -------------------------------------------------------------------------- // Local Headers // -------------------------------------------------------------------------- #include "util.h" #include "svg.h" // -------------------------------------------------------------------------- // Defines and Typedefs // -------------------------------------------------------------------------- #define P_SIDES (5) #define P_STEP_DEGREES (360.0 / P_SIDES) typedef struct { double mXValues[P_SIDES]; double mYValues[P_SIDES]; double mXCenter; double mYCenter; double mRadius; int mStartAngle; int mChildAngle; } Pentagon; // -------------------------------------------------------------------------- // makePentagon - Create and return a Pentagon using xCenter, yCenter // radius and startAngle // -------------------------------------------------------------------------- static Pentagon makePentagon( double xCenter, double yCenter, double radius, int startAngle ) { double angle = startAngle; int i = 0; Pentagon thePentagon; thePentagon.mXCenter = xCenter; thePentagon.mYCenter = yCenter; thePentagon.mRadius = radius; thePentagon.mStartAngle = startAngle; thePentagon.mChildAngle = (startAngle + 180) % 360; while(i < P_SIDES) { XY p = utilGetXY(angle, radius, xCenter, yCenter); thePentagon.mXValues[i] = p.mX; thePentagon.mYValues[i] = p.mY; angle += P_STEP_DEGREES; i++; } return(thePentagon); } // -------------------------------------------------------------------------- // draw - Render 'p' as svg // -------------------------------------------------------------------------- static void draw(Pentagon *p, SVG *svg) { int i = 0; while(i < P_SIDES) { int x0 = utilRound(p->mXValues[i]); int y0 = utilRound(p->mYValues[i]); int x1 = utilRound(p->mXValues[(i + 1) % P_SIDES]); int y1 = utilRound(p->mYValues[(i + 1) % P_SIDES]); svgLine(svg, x0, y0, x1, y1, svg->mColors.black); i++; } } // -------------------------------------------------------------------------- // getCosValue - Returns 'value' which is used by both // 'getCenter' and 'makeChild' // -------------------------------------------------------------------------- static double getCosValue() { static double value = 0.0; static int first = 1; if(first) { first = 0; value = cos(utilDegreesToRadians(P_STEP_DEGREES / 2)); } return(value); } // -------------------------------------------------------------------------- // getCenter - Find the center given a Pentagon and chosen side 'which' // -------------------------------------------------------------------------- static XY getCenter(Pentagon *p, int which) { double degrees = 0.0; double radius = 0.0; degrees = p->mStartAngle + P_STEP_DEGREES * which + (P_STEP_DEGREES / 2); radius = p->mRadius * 2 * getCosValue(); return(utilGetXY(utilRound(degrees) % 360, radius, p->mXCenter, p->mYCenter)); } // -------------------------------------------------------------------------- // makeChild - Create child Pentagon given parent 'p' // -------------------------------------------------------------------------- static Pentagon makeChild(Pentagon *p) { /* ** ** In the diagram below, '1' is the center of the parent ** pentagon. The distance from '1' to '3' is the ** radius of the parent polygon. We want to find ** out the radius of the child, the distance from ** '2' to '3'. ** ** 3 ** * * ** * 2 * ** * * ** ***** ** * * ** * 1 * ** * * ** * ** ** The distance from '1' to '2' won't be twice the ** child radius, it'll be less: ** ** 2 ** |\ ** | \ <------ this will be the child radius, the ** | \ vertical side's length will be the child radius ** | \ multiplied by the cos of P_STEP_DEGREES / 2 (which ** ********* is what is returned by 'getCosValue()') ** ** So, the parent radius will be the sum of the distance from '2' to ** '3' (the child radius) plus the distance from '1' to '2' ** (the child radius * 2 * getCosValue(). So, ** if 'R' is the parent radius and 'r' the child, we have: ** ** R = r + 2 * getCosValue() * r ** ** then solving for 'r' yields: ** ** r = R / (1 + 2 * getCosValue()) ** */ return( makePentagon( p->mXCenter, p->mYCenter, p->mRadius / (1 + 2 * getCosValue()), p->mChildAngle ) ); } // -------------------------------------------------------------------------- // drawPentagon - Render 'p' and recurse down to level 'total' // -------------------------------------------------------------------------- static void drawPentagon(Pentagon *p, SVG *svg, int count, int total) { draw(p, svg); if(++count < total) { int i = 0; Pentagon child = makeChild(p); drawPentagon(&child, svg, count, total); // Now the P_SIDES neighbors while(i < P_SIDES) { XY center = getCenter(&child, i); Pentagon p2 = makePentagon(center.mX, center.mY, child.mRadius, child.mChildAngle); drawPentagon(&p2, svg, count, total); i++; } } } // -------------------------------------------------------------------------- // main // -------------------------------------------------------------------------- int main() { const int WIDTH = 500; const int HEIGHT = 500; const char *NAME = "penrose.html"; SVG *svg = svgOpen(NAME, "Penrose Tiling", WIDTH, HEIGHT); if(!svg) { printf("Call to 'svgOpen' failed\n"); return(1); } int xc = WIDTH / 2; int yc = HEIGHT / 2; int r = utilRound(.45 * (WIDTH <= HEIGHT ? WIDTH : HEIGHT)); Pentagon p = makePentagon(xc, yc, r, 270); svgBorder(svg, svg->mColors.black); drawPentagon(&p, svg, 0, 4); svgClose(svg); printf("Successfully wrote: %s\n", NAME); return(0); }