General Polygons (i_polygons.cfdg)

If you have a design you're proud of, share the cfdg file here. It's also a good place to ask for feedback and collaborate.

Moderators: MtnViewJohn, chris, mtnviewmark

Post Reply
User avatar
chris
Site Admin
Posts: 72
Joined: Wed May 04, 2005 10:57 am
Location: New York, NY
Contact:

General Polygons (i_polygons.cfdg)

Post by chris »

This took some annoying geometry, but I was inspired by the cool hex and octagon work buy you guys. This include covers all convex regular polygons with obtuse inner angles, up to 99. They're all drawn without recursive definitions, so they're fast. Each one fits exactly in the circle of the same size, as seen here:

Image

Here's the demo CFDG

Code: Select all

startshape seed

include i_polygons.cfdg

rule seed {
 CIRCLE {x -2 y 2 b 0.5}
 CIRCLE {x -2 y 0 b 0.5}
 polygon5sided {x 0 y 0}
 polygon6sided {x 2 y 0}
 polygon7sided {x 4 y 0}
 polygon8sided {x 6 y 0}
 polygon9sided {x 0 y 2}
 polygon10sided {x 2 y 2}
 polygon11sided {x 4 y 2}
 polygon12sided {x 6 y 2}
 CIRCLE {x 8 y 0 b 0.5}
 CIRCLE {x 8 y 2 b 0.5}
}
And here's the C++ program I hacked together to generate it:

Code: Select all

#include <iostream>
#include <sstream>
#include <cmath>

using namespace std;
const float PI = 3.14159265;

float cleanFloat(float f) {
    if (f < 0.001 && f > -0.001)
        return (0);
    else
        return (f);
}

int main () {

    float R = 1.0;

    for (int n = 5; n < 100; n++) {
        cout << "\n\nrule polygon" << n << "sided {";
        float angle = 360.0 / float(n) / 2.0;
        float side = 2 * R * abs(sin(PI * angle / 180.0));
        float inner_circle_radius =  R * cos(angle * PI / 180.0);
        float dist = inner_circle_radius - side/2;

        for (int k = 0; k < n; k++) {
            float theta = float(k)*360.0/float(n);
            float x = dist * cos(PI * theta / 180.0);
            float y = dist * sin(PI * theta / 180.0);

            cout << "\n  SQUARE {size "
                 << cleanFloat(side)
                 << " x "
                 << cleanFloat(x)
                 << " y "
                 << cleanFloat(y)
                 << " rotate "
                 << cleanFloat (theta)
                 << "}";
        }
        cout << "\n  CIRCLE {s " << cleanFloat(inner_circle_radius)  << "}";
        cout << "\n}";
    }
}
And most important, here's the i_polygons.cfdg It's a pretty big file! n+1 replacements for each polygon, so it's like 300kB.

http://chriscoyne.com/cfdg/i_polygons.cfdg

Enjoy!

cc
Current Project: I'm creative director of OKCUPID at http://www.okcupid.com

David Spitzley
Posts: 21
Joined: Fri May 06, 2005 10:01 am

Post by David Spitzley »

Hot damn! Well, I feel a little silly having spent so much time hacking together my octogon, but if it helped inspire that file, I'm fine with silly :)

Now, I just need to test out the triangle that was recently posted.

User avatar
chris
Site Admin
Posts: 72
Joined: Wed May 04, 2005 10:57 am
Location: New York, NY
Contact:

Post by chris »

David Spitzley wrote:Hot damn! Well, I feel a little silly having spent so much time hacking together my octogon, but if it helped inspire that file, I'm fine with silly :)
Yeah, it was yours and Erik's work that made me try it in the first place.

Triangles ARE annoying, since it's pretty clear they have to be drawn with infinite parts.

Even worse: most curving shapes are in fact impossible. I'm pretty sure you can't draw any ellipse other than a circle, even with infinite replacements. I'd love to find out I'm wrong.

-cc
Current Project: I'm creative director of OKCUPID at http://www.okcupid.com

David Spitzley
Posts: 21
Joined: Fri May 06, 2005 10:01 am

Post by David Spitzley »

Ok, that's weird. I just now tried a test with the polygons, and they're much larger than the default CIRCLE{}, which flies in the face of your screenshot. When I run the following:

Code: Select all

startshape SANDWICH
include i_polygons.cfdg
rule SANDWICH{
	polygon8sided{b 0.5}
	polygon6sided{b 0.75}
	CIRCLE{x}
	CIRCLE{x 2}
}
the first circle should be appearing behind the other two polygons, but it is completely hidden. The second circle shows that the polygons are about three times as wide. I'm not sure how to square this with your sample image. Is it possible we're getting differences due to OS? I'm running on Windows XP.

David Spitzley
Posts: 21
Joined: Fri May 06, 2005 10:01 am

Post by David Spitzley »

Ok, I just did a little poking around in i_polygons.cfdg file, and it appears to me that your size parameters are twice what they should be. The hexagon is using unit squares, when they actually need to be size .5, and your octogon is using size 0.765367, in contrast with the 0.38 that I worked up. Mine definitely fit inside a unit CIRCLE.

Is it possible your code used diameter in a place where it actually needed a radius? If you sized your sides based on calculating chord lengths using a central angle of 360/n, then I could easily see myself forgetting to add the factor of .5 for radial length.

<CHECKS CODE> Ok, yip, you've got R=1, should be 0.5

User avatar
chris
Site Admin
Posts: 72
Joined: Wed May 04, 2005 10:57 am
Location: New York, NY
Contact:

Post by chris »

That's exactly it. I've updated the i_polygons.cfdg - let me know if it works for you, now.

http://www.chriscoyne.com/cfdg/i_polygons.cfdg

The problem was that the unix/windows/mac versions of Context Free had a different relative definition of size (square/circles, relative to each other) than I did in the original CFDG. I think the new way is better, so we'll call it a bug on my part. The new C++ program is this (I had to adjust a couple lines because of the implication of the radius change):

Code: Select all

#include <iostream>
#include <sstream>
#include <cmath>

using namespace std;
const float PI = 3.14159265;

float cleanFloat(float f) {
    if (f < 0.001 && f > -0.001)
        return (0);
    else
        return (f);
}

int main () {

    float R = 0.5;

    for (int n = 5; n < 100; n++) {
        cout << "\n\nrule polygon" << n << "sided {";
        float angle = 360.0 / float(n) / 2.0;
        float side = 2 * R * abs(sin(PI * angle / 180.0));
        float inner_circle_radius =  2 * R * cos(angle * PI / 180.0);
        float dist = inner_circle_radius/2 - side/2;

        for (int k = 0; k < n; k++) {
            float theta = float(k)*360.0/float(n);
            float x = dist * cos(PI * theta / 180.0);
            float y = dist * sin(PI * theta / 180.0);

            cout << "\n  SQUARE {size "
                 << cleanFloat(side)
                 << " x "
                 << cleanFloat(x)
                 << " y "
                 << cleanFloat(y)
                 << " rotate "
                 << cleanFloat (theta)
                 << "}";
        }
        cout << "\n  CIRCLE {s " << cleanFloat(inner_circle_radius)  << "}";
        cout << "\n}";
    }
}
-cc
Current Project: I'm creative director of OKCUPID at http://www.okcupid.com

David Spitzley
Posts: 21
Joined: Fri May 06, 2005 10:01 am

Post by David Spitzley »

WE HAVE A WINNER! :)

Yeah, that works really well. Ok, so now we've got pretty much arbitrary polygons. If we can get some wizard to develop triangles, we're set for pretty much anything.

User avatar
emrainey
Posts: 6
Joined: Fri May 06, 2005 9:37 am
Location: Dallas, TX
Contact:

Triangles

Post by emrainey »

yes but what types? isosceles? Right angle? you can easily make a right angle triangle, conceptually, by rendering a black square (b=1) of side length x and then diagonally bisecting it with a white square (b=0) of length on side of the black triangle of side length sqrt(2*pow(x,2)). I don't know that this will work right off since I'm at work, but it's somethign to try. An isosceles trianlge conceptually can be done similarly but with 2 white squares.
e.m.rainey

The next remark is false.
The previous remark is true.

User avatar
chris
Site Admin
Posts: 72
Joined: Wed May 04, 2005 10:57 am
Location: New York, NY
Contact:

croppping with white

Post by chris »

Cropping with white squares doesn't really work as a solution, because it'll screw up tilings. There's definitely a difference between "whitespace" and "nospace".
Current Project: I'm creative director of OKCUPID at http://www.okcupid.com

User avatar
mtnviewmark
Site Admin
Posts: 81
Joined: Wed May 04, 2005 12:46 pm
Location: Mountain View, CA
Contact:

Re: Triangles

Post by mtnviewmark »

emrainey wrote:yes but what types? isosceles? Right angle? you can easily make a right angle triangle, conceptually, by.
Try this:

Code: Select all

startshape TRIANGLE 

rule TRIANGLE{ 
	ARM { r 0 }
	ARM { r 120 }
	ARM { r -120 }
} 

rule ARM {
	CIRCLE { }
	ARM { y 0.1 s 0.9 }
}
This was my first guess and it looks pretty right! Now, I don't know if the values of 0.1 and 0.9 are "correct" for a perfect triangle... I didn't do the math (but I probably will tonight...).

I do know that if you play with those values you get all sorts of nice things. And, of course, adding in a little rotation does wonders...
I'm the "m" in "mtree.cfdg"

buckyboy314
Posts: 8
Joined: Wed May 11, 2005 11:30 am
Contact:

Triangles

Post by buckyboy314 »

Well, I cobbled together an Eigenmath program to create an arbitrary angle with relatively few polygons using the straight edge made by a row of squares. It looks really nice, and I will soon work on making a complete triangle program. My design is, in my opinion, a little obnoxious to program, but the result is nice, IMNSHO.
Here's the source:

###Use either THIS (and comment the next block):
###(Where M is the slope of the angles right side)
# m = 1/2
###Change only the above value^
# theta = float(arctan(m))


###Or THIS (and comment the previous block):
###(Where A is the angle of elevation of the right side or the complement of the angle at the tip)
A = 30
###Change only the above value^
theta=(A*pi/180)
m=tan(theta)


print("startshape TRI")
x= float(-1 / (2m + 2))
s= float(1 - (1 / (m + 1)))
y= float(1 - (1 / (2m + 2)))

print("rule TriEdgeL{")
print("SQUARE{}")
print("TriEdgeL{x ",x, " y ",y, " s ",s,"}")
print("}")

x = float(1 / (2m + 2))
print("rule TriEdgeR{")
print("SQUARE{}")
print("TriEdgeR{x ",x, " y ",y, " s ",s,"}")
print("}")

x = float((1/tan(theta)+1)/2-.5)
s = float(1/sin(theta))
y = -x
r = float(90-theta*180/pi)

print("rule TRI{")
print("TriEdgeL{}")
print("TriEdgeR{x ",x," y ",y," s ",s," r ",r,"}")
print("}")
-Dan

User avatar
chris
Site Admin
Posts: 72
Joined: Wed May 04, 2005 10:57 am
Location: New York, NY
Contact:

Re: Triangles

Post by chris »

buckyboy314 wrote:Well, I cobbled together an Eigenmath program to create an arbitrary angle with relatively few polygons using the straight edge made by a row of squares.
Hey, I'd never heard of eigenmath. Cool! Off to download it...
Current Project: I'm creative director of OKCUPID at http://www.okcupid.com

buckyboy314
Posts: 8
Joined: Wed May 11, 2005 11:30 am
Contact:

General acute triangle program

Post by buckyboy314 »

This makes an acute triangle with the current center at the triangle's incenter and an inscribed circle radius 1. (That basically means if instead of a triangle, you draw CIRCLE{}, it will touch each side of the triangle exactly.) You may want to lower all y factors slightly if you're on a Mac so antialiasing doesn't make lines through the triangle. Here's the Eigenmath program:

#A is the first angle (pointing up), B is the second (pointing right)
A = 130
B = 42
#Change only the above values^
C=180-A-B


print("startshape TRIANGLE")
print("rule TRIANGLE{")
print("triA{}")
print("triB{r",float(-180+(A+B)/2),"}")
print("triC{r",float(90+B/2),"}")
print("}")

print("rule triA{")
theta=(45-(90-A)/2)*pi/180
x= float(.5 (sin(theta)- cos(theta)))
y= float(.5 (sin(theta)+cos(theta)))
r= float(-theta*180/pi)
print("SQUARE{x ",x," y ",y," r ",r,"}")
r= -r
x= -x
print("SQUARE{x ",x," y ",y," r ",r,"}")
y=float(1/cos(theta))
s=float(1-tan(theta))
print("triA{y",y," s ",s,"}")
print("}")

print("rule triB{")
theta=(45-(90-B)/2)*pi/180
x= float(.5 (sin(theta)- cos(theta)))
y= float(.5 (sin(theta)+cos(theta)))
r= float(-theta*180/pi)
print("SQUARE{x ",x," y ",y," r ",r,"}")
r= -r
x= -x
print("SQUARE{x ",x," y ",y," r ",r,"}")
y=float(1/cos(theta))
s=float(1-tan(theta))
print("triB{y",y," s ",s,"}")
print("}")

print("rule triC{")
theta=(45-(90-C)/2)*pi/180
x= float(.5 (sin(theta)- cos(theta)))
y= float(.5 (sin(theta)+cos(theta)))
r= float(-theta*180/pi)
print("SQUARE{x ",x," y ",y," r ",r,"}")
r= -r
x= -x
print("SQUARE{x ",x," y ",y," r ",r,"}")
y=float(1/cos(theta))
s=float(1-tan(theta))
print("triC{y",y," s ",s,"}")
print("}")
-Dan

User avatar
mtnviewmark
Site Admin
Posts: 81
Joined: Wed May 04, 2005 12:46 pm
Location: Mountain View, CA
Contact:

Re: General acute triangle program

Post by mtnviewmark »

buckyboy314 wrote: You may want to lower all y factors slightly if you're on a Mac so antialiasing doesn't make lines through the triangle.
This has been fixed in the next release. Abutting squares now have no gaps on all platforms. We spent time tweaking the rendering on each to get it just right.
I'm the "m" in "mtree.cfdg"

Post Reply